1/* netcat.c - Forward stdin/stdout to a file or network connection. 2 * 3 * Copyright 2007 Rob Landley <rob@landley.net> 4 * 5 * TODO: udp, ipv6, genericize for telnet/microcom/tail-f 6 7USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN)) 8USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#p#s:q#f:", TOYFLAG_BIN)) 9 10config NETCAT 11 bool "netcat" 12 default y 13 help 14 usage: netcat [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME} 15 16 -f use FILENAME (ala /dev/ttyS0) instead of network 17 -p local port number 18 -q SECONDS quit this many seconds after EOF on stdin. 19 -s local ipv4 address 20 -w SECONDS timeout for connection 21 22 Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with 23 netcat -f to connect to a serial port. 24 25config NETCAT_LISTEN 26 bool "netcat server options (-let)" 27 default y 28 depends on NETCAT 29 help 30 usage: netcat [-t] [-lL COMMAND...] 31 32 -t allocate tty (must come before -l or -L) 33 -l listen for one incoming connection. 34 -L listen for multiple incoming connections (server mode). 35 36 The command line after -l or -L is executed to handle each incoming 37 connection. If none, the connection is forwarded to stdin/stdout. 38 39 For a quick-and-dirty server, try something like: 40 netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l 41*/ 42 43#define FOR_netcat 44#include "toys.h" 45 46GLOBALS( 47 char *filename; // -f read from filename instead of network 48 long quit_delay; // -q Exit after EOF from stdin after # seconds. 49 char *source_address; // -s Bind to a specific source address. 50 long port; // -p Bind to a specific source port. 51 long wait; // -w Wait # seconds for a connection. 52) 53 54static void timeout(int signum) 55{ 56 if (TT.wait) error_exit("Timeout"); 57 // This should be xexit() but would need siglongjmp()... 58 exit(0); 59} 60 61static void set_alarm(int seconds) 62{ 63 xsignal(SIGALRM, seconds ? timeout : SIG_DFL); 64 alarm(seconds); 65} 66 67// Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name. 68static void lookup_name(char *name, uint32_t *result) 69{ 70 struct hostent *hostbyname; 71 72 hostbyname = gethostbyname(name); // getaddrinfo 73 if (!hostbyname) error_exit("no host '%s'", name); 74 *result = *(uint32_t *)*hostbyname->h_addr_list; 75} 76 77// Worry about a fancy lookup later. 78static void lookup_port(char *str, uint16_t *port) 79{ 80 *port = SWAP_BE16(atoi(str)); 81} 82 83void netcat_main(void) 84{ 85 int sockfd=-1, pollcount=2; 86 struct pollfd pollfds[2]; 87 88 memset(pollfds, 0, 2*sizeof(struct pollfd)); 89 pollfds[0].events = pollfds[1].events = POLLIN; 90 set_alarm(TT.wait); 91 92 // The argument parsing logic can't make "<2" conditional on other 93 // arguments like -f and -l, so we do it by hand here. 94 if (toys.optflags&FLAG_f) { 95 if (toys.optc) toys.exithelp++; 96 } else if (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2) toys.exithelp++; 97 98 if (toys.exithelp) error_exit("Argument count wrong"); 99 100 if (TT.filename) pollfds[0].fd = xopen(TT.filename, O_RDWR); 101 else { 102 int temp; 103 struct sockaddr_in address; 104 105 // Setup socket 106 sockfd = xsocket(AF_INET, SOCK_STREAM, 0); 107 fcntl(sockfd, F_SETFD, FD_CLOEXEC); 108 temp = 1; 109 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(temp)); 110 memset(&address, 0, sizeof(address)); 111 address.sin_family = AF_INET; 112 if (TT.source_address || TT.port) { 113 address.sin_port = SWAP_BE16(TT.port); 114 if (TT.source_address) 115 lookup_name(TT.source_address, (uint32_t *)&address.sin_addr); 116 if (bind(sockfd, (struct sockaddr *)&address, sizeof(address))) 117 perror_exit("bind"); 118 } 119 120 // Dial out 121 122 if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) { 123 // Figure out where to dial out to. 124 lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr); 125 lookup_port(toys.optargs[1], &address.sin_port); 126 temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address)); 127 if (temp<0) perror_exit("connect"); 128 pollfds[0].fd = sockfd; 129 130 // Listen for incoming connections 131 132 } else { 133 socklen_t len = sizeof(address); 134 135 if (listen(sockfd, 5)) error_exit("listen"); 136 if (!TT.port) { 137 getsockname(sockfd, (struct sockaddr *)&address, &len); 138 printf("%d\n", SWAP_BE16(address.sin_port)); 139 fflush(stdout); 140 } 141 // Do we need to return immediately because -l has arguments? 142 143 if ((toys.optflags & FLAG_l) && toys.optc) { 144 if (xfork()) goto cleanup; 145 close(0); 146 close(1); 147 close(2); 148 } 149 150 for (;;) { 151 pid_t child = 0; 152 153 // For -l, call accept from the _new_ process. 154 155 pollfds[0].fd = accept(sockfd, (struct sockaddr *)&address, &len); 156 if (pollfds[0].fd<0) perror_exit("accept"); 157 158 // Do we need a tty? 159 160 if (toys.optflags&FLAG_t) 161 child = forkpty(&(pollfds[1].fd), NULL, NULL, NULL); 162 163 // Do we need to fork and/or redirect for exec? 164 165 else { 166 if (toys.optflags&FLAG_L) child = fork(); 167 if (!child && toys.optc) { 168 int fd = pollfds[0].fd; 169 170 dup2(fd, 0); 171 dup2(fd, 1); 172 if (toys.optflags&FLAG_L) dup2(fd, 2); 173 if (fd>2) close(fd); 174 } 175 } 176 177 if (child<0) error_msg("Fork failed\n"); 178 if (child<1) break; 179 close(pollfds[0].fd); 180 } 181 } 182 } 183 184 // We have a connection. Disarm timeout. 185 // (Does not play well with -L, but what _should_ that do?) 186 set_alarm(0); 187 188 if (CFG_NETCAT_LISTEN && (toys.optflags&(FLAG_L|FLAG_l) && toys.optc)) 189 xexec(toys.optargs); 190 191 // Poll loop copying stdin->socket and socket->stdout. 192 for (;;) { 193 int i; 194 195 if (0>poll(pollfds, pollcount, -1)) perror_exit("poll"); 196 197 for (i=0; i<pollcount; i++) { 198 if (pollfds[i].revents & POLLIN) { 199 int len = read(pollfds[i].fd, toybuf, sizeof(toybuf)); 200 if (len<1) goto dohupnow; 201 xwrite(i ? pollfds[0].fd : 1, toybuf, len); 202 } else if (pollfds[i].revents & POLLHUP) { 203dohupnow: 204 // Close half-connection. This is needed for things like 205 // "echo GET / | netcat landley.net 80" 206 if (i) { 207 shutdown(pollfds[0].fd, SHUT_WR); 208 pollcount--; 209 set_alarm(TT.quit_delay); 210 } else goto cleanup; 211 } 212 } 213 } 214cleanup: 215 if (CFG_TOYBOX_FREE) { 216 close(pollfds[0].fd); 217 close(sockfd); 218 } 219} 220