*/
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);
}
}
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);
}
}
-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) {
{ 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 },
}
c->broadcast.af = -1;
c->broadcast_from.af = -1;
+ c->listen.af = -1;
return c;
}
long prefsync; /* preflog sync interval */
/** @brief Secondary listen address */
- struct stringlist listen;
+ struct netaddress listen;
/** @brief Alias format string */
const char *alias;
l->pf = pf;
if(ev_listen(ev, fd, listen_callback, l, "server listener"))
exit(EXIT_FAILURE);
+ info("listening on %s", name);
return fd;
}
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;
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");
}
/* 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;
}
}
}