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
From cd3a377366ed25dcdb821559b39b8319b65eabf0 Mon Sep 17 00:00:00 2001
From: Josh Gao <jm...@google.com>
Date: Wed, 28 Jun 2017 15:41:56 -0700
Subject: [PATCH] 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 | 204 +++++++++++++++++++++++++++++-------------------------
 3 files changed, 148 insertions(+), 111 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..285979f 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,76 @@ 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
-
-    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
-
-    } 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);
-        // 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;
-      }
-
-      for (;;) {
-        child = 0;
-        len = sizeof(*address); // gcc's insane optimizer can overwrite this
-        in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len);
-
-        if (in1<0) perror_exit("accept");
-
-        // 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
-
-        // We have a connection. Disarm timeout.
-        set_alarm(0);
-
-        if (toys.optc) {
-          // Do we need a tty?
+    sockfd = xgetaddrinfo(toys.optargs[0], toys.optargs[1], family,
+      SOCK_STREAM, 0, 0, socket_connect, 0);
+    if (sockfd < 0) perror_exit("connect");
+
+    in1 = out2 = sockfd;
+  } else {
+    socklen_t len = sizeof(*address);
+
+    sockfd = xgetaddrinfo(TT.source_address, TT.port, family, SOCK_STREAM, 0, 0,
+      socket_server, &sockfd);
+    if (sockfd == -1) perror_exit("bind");
+
+    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;
+    }
+
+    for (;;) {
+      child = 0;
+      len = sizeof(*address); // gcc's insane optimizer can overwrite this
+      in1 = out2 = accept(sockfd, (struct sockaddr *)address, &len);
+
+      if (in1<0) perror_exit("accept");
+
+      // 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
+
+      // 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

_______________________________________________
Toybox mailing list
Toybox@lists.landley.net
http://lists.landley.net/listinfo.cgi/toybox-landley.net

Reply via email to