diff options
Diffstat (limited to 'toys/netcat.c')
-rw-r--r-- | toys/netcat.c | 188 |
1 files changed, 140 insertions, 48 deletions
diff --git a/toys/netcat.c b/toys/netcat.c index f5024c9b..9e27669c 100644 --- a/toys/netcat.c +++ b/toys/netcat.c @@ -6,28 +6,40 @@ * * Not in SUSv3. -USE_NETCAT(OLDTOY(nc, netcat, "i#w#l@p#s:q#f:e", TOYFLAG_BIN)) -USE_NETCAT(NEWTOY(netcat, "i#w#l@p#s:q#f:e", TOYFLAG_BIN)) +USE_NETCAT(OLDTOY(nc, netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN)) +USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("tl^L^")"w#p#s:q#f:", TOYFLAG_BIN)) config NETCAT bool "netcat" - default n + default y help - usage: netcat [-iwlp] {IPADDR PORTNUM|-f FILENAME} [-e COMMAND] + usage: netcat [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME|-let} [-e COMMAND] - -e exec the rest of the command line - -i SECONDS delay after each line sent -w SECONDS timeout for connection - -f filename use file (ala /dev/ttyS0) instead of network - -l listen for incoming connection (twice for persistent connection) -p local port number - -s local source address + -s local ipv4 address -q SECONDS quit this many seconds after EOF on stdin. - - Use -l twice with -e for a quick-and-dirty server. + -f use FILENAME (ala /dev/ttyS0) instead of network Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with netcat -f to connect to a serial port. + + +config NETCAT_LISTEN + bool "netcat sever options (-let)" + default y + depends on NETCAT + help + -t allocate tty (must come before -l or -L) + -l listen for one incoming connection. + -L listen for multiple incoming connections (server mode). + + Any additional command line arguments after -l or -L are executed + to handle each incoming connection. If none, the connection is + forwarded to stdin/stdout. + + For a quick-and-dirty server, try something like: + netcat -s 127.0.0.1 -p 1234 -tL /bin/bash -l */ #include "toys.h" @@ -38,54 +50,65 @@ DEFINE_GLOBALS( long quit_delay; // -q Exit after EOF from stdin after # seconds. char *source_address; // -s Bind to a specific source address. long port; // -p Bind to a specific source port. - long listen; // -l Listen for connection instead of dialing out. long wait; // -w Wait # seconds for a connection. - long delay; // -i delay between lines sent ) #define TT this.netcat +#define FLAG_f 1 +#define FLAG_L 32 +#define FLAG_l 64 +#define FLAG_t 128 + static void timeout(int signum) { - error_exit("Timeout"); + if (TT.wait) error_exit("Timeout"); + exit(0); +} + +static void set_alarm(int seconds) +{ + signal(SIGALRM, seconds ? timeout : SIG_DFL); + alarm(seconds); } // Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name. -void lookup_name(char *name, uint32_t *result) +static void lookup_name(char *name, uint32_t *result) { struct hostent *hostbyname; - hostbyname = gethostbyname(*toys.optargs); - if (!hostbyname) error_exit("name lookup failed"); + hostbyname = gethostbyname(name); + if (!hostbyname) error_exit("no host '%s'", name); *result = *(uint32_t *)*hostbyname->h_addr_list; } // Worry about a fancy lookup later. -void lookup_port(char *str, uint16_t *port) +static void lookup_port(char *str, uint16_t *port) { *port = SWAP_BE16(atoi(str)); } void netcat_main(void) { - int sockfd, pollcount; + int sockfd=-1, pollcount=2; struct pollfd pollfds[2]; - if (TT.wait) { - signal(SIGALRM, timeout); - alarm(TT.wait); - } + pollfds[0].events = pollfds[1].events = POLLIN; + set_alarm(TT.wait); + + // The argument parsing logic can't make "<2" conditional on other + // arguments like -f and -l, so we do it by hand here. + if ((toys.optflags&FLAG_f) && toys.optc!=1) toys.exithelp++; + if (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2) toys.exithelp++; + + if (toys.exithelp) error_exit("Argument count wrong"); if (TT.filename) pollfds[0].fd = xopen(TT.filename, O_RDWR); else { int temp; struct sockaddr_in address; - // The argument parsing logic can't make "<2" conditional on "-f", so... - if (!*toys.optargs || !toys.optargs[1]) { - toys.exithelp++; - error_exit("Need address and port"); - } + pollfds[1].fd = 0; // Setup socket sockfd = socket(AF_INET, SOCK_STREAM, 0); @@ -95,29 +118,95 @@ void netcat_main(void) setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(temp)); memset(&address, 0, sizeof(address)); address.sin_family = AF_INET; - if (TT.port) { - address.sin_port = TT.port; - if (-1 == bind(sockfd, &address, sizeof(address))) + if (TT.source_address || TT.port) { + address.sin_port = SWAP_BE16(TT.port); + if (TT.source_address) + lookup_name(TT.source_address, (uint32_t *)&address.sin_addr); + if (bind(sockfd, &address, sizeof(address))) perror_exit("bind"); } - // Figure out where to dial out to. - lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr); - lookup_port(toys.optargs[1], &address.sin_port); - temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address)); - if (temp<0) perror_exit("connect"); - pollfds[0].fd = sockfd; + // Dial out + + if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) { + // Figure out where to dial out to. + lookup_name(*toys.optargs, (uint32_t *)&address.sin_addr); + lookup_port(toys.optargs[1], &address.sin_port); + temp = connect(sockfd, (struct sockaddr *)&address, sizeof(address)); + if (temp<0) perror_exit("connect"); + pollfds[0].fd = sockfd; + + // Listen for incoming connections + + } else { + socklen_t len = sizeof(address); + + if (listen(sockfd, 5)) error_exit("listen"); + if (!TT.port) { + getsockname(sockfd, &address, &len); + printf("%d\n", SWAP_BE16(address.sin_port)); + fflush(stdout); + } + // Do we need to defer calling accept() to the new thread + // because -l has arguments and we want it to return immediately? + temp = 0; + if ((toys.optflags&FLAG_l) && toys.optc) temp++; + + for (;;) { + pid_t child = 0; + + // For -l, call accept from the _new_ thread. + + if (temp != 1) { + pollfds[0].fd = accept(sockfd, (struct sockaddr *)&address, + &len); + if (pollfds[0].fd<0) perror_exit("accept"); + + if (temp==2) { + close(sockfd); + break; + } + } else temp++; + + // Do we need a tty? + + if (toys.optflags&FLAG_t) + child = forkpty(&(pollfds[1].fd), NULL, NULL, NULL); + + // Do we need to fork and/or redirect for exec? + + else { + if (temp || (toys.optflags&FLAG_L)) child = fork(); + if (!child && toys.optc) { + int fd = pollfds[0].fd; + + if (!temp) close(sockfd); + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd>2) close(fd); + } + } + + if (child<0) error_msg("Fork failed\n"); + if (child<1) { + if (!temp) break; + continue; + } + if (temp) exit(0); + close(pollfds[0].fd); + } + } } // We have a connection. Disarm timeout. - if (TT.wait) { - alarm(0); - signal(SIGALRM, SIG_DFL); - } + // (Does not play well with -L, but what _should_ that do?) + set_alarm(0); - pollcount = 2; - pollfds[1].fd = 0; - pollfds[0].events = pollfds[1].events = POLLIN; + if (CFG_NETCAT_LISTEN && (toys.optflags&(FLAG_L|FLAG_l) && toys.optc)) { + execvp(*toys.optargs, toys.optargs); + error_exit("Exec failed"); + } // Poll loop copying stdin->socket and socket->stdout. for (;;) { @@ -133,16 +222,19 @@ void netcat_main(void) } if (pollfds[i].revents & POLLHUP) { dohupnow: - // Close half-connect. This is needed for things like - // "echo GET / | netcat landley.net 80" to work. + // Close half-connection. This is needed for things like + // "echo GET / | netcat landley.net 80" if (i) { shutdown(pollfds[0].fd, SHUT_WR); pollcount--; + set_alarm(TT.quit_delay); } else goto cleanup; } } } cleanup: - close(pollfds[0].fd); -// close(sockfd); + if (CFG_TOYBOX_FREE) { + close(pollfds[0].fd); + close(sockfd); + } } |