chiark / gitweb /
Start rationalizing network address configuration.
authorRichard Kettlewell <rjk@greenend.org.uk>
Sat, 14 Mar 2009 14:56:56 +0000 (14:56 +0000)
committerRichard Kettlewell <rjk@greenend.org.uk>
Sat, 14 Mar 2009 14:56:56 +0000 (14:56 +0000)
New 'struct netaddress' has address family, address and port.  Family
can be unspecified (effectively, let the OS choose), IPv4, IPv6 or UNIX
domain sockets (though in practice the latter won't work well in most
contexts).

The RTP server configuration uses this logic.

lib/addr.c
lib/addr.h
lib/configuration.c
lib/configuration.h
lib/uaudio-rtp.c
lib/uaudio.c
server/server.c

index fecc28c24b467612e3df8b4dbe626ddabcd007de..8082cad5cf659644a39a437d5fd5bba198688343 100644 (file)
@@ -30,6 +30,9 @@
 #include "printf.h"
 #include "addr.h"
 #include "mem.h"
 #include "printf.h"
 #include "addr.h"
 #include "mem.h"
+#include "syscalls.h"
+#include "configuration.h"
+#include "vector.h"
 
 /** @brief Convert a pair of strings to an address
  * @param a Pointer to string list
 
 /** @brief Convert a pair of strings to an address
  * @param a Pointer to string list
@@ -194,6 +197,142 @@ char *format_sockaddr(const struct sockaddr *sa) {
   }
 }
 
   }
 }
 
+/** @brief Parse the text form of a network address
+ * @param na Where to store result
+ * @param nvec Number of strings
+ * @param vec List of strings
+ * @return 0 on success, -1 on syntax error
+ */
+int netaddress_parse(struct netaddress *na,
+                    int nvec,
+                    char **vec) {
+  const char *port;
+
+  na->af = AF_UNSPEC;
+  if(nvec > 0 && vec[0][0] == '-') {
+    if(!strcmp(vec[0], "-4"))
+      na->af = AF_INET;
+    else if(!strcmp(vec[0], "-6")) 
+      na->af = AF_INET6;
+    else if(!strcmp(vec[0], "-unix"))
+      na->af = AF_UNIX;
+    else if(!strcmp(vec[0], "-"))
+      na->af = AF_UNSPEC;
+    else
+      return -1;
+    --nvec;
+    ++vec;
+  }
+  if(nvec == 0)
+    return -1;
+  /* Possibilities are:
+   *
+   *       /path/to/unix/socket      an AF_UNIX socket
+   *       * PORT                    any address, specific port
+   *       PORT                      any address, specific port
+   *       ADDRESS PORT              specific address, specific port
+   */
+  if(vec[0][0] == '/' && na->af == AF_UNSPEC)
+    na->af = AF_UNIX;
+  if(na->af == AF_UNIX) {
+    if(nvec != 1)
+      return -1;
+    na->address = xstrdup(vec[0]);
+    na->port = -1;                     /* makes no sense */
+  } else {
+    switch(nvec) {
+    case 1:
+      na->address = NULL;
+      port = vec[0];
+      break;
+    case 2:
+      if(!strcmp(vec[0], "*"))
+       na->address = NULL;
+      else
+       na->address = xstrdup(vec[0]);
+      port = vec[1];
+      break;
+    default:
+      return -1;
+    }
+    if(port[strspn(port, "0123456789")])
+      return -1;
+    long p;
+    int e = xstrtol(&p, port, NULL, 10);
+
+    if(e)
+      return -1;
+    if(p > 65535)
+      return -1;
+    na->port = (int)p;
+  }
+  return 0;
+}
+
+/** @brief Format a @ref netaddress structure
+ * @param na Network address to format
+ * @param nvecp Where to put string count, or NULL
+ * @param vecp Where to put strings
+ *
+ * The formatted form is suitable for passing to netaddress_parse().
+ */
+void netaddress_format(const struct netaddress *na,
+                      int *nvecp,
+                      char ***vecp) {
+  struct vector v[1];
+
+  vector_init(v);
+  switch(na->af) {
+  case AF_UNSPEC: vector_append(v, xstrdup("-")); break;
+  case AF_INET: vector_append(v, xstrdup("-4")); break;
+  case AF_INET6: vector_append(v, xstrdup("-6")); break;
+  case AF_UNIX: vector_append(v, xstrdup("-unix")); break;
+  }
+  if(na->address)
+    vector_append(v, xstrdup(na->address));
+  else
+    vector_append(v, xstrdup("*"));
+  if(na->port != -1) {
+    char buffer[64];
+
+    snprintf(buffer, sizeof buffer, "%d", na->port);
+    vector_append(v, xstrdup(buffer));
+  }
+  vector_terminate(v);
+  if(nvecp)
+    *nvecp = v->nvec;
+  if(vecp)
+    *vecp = v->vec;
+}
+
+/** @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
+ */
+struct addrinfo *netaddress_resolve(const struct netaddress *na,
+                                   int passive,
+                                   int protocol) {
+  struct addrinfo *res, hints[1];
+  char service[64];
+  int rc;
+
+  memset(hints, 0, sizeof hints);
+  hints->ai_family = na->af;
+  hints->ai_protocol = protocol;
+  hints->ai_flags = passive ? AI_PASSIVE : 0;
+  snprintf(service, sizeof service, "%d", na->port);
+  rc = getaddrinfo(na->address, service, hints, &res);
+  if(rc) {
+    error(0, "getaddrinfo %s %d: %s",
+         na->address ? na->address : "*",
+         na->port, gai_strerror(rc));
+    return NULL;
+  }
+  return res;
+}
+
 /*
 Local Variables:
 c-basic-offset:2
 /*
 Local Variables:
 c-basic-offset:2
index 973733f57ae9a2aa74581eb6b44551100ef94acc..fb7006817c5d690e2ef4f629ccdfbe4b4bdfd2c8 100644 (file)
 
 #include <netdb.h>
 
 
 #include <netdb.h>
 
-#include "configuration.h"
+struct stringlist;
+
+/** @brief A network address */
+struct netaddress {
+  /** @brief Address family
+   *
+   * Typically @c AF_UNIX, @c AF_INET, @c AF_INET6 or @c AF_UNSPEC.
+   * Set to -1 to mean 'no address'.
+   */
+  int af;
+
+  /** @brief Address or NULL for 'any' */
+  char *address;
+
+  /** @brief Port number */
+  int port;
+};
 
 struct addrinfo *get_address(const struct stringlist *a,
                             const struct addrinfo *pref,
 
 struct addrinfo *get_address(const struct stringlist *a,
                             const struct addrinfo *pref,
@@ -35,6 +51,16 @@ int addrinfocmp(const struct addrinfo *a,
 int multicast(const struct sockaddr *sa);
 char *format_sockaddr(const struct sockaddr *sa);
 
 int multicast(const struct sockaddr *sa);
 char *format_sockaddr(const struct sockaddr *sa);
 
+int netaddress_parse(struct netaddress *na,
+                    int nvec,
+                    char **vec);
+void netaddress_format(const struct netaddress *na,
+                      int *nvecp,
+                      char ***vecp);
+struct addrinfo *netaddress_resolve(const struct netaddress *na,
+                                   int passive,
+                                   int protocol);
+
 #endif /* ADDR_H */
 
 /*
 #endif /* ADDR_H */
 
 /*
index 67d50a07b8fd757a30b4de68ef60f6d5718f1b1d..8ec79c76294771ae8027b76eca6beefc23b7095e 100644 (file)
@@ -489,6 +489,18 @@ static int set_rights(const struct config_state *cs,
   return 0;
 }
 
   return 0;
 }
 
+static int set_netaddress(const struct config_state *cs,
+                         const struct conf *whoami,
+                         int nvec, char **vec) {
+  struct netaddress *na = ADDRESS(cs->config, struct netaddress);
+
+  if(netaddress_parse(na, nvec, vec)) {
+    error(0, "%s:%d: invalid network address", cs->path, cs->line);
+    return -1;
+  }
+  return 0;
+}
+
 /* free functions */
 
 static void free_none(struct config attribute((unused)) *c,
 /* free functions */
 
 static void free_none(struct config attribute((unused)) *c,
@@ -572,6 +584,13 @@ static void free_transformlist(struct config *c,
   xfree(tl->t);
 }
 
   xfree(tl->t);
 }
 
+static void free_netaddress(struct config *c,
+                           const struct conf *whoami) {
+  struct netaddress *na = ADDRESS(c, struct netaddress);
+
+  xfree(na->address);
+}
+
 /* configuration types */
 
 static const struct conftype
 /* configuration types */
 
 static const struct conftype
@@ -587,6 +606,7 @@ static const struct conftype
   type_restrict = { set_restrict, free_none },
   type_namepart = { set_namepart, free_namepartlist },
   type_transform = { set_transform, free_transformlist },
   type_restrict = { set_restrict, free_none },
   type_namepart = { set_namepart, free_namepartlist },
   type_transform = { set_transform, free_transformlist },
+  type_netaddress = { set_netaddress, free_netaddress },
   type_rights = { set_rights, free_none };
 
 /* specific validation routine */
   type_rights = { set_rights, free_none };
 
 /* specific validation routine */
@@ -898,6 +918,22 @@ static int validate_pausemode(const struct config_state attribute((unused)) *cs,
   return -1;
 }
 
   return -1;
 }
 
+static int validate_destaddr(const struct config_state attribute((unused)) *cs,
+                            int nvec,
+                            char **vec) {
+  struct netaddress na[1];
+
+  if(netaddress_parse(na, nvec, vec)) {
+    error(0, "%s:%d: invalid network address", cs->path, cs->line);
+    return -1;
+  }
+  if(!na->address) {
+    error(0, "%s:%d: destination address required", cs->path, cs->line);
+    return -1;
+  }
+  return 0;
+}
+
 /** @brief Item name and and offset */
 #define C(x) #x, offsetof(struct config, x)
 /** @brief Item name and and offset */
 /** @brief Item name and and offset */
 #define C(x) #x, offsetof(struct config, x)
 /** @brief Item name and and offset */
@@ -909,8 +945,8 @@ static const struct conf conf[] = {
   { C(allow),            &type_stringlist_accum, validate_allow },
   { C(api),              &type_string,           validate_backend },
   { C(authorization_algorithm), &type_string,    validate_algo },
   { C(allow),            &type_stringlist_accum, validate_allow },
   { C(api),              &type_string,           validate_backend },
   { C(authorization_algorithm), &type_string,    validate_algo },
-  { C(broadcast),        &type_stringlist,       validate_addrport },
-  { C(broadcast_from),   &type_stringlist,       validate_addrport },
+  { C(broadcast),        &type_netaddress,       validate_destaddr },
+  { C(broadcast_from),   &type_netaddress,       validate_any },
   { C(channel),          &type_string,           validate_any },
   { C(checkpoint_kbyte), &type_integer,          validate_non_negative },
   { C(checkpoint_min),   &type_integer,          validate_non_negative },
   { C(channel),          &type_string,           validate_any },
   { C(checkpoint_kbyte), &type_integer,          validate_non_negative },
   { C(checkpoint_min),   &type_integer,          validate_non_negative },
@@ -1202,6 +1238,8 @@ static struct config *config_default(void) {
                       default_players[n], "disorder-tracklength", (char *)0))
       exit(1);
   }
                       default_players[n], "disorder-tracklength", (char *)0))
       exit(1);
   }
