noip.c (encode_inet_addr): Avoid collisions with wildcard addresses.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 2 May 2016 21:35:42 +0000 (22:35 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 2 May 2016 23:14:25 +0000 (00:14 +0100)
If the caller is wanting to `encode' a currently floating socket (i.e.,
no explicit port number has been set and we're meant to pick one), then
make sure we pick a port number which doesn't collide with either the
chosen address /or/ the address-family wildcard address.  Otherwise, we
can get into the situation where process A listens on a floating socket
with a wildcard address, tells process B which port was allocated, and
then process B binds to localhost, tries to connect to A, and is stymied
because B actually allocated the same port number itself.

Really we ought to seed the RNG separately for each process.

noip.c

diff --git a/noip.c b/noip.c
index ee86680..de7cf89 100644 (file)
--- a/noip.c
+++ b/noip.c
@@ -624,6 +624,34 @@ done:
   return (rc);
 }
 
+/* Convert the IP address SA to a Unix-domain address SUN.  Fail if the
+ * address seems already taken.  If DESPARATEP then try cleaning up stale old
+ * sockets.
+ */
+static int encode_unused_inet_addr(struct sockaddr *sa,
+                                  struct sockaddr_un *sun,
+                                  int desperatep)
+{
+  address waddr;
+  struct sockaddr_un wsun;
+  int rc;
+  char buf[ADDRBUFSZ];
+
+  snprintf(sun->sun_path, sizeof(sun->sun_path), "%s/%s", sockdir,
+          present_sockaddr(sa, 0, buf, sizeof(buf)));
+  if ((rc = unix_socket_status(sun, !desperatep)) == USED) return (-1);
+  else if (rc == STALE) unlink(sun->sun_path);
+
+  wildcard_address(sa->sa_family, &waddr.sa);
+  port_to_sockaddr(&waddr.sa, port_from_sockaddr(sa));
+  snprintf(wsun.sun_path, sizeof(wsun.sun_path), "%s/%s", sockdir,
+          present_sockaddr(&waddr.sa, 0, buf, sizeof(buf)));
+  if ((rc = unix_socket_status(&wsun, !desperatep)) == USED) return (-1);
+  else if (rc == STALE) unlink(wsun.sun_path);
+
+  return (0);
+}
+
 /* Encode the Internet address SA as a Unix-domain address SUN.  If WANT is
  * WANT_FRESH, and SA's port number is zero, then we pick an arbitrary local
  * port.  Otherwise we pick the port given.  There's an unpleasant hack to
@@ -660,20 +688,12 @@ static int encode_inet_addr(struct sockaddr_un *sun,
     copy_sockaddr(&addr.sa, sa);
     for (i = 0; i < 10; i++) {
       port_to_sockaddr(&addr.sa, randrange(minautoport, maxautoport));
-      snprintf(sun->sun_path, sizeof(sun->sun_path), "%s/%s", sockdir,
-              present_sockaddr(&addr.sa, 0, buf, sizeof(buf)));
-      if (unix_socket_status(sun, 1) == UNUSED) goto found;
+      if (!encode_unused_inet_addr(&addr.sa, sun, 0)) goto found;
     }
     for (desperatep = 0; desperatep < 2; desperatep++) {
       for (i = minautoport; i <= maxautoport; i++) {
        port_to_sockaddr(&addr.sa, i);
-       snprintf(sun->sun_path, sizeof(sun->sun_path), "%s/%s", sockdir,
-                present_sockaddr(&addr.sa, 0, buf, sizeof(buf)));
-       rc = unix_socket_status(sun, !desperatep);
-       switch (rc) {
-         case STALE: unlink(sun->sun_path);
-         case UNUSED: goto found;
-       }
+       if (!encode_unused_inet_addr(&addr.sa, sun, 0)) goto found;
       }
     }
     errno = EADDRINUSE;