diff options
Diffstat (limited to 'toys/net/netcat.c')
-rw-r--r-- | toys/net/netcat.c | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/toys/net/netcat.c b/toys/net/netcat.c new file mode 100644 index 00000000..1c75eb26 --- /dev/null +++ b/toys/net/netcat.c @@ -0,0 +1,230 @@ +/* netcat.c - Forward stdin/stdout to a file or network connection. + * + * Copyright 2007 Rob Landley <rob@landley.net> + * + * TODO: udp, ipv6, genericize for telnet/microcom/tail-f + +USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN)) +USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#p#s:q#f:", TOYFLAG_BIN)) + +config NETCAT + bool "netcat" + default y + help + usage: netcat [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME} + + -f use FILENAME (ala /dev/ttyS0) instead of network + -p local port number + -q SECONDS quit this many seconds after EOF on stdin. + -s local ipv4 address + -w SECONDS timeout for connection + + Use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with + netcat -f to connect to a serial port. + +config NETCAT_LISTEN + bool "netcat server options (-let)" + default y + depends on NETCAT + depends on TOYBOX_FORK + help + usage: netcat [-lL COMMAND...] + + -l listen for one incoming connection. + -L listen for multiple incoming connections (server mode). + + The command line after -l or -L is 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 + +config NETCAT_LISTEN_TTY + bool + default y + depends on NETCAT_LISTEN + depends on TOYBOX_FORK + help + usage: netcat [-t] + + -t allocate tty (must come before -l or -L) +*/ + +#define FOR_netcat +#include "toys.h" + +GLOBALS( + char *filename; // -f read from filename instead of network + 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 wait; // -w Wait # seconds for a connection. +) + +static void timeout(int signum) +{ + if (TT.wait) error_exit("Timeout"); + // This should be xexit() but would need siglongjmp()... + exit(0); +} + +static void set_alarm(int seconds) +{ + xsignal(SIGALRM, seconds ? timeout : SIG_DFL); + alarm(seconds); +} + +// Translate x.x.x.x numeric IPv4 address, or else DNS lookup an IPv4 name. +static void lookup_name(char *name, uint32_t *result) +{ + struct hostent *hostbyname; + + hostbyname = gethostbyname(name); // getaddrinfo + if (!hostbyname) error_exit("no host '%s'", name); + *result = *(uint32_t *)*hostbyname->h_addr_list; +} + +// Worry about a fancy lookup later. +static void lookup_port(char *str, uint16_t *port) +{ + *port = SWAP_BE16(atoi(str)); +} + +void netcat_main(void) +{ + int sockfd=-1, pollcount=2; + struct pollfd pollfds[2]; + + memset(pollfds, 0, 2*sizeof(struct pollfd)); + 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 : + (!(toys.optflags&(FLAG_l|FLAG_L)) && toys.optc!=2)) + help_exit("Argument count wrong"); + + if (TT.filename) pollfds[0].fd = xopen(TT.filename, O_RDWR); + else { + int temp; + struct sockaddr_in address; + + // Setup socket + sockfd = xsocket(AF_INET, SOCK_STREAM, 0); + fcntl(sockfd, F_SETFD, FD_CLOEXEC); + temp = 1; + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &temp, sizeof(temp)); + memset(&address, 0, sizeof(address)); + address.sin_family = AF_INET; + 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, (struct sockaddr *)&address, sizeof(address))) + perror_exit("bind"); + } + + // 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, (struct sockaddr *)&address, &len); + printf("%d\n", SWAP_BE16(address.sin_port)); + fflush(stdout); + } + // Do we need to return immediately because -l has arguments? + + if ((toys.optflags & FLAG_l) && toys.optc) { + if (CFG_TOYBOX_FORK && xfork()) goto cleanup; + close(0); + close(1); + close(2); + } + + for (;;) { + pid_t child = 0; + + // For -l, call accept from the _new_ process. + + pollfds[0].fd = accept(sockfd, (struct sockaddr *)&address, &len); + if (pollfds[0].fd<0) perror_exit("accept"); + + // 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 (toys.optflags&FLAG_L) { + toys.stacktop = 0; + child = vfork(); + } + if (!child && toys.optc) { + int fd = pollfds[0].fd; + + dup2(fd, 0); + dup2(fd, 1); + if (toys.optflags&FLAG_L) dup2(fd, 2); + if (fd>2) close(fd); + } + } + + if (child<0) error_msg("Fork failed\n"); + if (child<1) break; + close(pollfds[0].fd); + } + } + } + + // We have a connection. Disarm timeout. + // (Does not play well with -L, but what _should_ that do?) + set_alarm(0); + + if (CFG_NETCAT_LISTEN && ((toys.optflags&(FLAG_L|FLAG_l)) && toys.optc)) + xexec(toys.optargs); + + // Poll loop copying stdin->socket and socket->stdout. + for (;;) { + int i; + + if (0>poll(pollfds, pollcount, -1)) perror_exit("poll"); + + for (i=0; i<pollcount; i++) { + if (pollfds[i].revents & POLLIN) { + int len = read(pollfds[i].fd, toybuf, sizeof(toybuf)); + if (len<1) goto dohupnow; + xwrite(i ? pollfds[0].fd : 1, toybuf, len); + } else if (pollfds[i].revents & POLLHUP) { +dohupnow: + // 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: + if (CFG_TOYBOX_FREE) { + close(pollfds[0].fd); + close(sockfd); + } +} |