/** @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
+ * @param type Socket type (e.g., @c SOCK_STREAM, @c SOCK_DGRAM)
+ * @param[out] raddr_out Vector of address/length pairs
+ * @param[out] nraddr_out Length of output vector
+ * @return Zero on success, -1 on error
*
- * Free the address using netaddress_freeaddrinfo() because it might not
- * have come from getaddrinfo() directly.
+ * Call netaddress_free_resolved() to free the resolved addresses.
*/
-struct addrinfo *netaddress_resolve(const struct netaddress *na,
- int passive,
- int protocol) {
- struct addrinfo *res, hints[1];
+int netaddress_resolve(const struct netaddress *na, int passive, int type,
+ struct resolved **raddr_out, size_t *nraddr_out) {
+ struct resolved *raddr;
+ size_t i, nraddr;
+ struct addrinfo *res, *ai, hints[1];
struct sockaddr_un *sun;
char service[64];
int rc;
#if HAVE_SYS_UN_H
if (na->af == AF_UNIX) {
/* `getaddrinfo' won't work, so we make our own one */
- res = xmalloc(sizeof(*res));
- res->ai_flags = 0;
- res->ai_family = AF_UNIX;
- res->ai_socktype = (protocol == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
- res->ai_protocol = 0;
- res->ai_addrlen = offsetof(struct sockaddr_un, sun_path) +
+ raddr = xmalloc(sizeof(*raddr));
+ raddr->len = offsetof(struct sockaddr_un, sun_path) +
strlen(na->address) + 1;
- sun = xmalloc(res->ai_addrlen);
+ sun = xmalloc_noptr(raddr->len);
sun->sun_family = AF_UNIX;
strcpy(sun->sun_path, na->address);
- res->ai_addr = (struct sockaddr *)sun;
- res->ai_canonname = sun->sun_path;
- res->ai_next = 0;
+ raddr->sa = (struct sockaddr *)sun;
+ nraddr = 1;
} else
#endif
{
/* get the system to do the heavy lifting */
memset(hints, 0, sizeof hints);
hints->ai_family = na->af;
- hints->ai_protocol = protocol;
+ hints->ai_socktype = type;
hints->ai_flags = passive ? AI_PASSIVE : 0;
byte_snprintf(service, sizeof service, "%d", na->port);
rc = getaddrinfo(na->address, service, hints, &res);
na->address ? na->address : "*",
na->port,
format_error(ec_getaddrinfo, rc, errbuf, sizeof errbuf));
- return NULL;
+ return -1;
+ }
+ /* copy the addresses into an output vector */
+ for(ai = res, nraddr = 0; ai; ai = ai->ai_next, nraddr++);
+ raddr = xmalloc(nraddr*sizeof(*raddr));
+ for(ai = res, i = 0; ai; ai = ai->ai_next, i++) {
+ raddr[i].sa = xmalloc_noptr(ai->ai_addrlen);
+ raddr[i].len = ai->ai_addrlen;
+ memcpy(raddr[i].sa, ai->ai_addr, ai->ai_addrlen);
}
- assert(res->ai_family != AF_UNIX);
+ freeaddrinfo(res);
}
- return res;
+ *raddr_out = raddr;
+ *nraddr_out = nraddr;
+ return 0;
}
-/** @brief Free an address-info list from netaddress_resovle()
- * @param res Address-info list
- */
-void netaddress_freeaddrinfo(struct addrinfo *res) {
-#if HAVE_SYS_UN_H
- if(res->ai_family == AF_UNIX) {
- xfree(res->ai_addr);
- xfree(res);
- } else
-#endif
- freeaddrinfo(res);
+/** @brief Free a vector of resolved addresses */
+void netaddress_free_resolved(struct resolved *raddr, size_t nraddr)
+{
+ size_t i;
+
+ if (!raddr)
+ return;
+ for(i = 0; i < nraddr; i++)
+ xfree(raddr[i].sa);
+ xfree(raddr);
}
/*
int port;
};
+/** @brief A socket address and length */
+struct resolved {
+
+ /** @brief Pointer to the address */
+ struct sockaddr *sa;
+
+ /** @brief Length of the address */
+ socklen_t len;
+};
+
struct addrinfo *get_address(const struct stringlist *a,
const struct addrinfo *pref,
char **namep)
void netaddress_format(const struct netaddress *na,
int *nvecp,
char ***vecp) attribute((nonnull (1)));
-struct addrinfo *netaddress_resolve(const struct netaddress *na,
- int passive,
- int protocol) attribute((nonnull (1)));
-void netaddress_freeaddrinfo(struct addrinfo *res) attribute((nonnull (1)));
+int netaddress_resolve(const struct netaddress *na, int passive, int type,
+ struct resolved **raddr_out, size_t *nraddr_out)
+ attribute((nonnull (1, 4, 5)));
+void netaddress_free_resolved(struct resolved *raddr, size_t nraddr);
#endif /* ADDR_H */
#if !_WIN32
struct sockaddr_un su;
#endif
- struct addrinfo *res = 0;
+ struct resolved *res;
+ size_t nres;
char *name = NULL;
socklen_t len;
if(c->connect.af != -1) {
- res = netaddress_resolve(&c->connect, 0, IPPROTO_TCP);
- if(!res)
+ if(netaddress_resolve(&c->connect, 0, SOCK_STREAM, &res, &nres))
return -1;
- sa = res->ai_addr;
- len = res->ai_addrlen;
+ sa = res->sa;
+ len = res->len;
} else {
#if _WIN32
disorder_fatal(0, "local connections are not supported on Windows");
strcpy(su.sun_path, name);
sa = (struct sockaddr *)&su;
len = sizeof su;
+ res = 0;
xfree(name);
#endif
}
if(namep)
*namep = format_sockaddr(sa);
if(res)
- netaddress_freeaddrinfo(res);
+ netaddress_free_resolved(res, nres);
return len;
}
}
static void rtp_open(void) {
- struct addrinfo *dres, *sres;
+ struct resolved *dres, *sres;
+ size_t ndres, nsres;
static const int one = 1;
struct netaddress dst[1], src[1];
const char *mode;
"rtp-source-port",
src);
if(dst->af != -1) {
- dres = netaddress_resolve(dst, 0, IPPROTO_UDP);
- if(!dres)
+ if(netaddress_resolve(dst, 0, SOCK_DGRAM, &dres, &ndres))
exit(-1);
- } else
+ } else {
dres = 0;
+ ndres = 0;
+ }
if(src->af != -1) {
- sres = netaddress_resolve(src, 1, IPPROTO_UDP);
- if(!sres)
+ if(netaddress_resolve(src, 0, SOCK_DGRAM, &sres, &nsres))
exit(-1);
- } else
+ } else {
sres = 0;
+ nsres = 0;
+ }
/* _AUTO inspects the destination address and acts accordingly */
if(rtp_mode == RTP_AUTO) {
if(!dres)
rtp_mode = RTP_REQUEST;
- else if(multicast(dres->ai_addr))
+ else if(multicast(dres->sa))
rtp_mode = RTP_MULTICAST;
else {
struct ifaddrs *ifs;
* for he same interface which _does_ have ifa_broadaddr though... */
if((ifs->ifa_flags & IFF_BROADCAST)
&& ifs->ifa_broadaddr
- && sockaddr_equal(ifs->ifa_broadaddr, dres->ai_addr))
+ && sockaddr_equal(ifs->ifa_broadaddr, dres->sa))
break;
ifs = ifs->ifa_next;
}
rtp_max_payload = 1500 - 8/*UDP*/ - 40/*IP*/ - 8/*conservatism*/;
/* Create the sockets */
if(rtp_mode != RTP_REQUEST) {
- if((rtp_fd = socket(dres->ai_family,
- dres->ai_socktype,
- dres->ai_protocol)) < 0)
+ if((rtp_fd = socket(dres->sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
disorder_fatal(errno, "error creating RTP transmission socket");
}
if((rtp_fd4 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
/* Enable multicast options */
const int ttl = atoi(uaudio_get("multicast-ttl", "1"));
const int loop = !strcmp(uaudio_get("multicast-loop", "yes"), "yes");
- switch(dres->ai_family) {
+ switch(dres->sa->sa_family) {
case PF_INET: {
if(setsockopt(rtp_fd, IPPROTO_IP, IP_MULTICAST_TTL,
&ttl, sizeof ttl) < 0)
break;
}
default:
- disorder_fatal(0, "unsupported address family %d", dres->ai_family);
+ disorder_fatal(0, "unsupported address family %d", dres->sa->sa_family);
}
disorder_info("multicasting on %s TTL=%d loop=%s",
- format_sockaddr(dres->ai_addr), ttl, loop ? "yes" : "no");
+ format_sockaddr(dres->sa), ttl, loop ? "yes" : "no");
break;
}
case RTP_UNICAST: {
- disorder_info("unicasting on %s", format_sockaddr(dres->ai_addr));
+ disorder_info("unicasting on %s", format_sockaddr(dres->sa));
break;
}
case RTP_BROADCAST: {
if(setsockopt(rtp_fd, SOL_SOCKET, SO_BROADCAST, &one, sizeof one) < 0)
disorder_fatal(errno, "error setting SO_BROADCAST on broadcast socket");
disorder_info("broadcasting on %s",
- format_sockaddr(dres->ai_addr));
+ format_sockaddr(dres->sa));
break;
}
case RTP_REQUEST: {
/* We might well want to set additional broadcast- or multicast-related
* options here */
if(rtp_mode != RTP_REQUEST) {
- if(sres && bind(rtp_fd, sres->ai_addr, sres->ai_addrlen) < 0)
+ if(sres && bind(rtp_fd, sres->sa, sres->len) < 0)
disorder_fatal(errno, "error binding broadcast socket to %s",
- format_sockaddr(sres->ai_addr));
- if(connect(rtp_fd, dres->ai_addr, dres->ai_addrlen) < 0)
+ format_sockaddr(sres->sa));
+ if(connect(rtp_fd, dres->sa, dres->len) < 0)
disorder_fatal(errno, "error connecting broadcast socket to %s",
- format_sockaddr(dres->ai_addr));
+ format_sockaddr(dres->sa));
}
#ifdef IP_MTU_DISCOVER
mtu_disc = uaudio_get("rtp-mtu-discovery", "default");
else break;
if(setsockopt(rtp_fd4, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof opt))
disorder_fatal(errno, "error setting MTU discovery");
- if(sres->ai_family == AF_INET &&
+ if(sres->sa->sa_family == AF_INET &&
setsockopt(rtp_fd, IPPROTO_IP, IP_MTU_DISCOVER, &opt, sizeof opt))
disorder_fatal(errno, "error setting MTU discovery");
} while (0);
}
/** @brief Create a copy of an @c addrinfo structure */
-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);
+static struct sockaddr *copy_sockaddr(const struct resolved *raddr) {
+ struct sockaddr *sa = xmalloc_noptr(raddr->len);
+ memcpy(sa, raddr->sa, raddr->len);
return sa;
}
/** @brief Create and destroy sockets to set current configuration */
void reset_sockets(ev_source *ev) {
- struct addrinfo *res, *r;
+ struct resolved *res;
+ size_t i, nres;
struct listener *l, **ll;
const char *private_dir = config_get_file("private");
reset_unix_socket(ev, priv_socket, config_get_file("private/socket"), 1);
/* get the new listen config */
- if(config->listen.af != -1)
- res = netaddress_resolve(&config->listen, 1, IPPROTO_TCP);
- else
- res = 0;
+ res = 0; nres = 0;
+ if(config->listen.af != -1)
+ netaddress_resolve(&config->listen, 1, SOCK_STREAM, &res, &nres);
/* 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))
+ for(i = 0; i < nres; i++)
+ if(!sockaddrcmp(res[i].sa, l->sa))
break;
- if(!r) {
+ if(i >= nres) {
/* Didn't find a match, remove this one */
server_stop(ev, l->fd);
*ll = l->next;
}
/* Open any new listeners that are required */
- for(r = res; r; r = r->ai_next) {
+ for(i = 0; i < nres; i++) {
for(l = listeners; l; l = l->next)
- if(!sockaddrcmp(r->ai_addr, l->sa))
+ if(!sockaddrcmp(res[i].sa, 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), 0);
+ int fd = server_start(ev, res[i].sa->sa_family, res[i].len, res[i].sa,
+ format_sockaddr(res[i].sa), 0);
if(fd >= 0) {
l = xmalloc(sizeof *l);
l->next = listeners;
- l->sa = copy_sockaddr(r);
+ l->sa = copy_sockaddr(&res[i]);
l->fd = fd;
listeners = l;
}
}
/* if res is still set it needs freeing */
if(res)
- netaddress_freeaddrinfo(res);
+ netaddress_free_resolved(res, nres);
}
/** @brief Reconfigure the server