+  c->broadcast.af = -1;
+  c->broadcast_from.af = -1;
   return c;
 }
 
   return c;
 }
 
@@ -1273,7 +1311,7 @@ static void config_postdefaults(struct config *c,
   if(!c->api) {
     if(c->speaker_command)
       c->api = xstrdup("command");
   if(!c->api) {
     if(c->speaker_command)
       c->api = xstrdup("command");
-    else if(c->broadcast.n)
+    else if(c->broadcast.af != -1)
       c->api = xstrdup("rtp");
     else if(config_uaudio_apis)
       c->api = xstrdup(config_uaudio_apis[0]->name);
       c->api = xstrdup("rtp");
     else if(config_uaudio_apis)
       c->api = xstrdup(config_uaudio_apis[0]->name);
@@ -1285,7 +1323,7 @@ static void config_postdefaults(struct config *c,
   if(server) {
     if(!strcmp(c->api, "command") && !c->speaker_command)
       fatal(0, "'api command' but speaker_command is not set");
   if(server) {
     if(!strcmp(c->api, "command") && !c->speaker_command)
       fatal(0, "'api command' but speaker_command is not set");
-    if((!strcmp(c->api, "rtp")) && !c->broadcast.n)
+    if((!strcmp(c->api, "rtp")) && c->broadcast.af == -1)
       fatal(0, "'api rtp' but broadcast is not set");
   }
   /* Override sample format */
       fatal(0, "'api rtp' but broadcast is not set");
   }
   /* Override sample format */
index 9a85e5da537c3bf440b6ddcddde44331df974181..754290223170c56654d66a7bb07ed08202c1ef0d 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "speaker-protocol.h"
 #include "rights.h"
 
 #include "speaker-protocol.h"
 #include "rights.h"
+#include "addr.h"
 
 struct uaudio;
 
 
 struct uaudio;
 
@@ -233,10 +234,10 @@ struct config {
   struct transformlist transform;      /* path name transformations */
 
   /** @brief Address to send audio data to */
   struct transformlist transform;      /* path name transformations */
 
   /** @brief Address to send audio data to */
-  struct stringlist broadcast;
+  struct netaddress broadcast;
 
   /** @brief Source address for network audio transmission */
 
   /** @brief Source address for network audio transmission */
-  struct stringlist broadcast_from;
+  struct netaddress broadcast_from;
 
   /** @brief RTP delay threshold */
   long rtp_delay_threshold;
 
   /** @brief RTP delay threshold */
   long rtp_delay_threshold;
index 01be09b52ed1cb638840ba0142db381cba13dcb6..bb03be4094b5c6c06687aedde8711b971e954865 100644 (file)
@@ -85,6 +85,50 @@ static const char *const rtp_options[] = {
   NULL
 };
 
   NULL
 };
 
+static void rtp_get_netconfig(const char *af,
+                              const char *addr,
+                              const char *port,
+                              struct netaddress *na) {
+  char *vec[3];
+  
+  vec[0] = uaudio_get(af, NULL);
+  vec[1] = uaudio_get(addr, NULL);
+  vec[2] = uaudio_get(port, NULL);
+  if(!*vec)
+    na->af = -1;
+  else
+    if(netaddress_parse(na, 3, vec))
+      fatal(0, "invalid RTP address");
+}
+
+static void rtp_set_netconfig(const char *af,
+                              const char *addr,
+                              const char *port,
+                              const struct netaddress *na) {
+  uaudio_set(af, NULL);
+  uaudio_set(addr, NULL);
+  uaudio_set(port, NULL);
+  if(na->af != -1) {
+    int nvec;
+    char **vec;
+
+    netaddress_format(na, &nvec, &vec);
+    if(nvec > 0) {
+      uaudio_set(af, vec[0]);
+      xfree(vec[0]);
+    }
+    if(nvec > 1) {
+      uaudio_set(addr, vec[1]);
+      xfree(vec[1]);
+    }
+    if(nvec > 2) {
+      uaudio_set(port, vec[2]);
+      xfree(vec[2]);
+    }
+    xfree(vec);
+  }
+}
+
 static size_t rtp_play(void *buffer, size_t nsamples) {
   struct rtp_header header;
   struct iovec vec[2];
 static size_t rtp_play(void *buffer, size_t nsamples) {
   struct rtp_header header;
   struct iovec vec[2];
@@ -129,52 +173,31 @@ static size_t rtp_play(void *buffer, size_t nsamples) {
 
 static void rtp_open(void) {
   struct addrinfo *res, *sres;
 
 static void rtp_open(void) {
   struct addrinfo *res, *sres;
-  static const struct addrinfo pref = {
-    .ai_flags = 0,
-    .ai_family = PF_INET,
-    .ai_socktype = SOCK_DGRAM,
-    .ai_protocol = IPPROTO_UDP,
-  };
-  static const struct addrinfo prefbind = {
-    .ai_flags = AI_PASSIVE,
-    .ai_family = PF_INET,
-    .ai_socktype = SOCK_DGRAM,
-    .ai_protocol = IPPROTO_UDP,
-  };
   static const int one = 1;
   int sndbuf, target_sndbuf = 131072;
   socklen_t len;
   static const int one = 1;
   int sndbuf, target_sndbuf = 131072;
   socklen_t len;
-  char *sockname, *ssockname;
-  struct stringlist dst, src;
+  struct netaddress dst[1], src[1];
   
   /* Get configuration */
   
   /* Get configuration */
-  dst.n = 2;
-  dst.s = xcalloc(2, sizeof *dst.s);
-  dst.s[0] = uaudio_get("rtp-destination", NULL);
-  dst.s[1] = uaudio_get("rtp-destination-port", NULL);
-  src.n = 2;
-  src.s = xcalloc(2, sizeof *dst.s);
-  src.s[0] = uaudio_get("rtp-source", NULL);
-  src.s[1] = uaudio_get("rtp-source-port", NULL);
-  if(!dst.s[0])
-    fatal(0, "'rtp-destination' not set");
-  if(!dst.s[1])
-    fatal(0, "'rtp-destination-port' not set");
-  if(src.s[0]) {
-    if(!src.s[1])
-      fatal(0, "'rtp-source-port' not set");
-    src.n = 2;
-  } else
-    src.n = 0;
+  rtp_get_netconfig("rtp-destination-af",
+                    "rtp-destination",
+                    "rtp-destination-port",
+                    dst);
+  rtp_get_netconfig("rtp-source-af",
+                    "rtp-source",
+                    "rtp-source-port",
+                    src);
   rtp_delay_threshold = atoi(uaudio_get("rtp-delay-threshold", "1000"));
   /* ...microseconds */
 
   /* Resolve addresses */
   rtp_delay_threshold = atoi(uaudio_get("rtp-delay-threshold", "1000"));
   /* ...microseconds */
 
   /* Resolve addresses */
-  res = get_address(&dst, &pref, &sockname);
-  if(!res) exit(-1);
-  if(src.n) {
-    sres = get_address(&src, &prefbind, &ssockname);
-    if(!sres) exit(-1);
+  res = netaddress_resolve(dst, 0, IPPROTO_UDP);
+  if(!res)
+    exit(-1);
+  if(src->af != -1) {
+    sres = netaddress_resolve(src, 1, IPPROTO_UDP);
+    if(!sres)
+      exit(-1);
   } else
     sres = 0;
   /* Create the socket */
   } else
     sres = 0;
   /* Create the socket */
@@ -209,7 +232,7 @@ static void rtp_open(void) {
       fatal(0, "unsupported address family %d", res->ai_family);
     }
     info("multicasting on %s TTL=%d loop=%s", 
       fatal(0, "unsupported address family %d", res->ai_family);
     }
     info("multicasting on %s TTL=%d loop=%s", 
-         sockname, ttl, loop ? "yes" : "no");
+         format_sockaddr(res->ai_addr), ttl, loop ? "yes" : "no");
   } else {
     struct ifaddrs *ifs;
 
   } else {
     struct ifaddrs *ifs;
 
@@ -228,9 +251,10 @@ static void rtp_open(void) {
     if(ifs) {
       if(setsockopt(rtp_fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
         fatal(errno, "error setting SO_BROADCAST on broadcast socket");
     if(ifs) {
       if(setsockopt(rtp_fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
         fatal(errno, "error setting SO_BROADCAST on broadcast socket");
-      info("broadcasting on %s (%s)", sockname, ifs->ifa_name);
+      info("broadcasting on %s (%s)", 
+           format_sockaddr(res->ai_addr), ifs->ifa_name);
     } else
     } else
-      info("unicasting on %s", sockname);
+      info("unicasting on %s", format_sockaddr(res->ai_addr));
   }
   /* Enlarge the socket buffer */
   len = sizeof sndbuf;
   }
   /* Enlarge the socket buffer */
   len = sizeof sndbuf;
@@ -250,9 +274,11 @@ static void rtp_open(void) {
   /* We might well want to set additional broadcast- or multicast-related
    * options here */
   if(sres && bind(rtp_fd, sres->ai_addr, sres->ai_addrlen) < 0)
   /* We might well want to set additional broadcast- or multicast-related
    * options here */
   if(sres && bind(rtp_fd, sres->ai_addr, sres->ai_addrlen) < 0)
-    fatal(errno, "error binding broadcast socket to %s", ssockname);
+    fatal(errno, "error binding broadcast socket to %s", 
+          format_sockaddr(sres->ai_addr));
   if(connect(rtp_fd, res->ai_addr, res->ai_addrlen) < 0)
   if(connect(rtp_fd, res->ai_addr, res->ai_addrlen) < 0)
-    fatal(errno, "error connecting broadcast socket to %s", sockname);
+    fatal(errno, "error connecting broadcast socket to %s", 
+          format_sockaddr(res->ai_addr));
 }
 
 static void rtp_start(uaudio_callback *callback,
 }
 
 static void rtp_start(uaudio_callback *callback,
@@ -303,15 +329,12 @@ static void rtp_deactivate(void) {
 static void rtp_configure(void) {
   char buffer[64];
 
 static void rtp_configure(void) {
   char buffer[64];
 
-  uaudio_set("rtp-destination", config->broadcast.s[0]);
-  uaudio_set("rtp-destination-port", config->broadcast.s[1]);
-  if(config->broadcast_from.n) {
-    uaudio_set("rtp-source", config->broadcast_from.s[0]);
-    uaudio_set("rtp-source-port", config->broadcast_from.s[1]);
-  } else {
-    uaudio_set("rtp-source", NULL);
-    uaudio_set("rtp-source-port", NULL);
-  }
+  rtp_set_netconfig("rtp-destination-af",
+                    "rtp-destination",
+                    "rtp-destination-port", &config->broadcast);
+  rtp_set_netconfig("rtp-source-af",
+                    "rtp-source",
+                    "rtp-source-port", &config->broadcast_from);
   snprintf(buffer, sizeof buffer, "%ld", config->multicast_ttl);
   uaudio_set("multicast-ttl", buffer);
   uaudio_set("multicast-loop", config->multicast_loop ? "yes" : "no");
   snprintf(buffer, sizeof buffer, "%ld", config->multicast_ttl);
   uaudio_set("multicast-ttl", buffer);
   uaudio_set("multicast-loop", config->multicast_loop ? "yes" : "no");
index 7f506ee348e60b05d31b88abfd5a18ed9bb87893..e793045ce77c995cb2f13ef52e4ae736e6168cdd 100644 (file)
@@ -24,6 +24,7 @@
 #include "uaudio.h"
 #include "hash.h"
 #include "mem.h"
 #include "uaudio.h"
 #include "hash.h"
 #include "mem.h"
+#include "log.h"
 
 /** @brief Options for chosen uaudio API */
 static hash *uaudio_options;
 
 /** @brief Options for chosen uaudio API */
 static hash *uaudio_options;
index 767fa31773ecb2bfeb39e3d5acd4e8ba7d10a1ac..ef03ceb2934b32d313198bfa2022de575b4f151b 100644 (file)
@@ -1105,9 +1105,12 @@ static int c_rtp_address(struct conn *c,
                         char attribute((unused)) **vec,
                         int attribute((unused)) nvec) {
   if(api == &uaudio_rtp) {
                         char attribute((unused)) **vec,
                         int attribute((unused)) nvec) {
   if(api == &uaudio_rtp) {
+    char **addr;
+
+    netaddress_format(&config->broadcast, NULL, &addr);
     sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
     sink_printf(ev_writer_sink(c->w), "252 %s %s\n",
-               quoteutf8(config->broadcast.s[0]),
-               quoteutf8(config->broadcast.s[1]));
+               quoteutf8(addr[1]),
+               quoteutf8(addr[2]));
   } else
     sink_writes(ev_writer_sink(c->w), "550 No RTP\n");
   return 1;
   } else
     sink_writes(ev_writer_sink(c->w), "550 No RTP\n");
   return 1;