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 7 USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN)) 8 USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#W#p#s:q#f:"USE_NETCAT_LISTEN("[!tlL][!Lw]"), TOYFLAG_BIN)) 9 10 config 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 quit SECONDS after EOF on stdin, even if stdout hasn't closed yet 19 -s local source address 20 -w SECONDS timeout to establish connection 21 -W SECONDS timeout for idle connection 22 23 Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with 24 netcat -f to connect to a serial port. 25 26 config NETCAT_LISTEN 27 bool "netcat server options (-let)" 28 default y 29 depends on NETCAT 30 help 31 usage: netcat [-t] [-lL COMMAND...] 32 33 -l listen for one incoming connection 34 -L listen for multiple incoming connections (server mode) 35 -t allocate tty (must come before -l or -L) 36 37 The command line after -l or -L is executed (as a child process) to handle 38 each incoming connection. If blank -l waits for a connection and forwards 39 it to stdin/stdout. If no -p specified, -l prints port it bound to and 40 backgrounds itself (returning immediately). 41 42 For a quick-and-dirty server, try something like: 43 netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l 44 */ 45 46 #define FOR_netcat 47 #include "toys.h" 48 49 GLOBALS( 50 char *filename; // -f read from filename instead of network 51 long quit_delay; // -q Exit after EOF from stdin after # seconds. 52 char *source_address; // -s Bind to a specific source address. 53 long port; // -p Bind to a specific source port. 54 long idle; // -W Wait # seconds for more data 55 long wait; // -w Wait # seconds for a connection. 56 ) 57 58 static void timeout(int signum) 59 { 60 if (TT.wait) error_exit("Timeout"); 61 // This should be xexit() but would need siglongjmp()... 62 exit(0); 63 } 64 65 static void set_alarm(int seconds) 66 { 67 xsignal(SIGALRM, seconds ? timeout : SIG_DFL); 68 alarm(seconds); 69 } 70 71 // Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name. 72 static void lookup_name(char *name, uint32_t *result) 73 { 74 struct hostent *hostbyname; 75 76 hostbyname = gethostbyname(name); // getaddrinfo 77 if (!hostbyname) error_exit("no host '%s'", name); 78 *result = *(uint32_t *)*hostbyname->h_addr_list; 79 } 80 81 // Worry about a fancy lookup later. 82 static void lookup_port(char *str, uint16_t *port) 83 { 84 *port = SWAP_BE16(atoi(str)); 85 } 86 87 void netcat_main(void) 88 { 89 struct sockaddr_in *address = (void *)toybuf; 90 int sockfd=-1, in1 = 0, in2 = 0, out1 = 1, out2 = 1; 91 pid_t child; 92 93 // Addjust idle and quit_delay to miliseconds or -1 for no timeout 94 TT.idle = TT.idle ? TT.idle*1000 : -1; 95 TT.quit_delay = TT.quit_delay ? TT.quit_delay*1000 : -1; 96 97 set_alarm(TT.wait); 98 99 // The argument parsing logic can't make "<2" conditional on other 100 // arguments like -f and -l, so we do it by hand here. 101 if ((toys.optflags&FLAG_f) ? toys.optc : 102 (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2)) 103 help_exit("bad argument count"); 104 105 if (TT.filename) in1 = out2 = xopen(TT.filename, O_RDWR); 106 else { 107 // Setup socket 108 sockfd = xsocket(AF_INET, SOCK_STREAM, 0); 109 fcntl(sockfd, F_SETFD, FD_CLOEXEC); 110 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &out1, sizeof(out1)); 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 // TODO xconnect 127 if (connect(sockfd, (struct sockaddr *)address, sizeof(*address))<0) 128 perror_exit("connect"); 129 130 // We have a connection. Disarm timeout. 131 set_alarm(0); 132 133 in1 = out2 = sockfd; 134 135 pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay); 136 } else { 137 // Listen for incoming connections 138 socklen_t len = sizeof(*address); 139 140 if (listen(sockfd, 5)) error_exit("listen"); 141 if (!TT.port) { 142 getsockname(sockfd, (struct sockaddr *)address, &len); 143 printf("%d\n", SWAP_BE16(address->sin_port)); 144 fflush(stdout); 145 // Return immediately if no -p and -Ll has arguments, so wrapper 146 // script can use port number. 147 if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup; 148 } 149 150 do { 151 child = 0; 152 len = sizeof(*address); // gcc's insane optimizer can overwrite this 153 in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len); 154 155 if (in1<0) perror_exit("accept"); 156 157 // We can't exit this loop or the optimizer's "liveness analysis" 158 // combines badly with vfork() to corrupt or local variables 159 // (the child's call stack gets trimmed and the next function call 160 // stops the variables the parent tries to re-use next loop) 161 // So there's a bit of redundancy here 162 163 // We have a connection. Disarm timeout. 164 set_alarm(0); 165 166 if (toys.optc) { 167 // Do we need a tty? 168 169 // TODO nommu, and -t only affects server mode...? Only do -t with optc 170 // if (CFG_TOYBOX_FORK && (toys.optflags&FLAG_t)) 171 // child = forkpty(&fdout, NULL, NULL, NULL); 172 // else 173 174 // Do we need to fork and/or redirect for exec? 175 176 if (toys.optflags&FLAG_L) { 177 toys.stacktop = 0; 178 child = vfork(); 179 } 180 if (child<0) error_msg("vfork failed\n"); 181 else { 182 if (child) { 183 close(in1); 184 continue; 185 } 186 dup2(in1, 0); 187 dup2(in1, 1); 188 if (toys.optflags&FLAG_L) dup2(in1, 2); 189 if (in1>2) close(in1); 190 xexec(toys.optargs); 191 } 192 } 193 194 pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay); 195 close(in1); 196 } while (!(toys.optflags&FLAG_l)); 197 } 198 } 199 200 cleanup: 201 if (CFG_TOYBOX_FREE) { 202 close(in1); 203 close(sockfd); 204 } 205 } 206