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 8082cad5cf659644a39a437d5fd5bba198688343..ecbab22e7c48ea9d4f66e8658c334d1aef3d8c97 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 fb7006817c5d690e2ef4f629ccdfbe4b4bdfd2c8..90c165eadf242c46223db49a14ed8fa646d3f5e0 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 8ec79c76294771ae8027b76eca6beefc23b7095e..5691e8ae24e518fa1d79ab3fe53d1a317281ae0a 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 754290223170c56654d66a7bb07ed08202c1ef0d..47856d7c52f72e52304cfe78a9dd34a91171f038 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 ef03ceb2934b32d313198bfa2022de575b4f151b..8bd23e74329227ea0bda09779357ab20c0f14727 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 cfcca1c16434a5d79e5e78d260bc761f2e8b7cb5..9a93dc967acfa31dd064e2bca58090e74370ddc4 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;
       }
     }
   }