From 169f1a6f188cf0f088c29f5da19aa04efc19b564 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 4 May 2020 18:28:39 +0100 Subject: [PATCH] clients/playrtp.c: Fix the RTP-stream request syntax. Previously, the `-' syntax was an undocumented (and booby-trapped) way to inform the rest of the program that it should request a unicast stream. If we see this, then we apply some heuristics to select an interface to bind to, and ask the server to hurl packets at it. This is unsatisfactory. One might want to set a particular port, say because other ports are firewalled off by default. And one might want to set a particular address if the heuristics go wrong. As a first step, overhaul the command-line parser to accept an address and port name/number. We still apply the old heuristics if no address is provided explicitly, and we get the kernel to select a port number if none is given. This change is rather larger than I usually like, but there doesn't seem to be a useful intermediate state. Sorry. --- clients/playrtp.c | 95 +++++++++++++++++++++++++++++++++++------------ doc/disorder-playrtp.1.in | 11 ++++++ 2 files changed, 83 insertions(+), 23 deletions(-) diff --git a/clients/playrtp.c b/clients/playrtp.c index eda7ce9..561ef99 100644 --- a/clients/playrtp.c +++ b/clients/playrtp.c @@ -782,8 +782,7 @@ int main(int argc, char **argv) { sl.s[0] = address; sl.s[1] = port; break; - case 1: - case 2: + case 1: case 2: case 3: /* Use command-line ADDRESS+PORT or just PORT */ sl.n = argc; sl.s = argv; @@ -796,35 +795,84 @@ int main(int argc, char **argv) { struct sockaddr *addr; socklen_t addr_len; if(!strcmp(sl.s[0], "-")) { + /* Syntax: - [[ADDRESS] PORT]. Here, the PORT may be `-' to get the local + * kernel to choose. The ADDRESS may be omitted or `-' to pick something + * suitable. */ + const char *node, *svc; + struct sockaddr *sa = 0; + switch (sl.n) { +#define NULLDASH(s) (strcmp((s), "-") ? (s) : 0) + case 1: node = 0; svc = 0; break; + case 2: node = 0; svc = NULLDASH(sl.s[1]); break; + case 3: node = NULLDASH(sl.s[1]); svc = NULLDASH(sl.s[2]); break; + default: disorder_fatal(0, "too many listening-address compoennts"); +#undef NULLDASH + } /* We'll need a connection to request the incoming stream, so open one if * we don't have one already */ if(!c) { if(!(c = disorder_new(1))) exit(EXIT_FAILURE); if(disorder_connect(c)) exit(EXIT_FAILURE); } - /* Pick address family to match known-working connectivity to the server */ - int family = disorder_client_af(c); - /* Get a list of interfaces */ - struct ifaddrs *ifa, *bestifa = NULL; - if(getifaddrs(&ifa) < 0) - disorder_fatal(errno, "error calling getifaddrs"); - /* Try to pick a good one */ - for(; ifa; ifa = ifa->ifa_next) { - if(!ifa->ifa_addr) continue; - if(bestifa == NULL - || compare_interfaces(ifa, bestifa, family) > 0) - bestifa = ifa; + /* If no address was given, pick something sensible based on the known- + * working connectivity to the server */ + if(!node) { + int family = disorder_client_af(c); + /* Get a list of interfaces */ + struct ifaddrs *ifa, *bestifa = NULL; + if(getifaddrs(&ifa) < 0) + disorder_fatal(errno, "error calling getifaddrs"); + /* Try to pick a good one */ + for(; ifa; ifa = ifa->ifa_next) { + if(!ifa->ifa_addr) continue; + if(bestifa == NULL + || compare_interfaces(ifa, bestifa, family) > 0) + bestifa = ifa; + } + if(!bestifa) + disorder_fatal(0, "failed to select a network interface"); + sa = bestifa->ifa_addr; + switch(sa->sa_family) { + case AF_INET: ((struct sockaddr_in *)sa)->sin_port = 0; break; + case AF_INET6: ((struct sockaddr_in6 *)sa)->sin6_port = 0; break; + default: assert(!"unexpected address family"); + } + prefs.ai_family = sa->sa_family; + } + /* If we have an address or port to resolve then do that now */ + if (node || svc) { + struct addrinfo *ai; + char errbuf[1024]; + int rc; + if((rc = getaddrinfo(node, svc, &prefs, &ai))) + disorder_fatal(0, "failed to resolve address `%s' and service `%s': %s", + node ? node : "-", svc ? svc : "-", + format_error(ec_getaddrinfo, rc, + errbuf, sizeof(errbuf))); + if(!sa) + sa = ai->ai_addr; + else { + assert(sa->sa_family == ai->ai_addr->sa_family); + switch(sa->sa_family) { + case AF_INET: + ((struct sockaddr_in *)sa)->sin_port = + ((struct sockaddr_in *)ai->ai_addr)->sin_port; + break; + case AF_INET6: + ((struct sockaddr_in6 *)sa)->sin6_port = + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port; + break; + default: + assert(!"unexpected address family"); + } + } } - if(!bestifa) - disorder_fatal(0, "failed to select a network interface"); - family = bestifa->ifa_addr->sa_family; - if((rtpfd = socket(family, - SOCK_DGRAM, - IPPROTO_UDP)) < 0) - disorder_fatal(errno, "error creating socket (family %d)", family); + if((rtpfd = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) + disorder_fatal(errno, "error creating socket (family %d)", + sa->sa_family); /* Bind the address */ - if(bind(rtpfd, bestifa->ifa_addr, - family == AF_INET + if(bind(rtpfd, sa, + sa->sa_family == AF_INET ? sizeof (struct sockaddr_in) : sizeof (struct sockaddr_in6)) < 0) disorder_fatal(errno, "error binding socket"); static struct sockaddr_storage bound_address; @@ -844,6 +892,7 @@ int main(int argc, char **argv) { /* Report what we did */ disorder_info("listening on %s", format_sockaddr(addr)); } else { + if(sl.n > 2) disorder_fatal(0, "too many address components"); /* Look up address and port */ if(!(res = get_address(&sl, &prefs, &sockname))) exit(1); diff --git a/doc/disorder-playrtp.1.in b/doc/disorder-playrtp.1.in index 0df5389..5f4fa3f 100644 --- a/doc/disorder-playrtp.1.in +++ b/doc/disorder-playrtp.1.in @@ -36,6 +36,17 @@ broadcast to that port. .PP If an address and a port are specified then the RTP stream is assumed to be multicast to that group address and port. +.PP +Alternatively, the +.I ADDRESS +can start with a +.RB ` \- ', +in which case +.B disorder-playrtp +will request a dedicated unicast stream from the server. The +.RB ` \- ' +may be followed by an optional port, or address/port pair, which will be the +local address/port to bind to and announce to the server. .SH OPTIONS .TP .B \-\-api\fR, -\fB-A\fR \fIAPI\fR -- 2.11.0