lib/addr.c, etc.: Return plain addresses from `netaddress_resolve'.
authorMark Wooding <mdw@distorted.org.uk>
Sat, 6 Jun 2020 12:27:45 +0000 (13:27 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 15 Jun 2020 12:03:09 +0000 (13:03 +0100)
Instead of a list of `struct addrinfo' structures, return a vector of
`struct resolved', each of which just contains a socket address and
length.  This is sufficient for all of the existing callers (which have
been changed to cope), and means that we don't need any awful hacks
because they're easy to free.

lib/addr.c
lib/addr.h
lib/client-common.c
lib/uaudio-rtp.c
server/state.c

index 802204a..0ba88f1 100644 (file)
@@ -339,16 +339,18 @@ void netaddress_format(const struct netaddress *na,
 /** @brief Resolve a network address
  * @param na Address structure
  * @param passive True if passive (bindable) address is desired
- * @param protocol Protocol number desired (e.g. @c IPPROTO_TCP)
- * @return List of suitable addresses or NULL
+ * @param type Socket type (e.g., @c SOCK_STREAM, @c SOCK_DGRAM)
+ * @param[out] raddr_out Vector of address/length pairs
+ * @param[out] nraddr_out Length of output vector
+ * @return Zero on success, -1 on error
  *
- * Free the address using netaddress_freeaddrinfo() because it might not
- * have come from getaddrinfo() directly.
+ * Call netaddress_free_resolved() to free the resolved addresses.
  */
-struct addrinfo *netaddress_resolve(const struct netaddress *na,
-                                   int passive,
-                                   int protocol) {
-  struct addrinfo *res, hints[1];
+int netaddress_resolve(const struct netaddress *na, int passive, int type,
+                      struct resolved **raddr_out, size_t *nraddr_out) {
+  struct resolved *raddr;
+  size_t i, nraddr;
+  struct addrinfo *res, *ai, hints[1];
   struct sockaddr_un *sun;
   char service[64];
   int rc;
@@ -357,26 +359,21 @@ struct addrinfo *netaddress_resolve(const struct netaddress *na,
 #if HAVE_SYS_UN_H
   if (na->af == AF_UNIX) {
     /* `getaddrinfo' won't work, so we make our own one */
-    res = xmalloc(sizeof(*res));
-    res->ai_flags = 0;
-    res->ai_family = AF_UNIX;
-    res->ai_socktype = (protocol == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
-    res->ai_protocol = 0;
-    res->ai_addrlen = offsetof(struct sockaddr_un, sun_path) +
+    raddr = xmalloc(sizeof(*raddr));
+    raddr->len = offsetof(struct sockaddr_un, sun_path) +
       strlen(na->address) + 1;
-    sun = xmalloc(res->ai_addrlen);
+    sun = xmalloc_noptr(raddr->len);
     sun->sun_family = AF_UNIX;
     strcpy(sun->sun_path, na->address);
-    res->ai_addr = (struct sockaddr *)sun;
-    res->ai_canonname = sun->sun_path;
-    res->ai_next = 0;
+    raddr->sa = (struct sockaddr *)sun;
+    nraddr = 1;
   } else
 #endif
   {
     /* get the system to do the heavy lifting */
     memset(hints, 0, sizeof hints);
     hints->ai_family = na->af;
-    hints->ai_protocol = protocol;
+    hints->ai_socktype = type;
     hints->ai_flags = passive ? AI_PASSIVE : 0;
     byte_snprintf(service, sizeof service, "%d", na->port);
     rc = getaddrinfo(na->address, service, hints, &res);
@@ -385,24 +382,33 @@ struct addrinfo *netaddress_resolve(const struct netaddress *na,
                     na->address ? na->address : "*",
                     na->port,
                     format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
-      return NULL;
+      return -1;
+    }
+    /* copy the addresses into an output vector */
+    for(ai = res, nraddr = 0; ai; ai = ai->ai_next, nraddr++);
+    raddr = xmalloc(nraddr*sizeof(*raddr));
+    for(ai = res, i = 0; ai; ai = ai->ai_next, i++) {
+      raddr[i].sa = xmalloc_noptr(ai->ai_addrlen);
+      raddr[i].len = ai->ai_addrlen;
+      memcpy(raddr[i].sa, ai->ai_addr, ai->ai_addrlen);
     }
-    assert(res->ai_family != AF_UNIX);
+    freeaddrinfo(res);
   }
