1 /* netcat.c - Forward stdin/stdout to a file or network connection. 2 * 3 * Copyright 2007 Rob Landley <rob (at) 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 in1 = out2 = sockfd; 130 131 // Listen for incoming connections 132 133 } else { 134 socklen_t len = sizeof(*address); 135 136 if (listen(sockfd, 5)) error_exit("listen"); 137 if (!TT.port) { 138 getsockname(sockfd, (struct sockaddr *)address, &len); 139 printf("%d\n", SWAP_BE16(address->sin_port)); 140 fflush(stdout); 141 // Return immediately if no -p and -Ll has arguments, so wrapper 142 // script can use port number. 143 if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup; 144 } 145 146 for (;;) { 147 child = 0; 148 len = sizeof(*address); // gcc's insane optimizer can overwrite this 149 in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len); 150 151 if (in1<0) perror_exit("accept"); 152 153 // We can't exit this loop or the optimizer's "liveness analysis" 154 // combines badly with vfork() to corrupt or local variables 155 // (the child's call stack gets trimmed and the next function call 156 // stops the variables the parent tries to re-use next loop) 157 // So there's a bit of redundancy here 158 159 // We have a connection. Disarm timeout. 160 set_alarm(0); 161 162 if (toys.optc) { 163 // Do we need a tty? 164 165 // TODO nommu, and -t only affects server mode...? Only do -t with optc 166 // if (CFG_TOYBOX_FORK && (toys.optflags&FLAG_t)) 167 // child = forkpty(&fdout, NULL, NULL, NULL); 168 // else 169 170 // Do we need to fork and/or redirect for exec? 171 172 if (toys.optflags&FLAG_L) { 173 toys.stacktop = 0; 174 child = vfork(); 175 } 176 if (child<0) error_msg("vfork failed\n"); 177 else { 178 if (child) { 179 close(in1); 180 continue; 181 } 182 dup2(in1, 0); 183 dup2(in1, 1); 184 if (toys.optflags&FLAG_L) dup2(in1, 2); 185 if (in1>2) close(in1); 186 xexec(toys.optargs); 187 } 188 } 189 190 pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay); 191 close(in1); 192 } 193 } 194 } 195 196 // We have a connection. Disarm timeout. 197 set_alarm(0); 198 199 pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay); 200 201 cleanup: 202 if (CFG_TOYBOX_FREE) { 203 close(in1); 204 close(sockfd); 205 } 206 } 207