Updated the original patch to just jump to cleanup, instead of rearranging things.
Also, polished up the proof of concept xgetaddrinfo (this needs a better name) patch, and used it to implement -4, -6 (plus another patch to do -u). On Thu, Jun 29, 2017 at 6:59 PM, enh <e...@google.com> wrote: > ping? various networking folks are looking to add tests that use > netcat, and i'd rather start them off on toybox netcat rather than BSD > and then have to move them across to toybox later. (obviously there's > other missing stuff in toybox, but these patches are the only things > they actually need right now.) > > On Wed, Jun 28, 2017 at 3:47 PM, Josh Gao <jm...@google.com> wrote: > > On Sun, Jun 25, 2017 at 12:14 PM, Rob Landley <r...@landley.net> wrote: > >> > >> 1) switching it to use xconnect() which it predates, and which is hard > >> because the various users in tree all want slighty different things out > >> of the getaddrinfo() plumbing and I've made a couple attempts to > >> unify/genericize it but keep getting pulled alway by $DAYJOB crisis du > >> jour halfway through and forgetting what design problem details I was > >> halfway through solving and have to start over again... > > > > > > BTW, I took a quick look at this because we have users that want -4/-6 > (and > > IPv6 support in general). `nc -s` makes it so that you can't use xconnect > > because you don't know what to bind to until after you've resolved the > > target > > address. Something like xbind_and_connect might work, but there's also > > things > > that we might want to do in between socket and bind (e.g. setting > > SO_REUSEADDR). > > > > The thing that everyone really wants is a way to iterate over getaddrinfo > > results; > > maybe that's what should be exposed? I have a rough proof of concept > patch > > attached that implements this and uses it in netcat. > > > > (There's also another edge case with -s: what happens if the host you > pass > > in > > resolves to multiple addresses? OpenBSD's netcat seems to just bind the > > first > > compatible address it resolves to, so we can maybe just ignore this.) > > > > -Josh > > > > _______________________________________________ > > Toybox mailing list > > Toybox@lists.landley.net > > http://lists.landley.net/listinfo.cgi/toybox-landley.net > > > > > > -- > Elliott Hughes - http://who/enh - http://jessies.org/~enh/ > Android native code/tools questions? Mail me/drop by/add me as a reviewer. >
From bebee0943874f77bb4a7ff7126ba13738e50a3e3 Mon Sep 17 00:00:00 2001 From: Josh Gao <jm...@google.com> Date: Wed, 28 Jun 2017 15:41:56 -0700 Subject: [PATCH 1/4] Add xgetaddrinfo, use it in netcat. Add a helper function that iterates over getaddrinfo results with a callback, and switch netcat over to it to add support for IPv6. --- lib/lib.h | 2 + lib/net.c | 53 ++++++++++----- toys/net/netcat.c | 191 ++++++++++++++++++++++++++++++------------------------ 3 files changed, 144 insertions(+), 102 deletions(-) diff --git a/lib/lib.h b/lib/lib.h index 0b93bde..7d12233 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -284,6 +284,8 @@ void tty_sigreset(int i); // net.c int xsocket(int domain, int type, int protocol); void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len); +int xgetaddrinfo(char *host, char *port, int family, int socktype, int protocol, + int flags, int (*cb)(struct addrinfo*, void*), void *cookie); int xconnect(char *host, char *port, int family, int socktype, int protocol, int flags); int xpoll(struct pollfd *fds, int nfds, int timeout); diff --git a/lib/net.c b/lib/net.c index df2551f..b4f5f65 100644 --- a/lib/net.c +++ b/lib/net.c @@ -15,36 +15,55 @@ void xsetsockopt(int fd, int level, int opt, void *val, socklen_t len) if (-1 == setsockopt(fd, level, opt, val, len)) perror_exit("setsockopt"); } -int xconnect(char *host, char *port, int family, int socktype, int protocol, - int flags) +int xgetaddrinfo(char *host, char *port, int family, int socktype, int protocol, + int flags, int (*cb)(struct addrinfo*, void*), void *cookie) { - struct addrinfo info, *ai, *ai2; - int fd; + int rc; + struct addrinfo info, *ai, *ai_head; - memset(&info, 0, sizeof(struct addrinfo)); + memset(&info, 0, sizeof(info)); info.ai_family = family; info.ai_socktype = socktype; info.ai_protocol = protocol; info.ai_flags = flags; - fd = getaddrinfo(host, port, &info, &ai); - if (fd || !ai) - error_exit("Connect '%s%s%s': %s", host, port ? ":" : "", port ? port : "", - fd ? gai_strerror(fd) : "not found"); - - // Try all the returned addresses. Report errors if last entry can't connect. - for (ai2 = ai; ai; ai = ai->ai_next) { - fd = (ai->ai_next ? socket : xsocket)(ai->ai_family, ai->ai_socktype, - ai->ai_protocol); - if (!connect(fd, ai->ai_addr, ai->ai_addrlen)) break; - else if (!ai->ai_next) perror_exit("connect"); + rc = getaddrinfo(host, port, &info, &ai); + if (rc || !ai) { + error_exit("getaddrinfo '%s%s%s': %s", host, port ? ":" : "", port ? port : "", + rc ? gai_strerror(rc) : "not found"); + } + + for (ai_head = ai; ai; ai = ai->ai_next) { + rc = cb(ai, cookie); + if (rc != -1) break; + } + freeaddrinfo(ai_head); + + return rc; +} + +static int _xconnect(struct addrinfo* ai, void *unused) { + int fd; + + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd == -1) { + return -1; + } else if (!connect(fd, ai->ai_addr, ai->ai_addrlen)) { close(fd); + return -1; } - freeaddrinfo(ai2); return fd; } +int xconnect(char *host, char *port, int family, int socktype, int protocol, + int flags) +{ + int rc = xgetaddrinfo(host, port, family, socktype, protocol, flags, _xconnect, 0); + if (rc == -1) perror_exit("connect"); + return rc; +} + int xpoll(struct pollfd *fds, int nfds, int timeout) { int i; diff --git a/toys/net/netcat.c b/toys/net/netcat.c index 13ba311..1ad8b19 100644 --- a/toys/net/netcat.c +++ b/toys/net/netcat.c @@ -5,7 +5,7 @@ * 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#W#p#s:q#f:"USE_NETCAT_LISTEN("[!tlL][!Lw]"), TOYFLAG_BIN)) +USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#W#p:s:q#f:"USE_NETCAT_LISTEN("[!tlL][!Lw]"), TOYFLAG_BIN)) config NETCAT bool "netcat" @@ -50,7 +50,7 @@ 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. + char *port; // -p Bind to a specific source port. long idle; // -W Wait # seconds for more data long wait; // -w Wait # seconds for a connection. ) @@ -68,26 +68,59 @@ static void set_alarm(int seconds) 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; +static int socket_bind(struct addrinfo *ai, void *fd_) { + int fd = *(int*)fd_; + return bind(fd, ai->ai_addr, ai->ai_addrlen); } -// Worry about a fancy lookup later. -static void lookup_port(char *str, uint16_t *port) -{ - *port = SWAP_BE16(atoi(str)); +static int socket_connect(struct addrinfo *ai, void *unused) { + int fd, rc; + + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd == -1) return -1; + + fcntl(fd, F_SETFD, FD_CLOEXEC); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)); + + if (TT.source_address || TT.port) { + rc = xgetaddrinfo(TT.source_address, TT.port, ai->ai_family, + ai->ai_socktype, ai->ai_protocol, ai->ai_flags, socket_bind, &fd); + if (rc != 0) { + close(fd); + return -1; + } + } + + rc = connect(fd, ai->ai_addr, ai->ai_addrlen); + if (rc == -1) { + close(fd); + return -1; + } + return fd; +} + +static int socket_server(struct addrinfo *ai, void *unused) { + int fd, rc; + + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd == -1) return -1; + + fcntl(fd, F_SETFD, FD_CLOEXEC); + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)); + + if (bind(fd, ai->ai_addr, ai->ai_addrlen)) { + close(fd); + return -1; + } + + return fd; } void netcat_main(void) { struct sockaddr_in *address = (void *)toybuf; - int sockfd=-1, in1 = 0, in2 = 0, out1 = 1, out2 = 1; + int sockfd = -1, in1 = 0, in2 = 0, out1 = 1, out2 = 1; + int family = 0; // TODO: -4, -6 pid_t child; // Addjust idle and quit_delay to miliseconds or -1 for no timeout @@ -103,93 +136,81 @@ void netcat_main(void) help_exit("bad argument count"); if (TT.filename) in1 = out2 = xopen(TT.filename, O_RDWR); - else { - // Setup socket - sockfd = xsocket(AF_INET, SOCK_STREAM, 0); - fcntl(sockfd, F_SETFD, FD_CLOEXEC); - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &out1, sizeof(out1)); - 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"); - } - + else if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) { // Dial out + sockfd = xgetaddrinfo(toys.optargs[0], toys.optargs[1], family, + SOCK_STREAM, 0, 0, socket_connect, 0); + if (sockfd < 0) perror_exit("connect"); - 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)); -// TODO xconnect - if (connect(sockfd, (struct sockaddr *)address, sizeof(*address))<0) - perror_exit("connect"); - in1 = out2 = sockfd; - - // Listen for incoming connections + in1 = out2 = sockfd; + } else { + socklen_t len = sizeof(*address); + if (TT.source_address || TT.port) { + sockfd = xgetaddrinfo(TT.source_address, TT.port, family, SOCK_STREAM, 0, 0, + socket_server, &sockfd); } else { - socklen_t len = sizeof(*address); + sockfd = xsocket(family ? family : AF_INET, SOCK_STREAM, 0); + } - 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); - // Return immediately if no -p and -Ll has arguments, so wrapper - // script can use port number. - if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup; - } + if (sockfd == -1) perror_exit("bind"); - for (;;) { - child = 0; - len = sizeof(*address); // gcc's insane optimizer can overwrite this - in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len); + 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); + // Return immediately if no -p and -Ll has arguments, so wrapper + // script can use port number. + if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup; + } - if (in1<0) perror_exit("accept"); + for (;;) { + child = 0; + len = sizeof(*address); // gcc's insane optimizer can overwrite this + in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len); - // We can't exit this loop or the optimizer's "liveness analysis" - // combines badly with vfork() to corrupt or local variables - // (the child's call stack gets trimmed and the next function call - // stops the variables the parent tries to re-use next loop) - // So there's a bit of redundancy here + if (in1<0) perror_exit("accept"); - // We have a connection. Disarm timeout. - set_alarm(0); + // We can't exit this loop or the optimizer's "liveness analysis" + // combines badly with vfork() to corrupt or local variables + // (the child's call stack gets trimmed and the next function call + // stops the variables the parent tries to re-use next loop) + // So there's a bit of redundancy here - if (toys.optc) { - // Do we need a tty? + // We have a connection. Disarm timeout. + set_alarm(0); + + if (toys.optc) { + // Do we need a tty? // TODO nommu, and -t only affects server mode...? Only do -t with optc -// if (CFG_TOYBOX_FORK && (toys.optflags&FLAG_t)) -// child = forkpty(&fdout, NULL, NULL, NULL); -// else +// if (CFG_TOYBOX_FORK && (toys.optflags&FLAG_t)) +// child = forkpty(&fdout, NULL, NULL, NULL); +// else - // Do we need to fork and/or redirect for exec? + // Do we need to fork and/or redirect for exec? - if (toys.optflags&FLAG_L) { - toys.stacktop = 0; - child = vfork(); - } - if (child<0) error_msg("vfork failed\n"); - else { - if (child) { - close(in1); - continue; - } - dup2(in1, 0); - dup2(in1, 1); - if (toys.optflags&FLAG_L) dup2(in1, 2); - if (in1>2) close(in1); - xexec(toys.optargs); + if (toys.optflags&FLAG_L) { + toys.stacktop = 0; + child = vfork(); + } + if (child<0) error_msg("vfork failed\n"); + else { + if (child) { + close(in1); + continue; } + dup2(in1, 0); + dup2(in1, 1); + if (toys.optflags&FLAG_L) dup2(in1, 2); + if (in1>2) close(in1); + xexec(toys.optargs); } - - pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay); - close(in1); } + + pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay); + close(in1); } } -- 2.13.2.725.g09c95d1e9-goog
From 82223a53be96f57de34591f4ed3694e0a0cdc6f6 Mon Sep 17 00:00:00 2001 From: Josh Gao <jm...@google.com> Date: Wed, 28 Jun 2017 15:53:16 -0700 Subject: [PATCH 2/4] Add -4, -6 to netcat. --- toys/net/netcat.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/toys/net/netcat.c b/toys/net/netcat.c index 1ad8b19..cbfa875 100644 --- a/toys/net/netcat.c +++ b/toys/net/netcat.c @@ -5,7 +5,7 @@ * 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#W#p:s:q#f:"USE_NETCAT_LISTEN("[!tlL][!Lw]"), TOYFLAG_BIN)) +USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#W#p:s:q#f:46"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46]", TOYFLAG_BIN)) config NETCAT bool "netcat" @@ -13,6 +13,7 @@ config NETCAT help usage: netcat [-u] [-wpq #] [-s addr] {IPADDR PORTNUM|-f FILENAME} + -4, -6 force IPv4, IPv6 -f use FILENAME (ala /dev/ttyS0) instead of network -p local port number -q quit SECONDS after EOF on stdin, even if stdout hasn't closed yet @@ -120,9 +121,12 @@ void netcat_main(void) { struct sockaddr_in *address = (void *)toybuf; int sockfd = -1, in1 = 0, in2 = 0, out1 = 1, out2 = 1; - int family = 0; // TODO: -4, -6 + int family = 0; pid_t child; + if (toys.optflags&FLAG_4) family = AF_INET; + if (toys.optflags&FLAG_6) family = AF_INET6; + // Addjust idle and quit_delay to miliseconds or -1 for no timeout TT.idle = TT.idle ? TT.idle*1000 : -1; TT.quit_delay = TT.quit_delay ? TT.quit_delay*1000 : -1; -- 2.13.2.725.g09c95d1e9-goog
From 1cb43b4c8e793f37db3708c8eed887f504e8e1a1 Mon Sep 17 00:00:00 2001 From: Josh Gao <jm...@google.com> Date: Thu, 29 Jun 2017 13:39:55 -0700 Subject: [PATCH 3/4] Add UDP support to netcat. --- toys/net/netcat.c | 42 +++++++++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 9 deletions(-) diff --git a/toys/net/netcat.c b/toys/net/netcat.c index cbfa875..b3a0c05 100644 --- a/toys/net/netcat.c +++ b/toys/net/netcat.c @@ -5,7 +5,7 @@ * 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#W#p:s:q#f:46"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46]", TOYFLAG_BIN)) +USE_NETCAT(NEWTOY(netcat, USE_NETCAT_LISTEN("^tlL")"w#W#p:s:uq#f:46"USE_NETCAT_LISTEN("[!tlL][!Lw]")"[!46][!fu]", TOYFLAG_BIN)) config NETCAT bool "netcat" @@ -18,6 +18,7 @@ config NETCAT -p local port number -q quit SECONDS after EOF on stdin, even if stdout hasn't closed yet -s local source address + -u use UDP instead of TCP -w SECONDS timeout to establish connection -W SECONDS timeout for idle connection @@ -81,7 +82,10 @@ static int socket_connect(struct addrinfo *ai, void *unused) { if (fd == -1) return -1; fcntl(fd, F_SETFD, FD_CLOEXEC); - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)); + + if (!(toys.optflags&FLAG_u)) { + setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &rc, sizeof(rc)); + } if (TT.source_address || TT.port) { rc = xgetaddrinfo(TT.source_address, TT.port, ai->ai_family, @@ -121,11 +125,13 @@ void netcat_main(void) { struct sockaddr_in *address = (void *)toybuf; int sockfd = -1, in1 = 0, in2 = 0, out1 = 1, out2 = 1; + int socktype = SOCK_STREAM; int family = 0; pid_t child; - if (toys.optflags&FLAG_4) family = AF_INET; - if (toys.optflags&FLAG_6) family = AF_INET6; + if ((toys.optflags&FLAG_u)) socktype = SOCK_DGRAM; + if ((toys.optflags&FLAG_4)) family = AF_INET; + if ((toys.optflags&FLAG_6)) family = AF_INET6; // Addjust idle and quit_delay to miliseconds or -1 for no timeout TT.idle = TT.idle ? TT.idle*1000 : -1; @@ -143,7 +149,7 @@ void netcat_main(void) else if (!CFG_NETCAT_LISTEN || !(toys.optflags&(FLAG_L|FLAG_l))) { // Dial out sockfd = xgetaddrinfo(toys.optargs[0], toys.optargs[1], family, - SOCK_STREAM, 0, 0, socket_connect, 0); + socktype, 0, 0, socket_connect, 0); if (sockfd < 0) perror_exit("connect"); in1 = out2 = sockfd; @@ -151,15 +157,29 @@ void netcat_main(void) socklen_t len = sizeof(*address); if (TT.source_address || TT.port) { - sockfd = xgetaddrinfo(TT.source_address, TT.port, family, SOCK_STREAM, 0, 0, + sockfd = xgetaddrinfo(TT.source_address, TT.port, family, socktype, 0, 0, socket_server, &sockfd); } else { - sockfd = xsocket(family ? family : AF_INET, SOCK_STREAM, 0); + sockfd = xsocket(family ? family : AF_INET, socktype, 0); } if (sockfd == -1) perror_exit("bind"); - if (listen(sockfd, 5)) error_exit("listen"); + if ((toys.optflags&FLAG_u)) { + struct sockaddr_storage ss; + socklen_t socklen = sizeof(ss); + + if (recvfrom(sockfd, 0, 0, MSG_PEEK, (struct sockaddr*)&ss, &socklen) == -1) { + perror_exit("recvfrom"); + } + + if (connect(sockfd, (struct sockaddr*)&ss, socklen) == -1) { + perror_exit("connect"); + } + } else { + if (listen(sockfd, 5)) perror_exit("listen"); + } + if (!TT.port) { getsockname(sockfd, (struct sockaddr *)address, &len); printf("%d\n", SWAP_BE16(address->sin_port)); @@ -172,7 +192,11 @@ void netcat_main(void) for (;;) { child = 0; len = sizeof(*address); // gcc's insane optimizer can overwrite this - in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len); + if ((toys.optflags&FLAG_u)) { + in1 = out2 = sockfd; + } else { + in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len); + } if (in1<0) perror_exit("accept"); -- 2.13.2.725.g09c95d1e9-goog
From 39d8a2cde7e11f26bfd91b201a4dcf169159b216 Mon Sep 17 00:00:00 2001 From: Josh Gao <jm...@google.com> Date: Fri, 30 Jun 2017 11:31:28 -0700 Subject: [PATCH 4/4] netcat: make -l exit after handling a request. --- toys/net/netcat.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/toys/net/netcat.c b/toys/net/netcat.c index b3a0c05..2464ad8 100644 --- a/toys/net/netcat.c +++ b/toys/net/netcat.c @@ -189,7 +189,7 @@ void netcat_main(void) if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup; } - for (;;) { + do { child = 0; len = sizeof(*address); // gcc's insane optimizer can overwrite this if ((toys.optflags&FLAG_u)) { @@ -239,7 +239,9 @@ void netcat_main(void) pollinate(in1, in2, out1, out2, TT.idle, TT.quit_delay); close(in1); - } + } while (!(toys.optflags&FLAG_l)); + + goto cleanup; } // We have a connection. Disarm timeout. -- 2.13.2.725.g09c95d1e9-goog
_______________________________________________ Toybox mailing list Toybox@lists.landley.net http://lists.landley.net/listinfo.cgi/toybox-landley.net