-  return res;
+  *raddr_out = raddr;
+  *nraddr_out = nraddr;
+  return 0;
 }
 
-/** @brief Free an address-info list from netaddress_resovle()
- * @param res Address-info list
- */
-void netaddress_freeaddrinfo(struct addrinfo *res) {
-#if HAVE_SYS_UN_H
-  if(res->ai_family == AF_UNIX) {
-    xfree(res->ai_addr);
-    xfree(res);
-  } else
-#endif
-    freeaddrinfo(res);
+/** @brief Free a vector of resolved addresses */
+void netaddress_free_resolved(struct resolved *raddr, size_t nraddr)
+{
+  size_t i;
+
+  if (!raddr)
+    return;
+  for(i = 0; i < nraddr; i++)
+    xfree(raddr[i].sa);
+  xfree(raddr);
 }
 
 /*
index c0e82b0..edb727c 100644 (file)
@@ -43,6 +43,16 @@ struct netaddress {
   int port;
 };
 
+/** @brief A socket address and length */
+struct resolved {
+
+  /** @brief Pointer to the address */
+  struct sockaddr *sa;
+
+  /** @brief Length of the address */
+  socklen_t len;
+};
+
 struct addrinfo *get_address(const struct stringlist *a,
                             const struct addrinfo *pref,
                             char **namep)
@@ -62,10 +72,10 @@ int netaddress_parse(struct netaddress *na,
 void netaddress_format(const struct netaddress *na,
                       int *nvecp,
                       char ***vecp) attribute((nonnull (1)));
-struct addrinfo *netaddress_resolve(const struct netaddress *na,
-                                   int passive,
-                                   int protocol) attribute((nonnull (1)));
-void netaddress_freeaddrinfo(struct addrinfo *res) attribute((nonnull (1)));
+int netaddress_resolve(const struct netaddress *na, int passive, int type,
+                      struct resolved **raddr_out, size_t *nraddr_out)
+  attribute((nonnull (1, 4, 5)));
+void netaddress_free_resolved(struct resolved *raddr, size_t nraddr);
 
 #endif /* ADDR_H */
 
index f344159..c2ea90d 100644 (file)
@@ -57,16 +57,16 @@ socklen_t disorder_find_server(struct config *c, unsigned flags,
 #if !_WIN32
   struct sockaddr_un su;
 #endif
-  struct addrinfo *res = 0;
+  struct resolved *res;
+  size_t nres;
   char *name = NULL;
   socklen_t len;
 
   if(c->connect.af != -1) {
-    res = netaddress_resolve(&c->connect, 0, IPPROTO_TCP);
-    if(!res) 
+    if(netaddress_resolve(&c->connect, 0, SOCK_STREAM, &res, &nres))
       return -1;
-    sa = res->ai_addr;
-    len = res->ai_addrlen;
+    sa = res->sa;
+    len = res->len;
   } else {
 #if _WIN32
     disorder_fatal(0, "local connections are not supported on Windows");
@@ -90,6 +90,7 @@ socklen_t disorder_find_server(struct config *c, unsigned flags,
     strcpy(su.sun_path, name);
     sa = (struct sockaddr *)&su;
     len = sizeof su;
+    res = 0;
     xfree(name);
 #endif
   }
@@ -98,7 +99,7 @@ socklen_t disorder_find_server(struct config *c, unsigned flags,
   if(namep)
     *namep = format_sockaddr(sa);
   if(res)
-    netaddress_freeaddrinfo(res);
+    netaddress_free_resolved(res, nres);
   return len;
 }
 
index 9e7abde..dc1fd6c 100644 (file)
@@ -261,7 +261,8 @@ static void hack_send_buffer_size(int fd, const char *what) {
 }
 
 static void rtp_open(void) {
-  struct addrinfo *dres, *sres;
+  struct resolved *dres, *sres;
+  size_t ndres, nsres;
   static const int one = 1;
   struct netaddress dst[1], src[1];
   const char *mode;
@@ -287,22 +288,24 @@ static void rtp_open(void) {
                     "rtp-source-port",
                     src);
   if(dst->af != -1) {
-    dres = netaddress_resolve(dst, 0, IPPROTO_UDP);
-    if(!dres)
+    if(netaddress_resolve(dst, 0, SOCK_DGRAM, &dres, &ndres))
       exit(-1);
-  } else
+  } else {
     dres = 0;
+    ndres = 0;
+  }
   if(src->af != -1) {
-    sres = netaddress_resolve(src, 1, IPPROTO_UDP);
-    if(!sres)
+    if(netaddress_resolve(src, 0, SOCK_DGRAM, &sres, &nsres))
       exit(-1);
-  } else
+  } else {
     sres = 0;
+    nsres = 0;
+  }
   /* _AUTO inspects the destination address and acts accordingly */
   if(rtp_mode == RTP_AUTO) {
     if(!dres)
       rtp_mode = RTP_REQUEST;
-    else if(multicast(dres->ai_addr))
+    else if(multicast(dres->sa))
       rtp_mode = RTP_MULTICAST;
     else {
       struct ifaddrs *ifs;
@@ -315,7 +318,7 @@ static void rtp_open(void) {
          * for he same interface which _does_ have ifa_broadaddr though... */
         if((ifs->ifa_flags & IFF_BROADCAST)
            && ifs->ifa_broadaddr
-           && sockaddr_equal(ifs->ifa_broadaddr, dres->ai_addr))
+           && sockaddr_equal(ifs->ifa_broadaddr, dres->sa))
           break;
         ifs = ifs->ifa_next;
       }
@@ -330,9 +333,7 @@ static void rtp_open(void) {
     rtp_max_payload = 1500 - 8/*UDP*/ - 40/*IP*/ - 8/*conservatism*/;
   /* Create the sockets */
   if(rtp_mode != RTP_REQUEST) {
-    if((rtp_fd = socket(dres->ai_family,
-                        dres->ai_socktype,
-                        dres->ai_protocol)) < 0)
+    if((rtp_fd = socket(dres->sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
       disorder_fatal(errno, "error creating RTP transmission socket");
   }
   if((rtp_fd4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
@@ -345,7 +346,7 @@ static void rtp_open(void) {
     /* Enable multicast options */
     const int ttl = atoi(uaudio_get("multicast-ttl", "1"));
     const int loop = !strcmp(uaudio_get("multicast-loop", "yes"), "yes");
-    switch(dres->ai_family) {
+    switch(dres->sa->sa_family) {
     case PF_INET: {
       if(setsockopt(rtp_fd, IPPROTO_IP, IP_MULTICAST_TTL,
                     &ttl, sizeof ttl) < 0)
@@ -365,21 +366,21 @@ static void rtp_open(void) {
       break;
     }
     default:
-      disorder_fatal(0, "unsupported address family %d", dres->ai_family);
+      disorder_fatal(0, "unsupported address family %d", dres->sa->sa_family);
     }
     disorder_info("multicasting on %s TTL=%d loop=%s", 
-                  format_sockaddr(dres->ai_addr), ttl, loop ? "yes" : "no");
+                  format_sockaddr(dres->sa), ttl, loop ? "yes" : "no");
     break;
   }
   case RTP_UNICAST: {
-    disorder_info("unicasting on %s", format_sockaddr(dres->ai_addr));
+    disorder_info("unicasting on %s", format_sockaddr(dres->sa));
     break;
   }
   case RTP_BROADCAST: {
     if(setsockopt(rtp_fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
       disorder_fatal(errno, "error setting SO_BROADCAST on broadcast socket");
     disorder_info("broadcasting on %s", 
-                  format_sockaddr(dres->ai_addr));
+                  format_sockaddr(dres->sa));
     break;
   }
   case RTP_REQUEST: {
@@ -394,12 +395,12 @@ static void rtp_open(void) {
   /* We might well want to set additional broadcast- or multicast-related
    * options here */
   if(rtp_mode != RTP_REQUEST) {
-    if(sres && bind(rtp_fd, sres->ai_addr, sres->ai_addrlen) < 0)
+    if(sres && bind(rtp_fd, sres->sa, sres->len) < 0)
       disorder_fatal(errno, "error binding broadcast socket to %s", 
-                     format_sockaddr(sres->ai_addr));
-    if(connect(rtp_fd, dres->ai_addr, dres->ai_addrlen) < 0)
+                     format_sockaddr(sres->sa));
+    if(connect(rtp_fd, dres->sa, dres->len) < 0)
       disorder_fatal(errno, "error connecting broadcast socket to %s", 
-                     format_sockaddr(dres->ai_addr));
+                     format_sockaddr(dres->sa));
   }
 #ifdef IP_MTU_DISCOVER
   mtu_disc = uaudio_get("rtp-mtu-discovery", "default");
@@ -409,7 +410,7 @@ static void rtp_open(void) {
     else break;
     if(setsockopt(rtp_fd4, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof opt))
       disorder_fatal(errno, "error setting MTU discovery");
-    if(sres->ai_family == AF_INET &&
+    if(sres->sa->sa_family == AF_INET &&
         setsockopt(rtp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof opt))
       disorder_fatal(errno, "error setting MTU discovery");
   } while (0);
index 63c7c45..19e8be3 100644 (file)
@@ -73,9 +73,9 @@ void quit(ev_source *ev) {
 }
 
 /** @brief Create a copy of an @c addrinfo structure */
-static struct sockaddr *copy_sockaddr(const struct addrinfo *addr) {
-  struct sockaddr *sa = xmalloc_noptr(addr->ai_addrlen);
-  memcpy(sa, addr->ai_addr, addr->ai_addrlen);
+static struct sockaddr *copy_sockaddr(const struct resolved *raddr) {
+  struct sockaddr *sa = xmalloc_noptr(raddr->len);
+  memcpy(sa, raddr->sa, raddr->len);
   return sa;
 }
 
@@ -114,7 +114,8 @@ static void reset_unix_socket(ev_source *ev,
 
 /** @brief Create and destroy sockets to set current configuration */
 void reset_sockets(ev_source *ev) {
-  struct addrinfo *res, *r;
+  struct resolved *res;
+  size_t i, nres;
   struct listener *l, **ll;
   const char *private_dir = config_get_file("private");
   
@@ -128,18 +129,17 @@ void reset_sockets(ev_source *ev) {
   reset_unix_socket(ev, priv_socket, config_get_file("private/socket"), 1);
 
   /* get the new listen config */
-  if(config->listen.af != -1)
-    res = netaddress_resolve(&config->listen, 1, IPPROTO_TCP);
-  else
-    res = 0;
+  res = 0; nres = 0;
+  if(config->listen.af != -1) 
+    netaddress_resolve(&config->listen, 1, SOCK_STREAM, &res, &nres);
 
   /* Close any current listeners that aren't required any more */
   ll = &listeners;
   while((l = *ll)) {
-    for(r = res; r; r = r->ai_next)
-      if(!sockaddrcmp(r->ai_addr, l->sa))
+    for(i = 0; i < nres; i++)
+      if(!sockaddrcmp(res[i].sa, l->sa))
        break;
-    if(!r) {
+    if(i >= nres) {
       /* Didn't find a match, remove this one */
       server_stop(ev, l->fd);
       *ll = l->next;
@@ -150,18 +150,18 @@ void reset_sockets(ev_source *ev) {
   }
 
   /* Open any new listeners that are required */
-  for(r = res; r; r = r->ai_next) {
+  for(i = 0; i < nres; i++) {
     for(l = listeners; l; l = l->next)
-      if(!sockaddrcmp(r->ai_addr, l->sa))
+      if(!sockaddrcmp(res[i].sa, l->sa))
        break;
     if(!l) {
       /* Didn't find a match, need a new listener */
-      int fd = server_start(ev, r->ai_family, r->ai_addrlen, r->ai_addr,
-                           format_sockaddr(r->ai_addr), 0);
+      int fd = server_start(ev, res[i].sa->sa_family, res[i].len, res[i].sa,
+                           format_sockaddr(res[i].sa), 0);
       if(fd >= 0) {
        l = xmalloc(sizeof *l);
        l->next = listeners;
-       l->sa = copy_sockaddr(r);
+       l->sa = copy_sockaddr(&res[i]);
        l->fd = fd;
        listeners = l;
       }
@@ -171,7 +171,7 @@ void reset_sockets(ev_source *ev) {
   }
   /* if res is still set it needs freeing */
   if(res)
-    netaddress_freeaddrinfo(res);
+    netaddress_free_resolved(res, nres);
 }
 
 /** @brief Reconfigure the server