From 76e72f65da97e7482da0a1eb0b110ca323f21643 Mon Sep 17 00:00:00 2001 Message-Id: <76e72f65da97e7482da0a1eb0b110ca323f21643.1716341367.git.mdw@distorted.org.uk> From: Mark Wooding Date: Sat, 14 Mar 2009 14:56:56 +0000 Subject: [PATCH] Start rationalizing network address configuration. Organization: Straylight/Edgeware From: Richard Kettlewell 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 | 139 ++++++++++++++++++++++++++++++++++++++++++++ lib/addr.h | 28 ++++++++- lib/configuration.c | 46 +++++++++++++-- lib/configuration.h | 5 +- lib/uaudio-rtp.c | 125 +++++++++++++++++++++++---------------- lib/uaudio.c | 1 + server/server.c | 7 ++- 7 files changed, 291 insertions(+), 60 deletions(-) diff --git a/lib/addr.c b/lib/addr.c index fecc28c..8082cad 100644 --- a/lib/addr.c +++ b/lib/addr.c @@ -30,6 +30,9 @@ #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 @@ -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 diff --git a/lib/addr.h b/lib/addr.h index 973733f..fb70068 100644 --- a/lib/addr.h +++ b/lib/addr.h @@ -23,7 +23,23 @@ #include -#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, @@ -35,6 +51,16 @@ int addrinfocmp(const struct addrinfo *a, 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 */ /* diff --git a/lib/configuration.c b/lib/configuration.c index 67d50a0..8ec79c7 100644 --- a/lib/configuration.c +++ b/lib/configuration.c @@ -489,6 +489,18 @@ static int set_rights(const struct config_state *cs, 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, @@ -572,6 +584,13 @@ static void free_transformlist(struct config *c, 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 @@ -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_netaddress = { set_netaddress, free_netaddress }, 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; } +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 */ @@ -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(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 }, @@ -1202,6 +1238,8 @@ static struct config *config_default(void) { default_players[n], "disorder-tracklength", (char *)0)) exit(1); } + c->broadcast.af = -1; + c->broadcast_from.af = -1; return c; } @@ -1273,7 +1311,7 @@ static void config_postdefaults(struct config *c, 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); @@ -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((!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 */ diff --git a/lib/configuration.h b/lib/configuration.h index 9a85e5d..7542902 100644 --- a/lib/configuration.h +++ b/lib/configuration.h @@ -26,6 +26,7 @@ #include "speaker-protocol.h" #include "rights.h" +#include "addr.h" struct uaudio; @@ -233,10 +234,10 @@ struct config { 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 */ - struct stringlist broadcast_from; + struct netaddress broadcast_from; /** @brief RTP delay threshold */ long rtp_delay_threshold; diff --git a/lib/uaudio-rtp.c b/lib/uaudio-rtp.c index 01be09b..bb03be4 100644 --- a/lib/uaudio-rtp.c +++ b/lib/uaudio-rtp.c @@ -85,6 +85,50 @@ static const char *const rtp_options[] = { 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]; @@ -129,52 +173,31 @@ static size_t rtp_play(void *buffer, size_t nsamples) { 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; - char *sockname, *ssockname; - struct stringlist dst, src; + struct netaddress dst[1], src[1]; /* 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 */ - 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 */ @@ -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", - sockname, ttl, loop ? "yes" : "no"); + format_sockaddr(res->ai_addr), ttl, loop ? "yes" : "no"); } 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"); - info("broadcasting on %s (%s)", sockname, ifs->ifa_name); + info("broadcasting on %s (%s)", + format_sockaddr(res->ai_addr), ifs->ifa_name); } else - info("unicasting on %s", sockname); + info("unicasting on %s", format_sockaddr(res->ai_addr)); } /* 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) - 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) - 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, @@ -303,15 +329,12 @@ static void rtp_deactivate(void) { 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"); diff --git a/lib/uaudio.c b/lib/uaudio.c index 7f506ee..e793045 100644 --- a/lib/uaudio.c +++ b/lib/uaudio.c @@ -24,6 +24,7 @@ #include "uaudio.h" #include "hash.h" #include "mem.h" +#include "log.h" /** @brief Options for chosen uaudio API */ static hash *uaudio_options; diff --git a/server/server.c b/server/server.c index 767fa31..ef03ceb 100644 --- a/server/server.c +++ b/server/server.c @@ -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 **addr; + + netaddress_format(&config->broadcast, NULL, &addr); 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; -- [mdw]