chiark / gitweb /
'listen' option now uses struct netaddress too.
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 14 Mar 2009 15:48:24 +0000 (15:48 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sat, 14 Mar 2009 15:48:24 +0000 (15:48 +0000)
If '*' is used then it will bind to the IPv6 default listen address and
then report an error trying to bind to the IPv4 address.  In fact this
is harmless as the socket will accept connections of both kinds.

lib/addr.c
lib/addr.h
lib/configuration.c
lib/configuration.h
server/server.c
server/state.c

index 8082cad..ecbab22 100644 (file)
@@ -95,29 +95,38 @@ struct addrinfo *get_address(const struct stringlist *a,
  */
 int addrinfocmp(const struct addrinfo *a,
                const struct addrinfo *b) {
-  const struct sockaddr_in *ina, *inb;
-  const struct sockaddr_in6 *in6a, *in6b;
-  
   if(a->ai_family != b->ai_family) return a->ai_family - b->ai_family;
   if(a->ai_socktype != b->ai_socktype) return a->ai_socktype - b->ai_socktype;
   if(a->ai_protocol != b->ai_protocol) return a->ai_protocol - b->ai_protocol;
-  switch(a->ai_family) {
+  return sockaddrcmp(a->ai_addr, b->ai_addr);
+}
+
+/** @brief Comparison function for socket addresses
+ *
+ * Suitable for qsort().
+ */
+int sockaddrcmp(const struct sockaddr *a,
+               const struct sockaddr *b) {
+  const struct sockaddr_in *ina, *inb;
+  const struct sockaddr_in6 *in6a, *in6b;
+  
+  if(a->sa_family != b->sa_family) return a->sa_family - b->sa_family;
+  switch(a->sa_family) {
   case PF_INET:
-    ina = (const struct sockaddr_in *)a->ai_addr;
-    inb = (const struct sockaddr_in *)b->ai_addr;
+    ina = (const struct sockaddr_in *)a;
+    inb = (const struct sockaddr_in *)b;
     if(ina->sin_port != inb->sin_port) return ina->sin_port - inb->sin_port;
     return ina->sin_addr.s_addr - inb->sin_addr.s_addr;
     break;
   case PF_INET6:
-    in6a = (const struct sockaddr_in6 *)a->ai_addr;
-    in6b = (const struct sockaddr_in6 *)b->ai_addr;
+    in6a = (const struct sockaddr_in6 *)a;
+    in6b = (const struct sockaddr_in6 *)b;
     if(in6a->sin6_port != in6b->sin6_port)
       return in6a->sin6_port - in6b->sin6_port;
     return memcmp(&in6a->sin6_addr, &in6b->sin6_addr,
                  sizeof (struct in6_addr));
   default:
-    error(0, "unsupported protocol family %d", a->ai_protocol);
-    return memcmp(a->ai_addr, b->ai_addr, a->ai_addrlen); /* kludge */
+    fatal(0, "unsupported protocol family %d", a->sa_family);
   }
 }
 
index fb70068..90c165e 100644 (file)
@@ -47,6 +47,8 @@ struct addrinfo *get_address(const struct stringlist *a,
 
 int addrinfocmp(const struct addrinfo *a,
                const struct addrinfo *b);
+int sockaddrcmp(const struct sockaddr *a,
+               const struct sockaddr *b);
 
 int multicast(const struct sockaddr *sa);
 char *format_sockaddr(const struct sockaddr *sa);
index 8ec79c7..5691e8a 100644 (file)
@@ -854,24 +854,6 @@ static int validate_addrport(const struct config_state attribute((unused)) *cs,
   }
 }
 
-static int validate_port(const struct config_state attribute((unused)) *cs,
-                        int nvec,
-                        char attribute((unused)) **vec) {
-  switch(nvec) {
-  case 0:
-    error(0, "%s:%d: missing address",
-         cs->path, cs->line);
-    return -1;
-  case 1:
-  case 2:
-    return 0;
-  default:
-    error(0, "%s:%d: expected [ADDRESS] PORT",
-         cs->path, cs->line);
-    return -1;
-  }
-}
-
 static int validate_algo(const struct config_state attribute((unused)) *cs,
                         int nvec,
                         char **vec) {
@@ -960,7 +942,7 @@ static const struct conf conf[] = {
   { C(gap),              &type_integer,          validate_non_negative },
   { C(history),          &type_integer,          validate_positive },
   { C(home),             &type_string,           validate_isabspath },
-  { C(listen),           &type_stringlist,       validate_port },
+  { C(listen),           &type_netaddress,       validate_any },
   { C(lock),             &type_boolean,          validate_any },
   { C(mail_sender),      &type_string,           validate_any },
   { C(mixer),            &type_string,           validate_any },
@@ -1240,6 +1222,7 @@ static struct config *config_default(void) {
   }
   c->broadcast.af = -1;
   c->broadcast_from.af = -1;
+  c->listen.af = -1;
   return c;
 }
 
index 7542902..47856d7 100644 (file)
@@ -155,7 +155,7 @@ struct config {
   long prefsync;                       /* preflog sync interval */
 
   /** @brief Secondary listen address */
-  struct stringlist listen;
+  struct netaddress listen;
 
   /** @brief Alias format string */
   const char *alias;
index ef03ceb..8bd23e7 100644 (file)
@@ -1850,6 +1850,7 @@ int server_start(ev_source *ev, int pf,
   l->pf = pf;
   if(ev_listen(ev, fd, listen_callback, l, "server listener"))
     exit(EXIT_FAILURE);
+  info("listening on %s", name);
   return fd;
 }
 
index cfcca1c..9a93dc9 100644 (file)
 static const char *current_unix;
 static int current_unix_fd;
 
-static struct addrinfo *current_listen_addrinfo;
-static int current_listen_fd;
+/** @brief TCP listener definition */
+struct listener {
+  struct listener *next;
+  struct sockaddr *sa;
+  int fd;
+};
+
+/** @brief Current listeners */
+static struct listener *listeners;
 
 /** @brief Current audio API */
 const struct uaudio *api;
@@ -38,18 +45,17 @@ void quit(ev_source *ev) {
   exit(0);
 }
 
+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);
+  return sa;
+}
+
 static void reset_socket(ev_source *ev) {
   const char *new_unix;
-  struct addrinfo *res;
+  struct addrinfo *res, *r;
+  struct listener *l, **ll;
   struct sockaddr_un sun;
-  char *name;
-  
-  static const struct addrinfo pref = {
-    .ai_flags = AI_PASSIVE,
-    .ai_family = PF_INET,
-    .ai_socktype = SOCK_STREAM,
-    .ai_protocol = IPPROTO_TCP,
-  };
 
   /* unix first */
   new_unix = config_get_file("socket");
@@ -80,28 +86,42 @@ static void reset_socket(ev_source *ev) {
   }
 
   /* get the new listen config */
-  if(config->listen.n)
-    res = get_address(&config->listen, &pref, &name);
+  if(config->listen.af != -1)
+    res = netaddress_resolve(&config->listen, 1, IPPROTO_TCP);
   else
     res = 0;
 
-  if((res && !current_listen_addrinfo)
-     || (current_listen_addrinfo
-        && (!res
-            || addrinfocmp(res, current_listen_addrinfo)))) {
-    /* something has to change */
-    if(current_listen_addrinfo) {
-      /* delete the old listener */
-      server_stop(ev, current_listen_fd);
-      freeaddrinfo(current_listen_addrinfo);
-      current_listen_addrinfo = 0;
+  /* 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))
+       break;
+    if(!r) {
+      /* Didn't find a match, remove this one */
+      server_stop(ev, l->fd);
+      *ll = l->next;
+    } else {
+      /* This address is still wanted */
+      ll = &l->next;
     }
-    if(res) {
-      /* start the new listener */
-      if((current_listen_fd = server_start(ev, res->ai_family, res->ai_addrlen,
-                                          res->ai_addr, name)) >= 0) {
-       current_listen_addrinfo = res;
-       res = 0;
+  }
+
+  /* Open any new listeners that are required */
+  for(r = res; r; r = r->ai_next) {
+    for(l = listeners; l; l = l->next)
+      if(!sockaddrcmp(r->ai_addr, 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));
+      if(fd >= 0) {
+       l = xmalloc(sizeof *l);
+       l->next = listeners;
+       l->sa = copy_sockaddr(r);
+       l->fd = fd;
+       listeners = l;
       }
     }
   }