From 89640f3f83c29967e524d754bf130bc452bdefe2 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Sat, 16 Sep 2017 19:21:28 +0100 Subject: [PATCH] server/: Use modern functions for address/text conversions. The tricky part is the change to the `p_init' function, which now takes a list of `addrinfo' structures and goes through the motions of matching the addresses up to `udpsock' slots, but actually everything is rigged so that there's only the IPv4 entry. --- mon/tripemon.in | 12 +++++--- server/admin.c | 49 +++++++++++++++++++++-------- server/peer.c | 95 ++++++++++++++++++++++++++++++++++++--------------------- server/tripe.c | 45 +++++++++++++-------------- server/tripe.h | 5 ++- 5 files changed, 127 insertions(+), 79 deletions(-) diff --git a/mon/tripemon.in b/mon/tripemon.in index 24709344..593b2e8b 100644 --- a/mon/tripemon.in +++ b/mon/tripemon.in @@ -324,12 +324,14 @@ class Peer (MonitorObject): def _setaddr(me, addr): """Set the peer's address.""" if addr[0] == 'INET': - ipaddr, port = addr[1:] + af, ipaddr, port = addr try: - name = S.gethostbyaddr(ipaddr)[0] - me.addr = 'INET %s:%s [%s]' % (name, port, ipaddr) - except S.herror: - me.addr = 'INET %s:%s' % (ipaddr, port) + name, _ = S.getnameinfo((ipaddr, int(port)), + S.NI_NUMERICSERV | S.NI_NAMEREQD) + except S.gaierror: + me.addr = '%s %s:%s' % (af, ipaddr, port) + else: + me.addr = '%s %s:%s [%s]' % (af, name, port, ipaddr) else: me.addr = ' '.join(addr) diff --git a/server/admin.c b/server/admin.c index 72f42f29..44222eb8 100644 --- a/server/admin.c +++ b/server/admin.c @@ -272,14 +272,19 @@ void a_vformat(dstr *d, const char *fmt, va_list *ap) } else if (*fmt == '?') { if (strcmp(fmt, "?ADDR") == 0) { const addr *a = va_arg(*ap, const addr *); - switch (a->sa.sa_family) { - case AF_INET: - u_quotify(d, "INET"); - u_quotify(d, inet_ntoa(a->sin.sin_addr)); - dstr_putf(d, " %u", (unsigned)ntohs(a->sin.sin_port)); - break; - default: - abort(); + char name[NI_MAXHOST], serv[NI_MAXSERV]; + int ix, err; + if ((err = getnameinfo(&a->sa, addrsz(a), + name, sizeof(name), serv, sizeof(serv), + (NI_NUMERICHOST | NI_NUMERICSERV | + NI_DGRAM)))) { + dstr_putf(d, " E%d", err); + u_quotify(d, gai_strerror(err)); + } else { + ix = afix(a->sa.sa_family); assert(ix >= 0); + u_quotify(d, aftab[ix].name); + u_quotify(d, name); + u_quotify(d, serv); } } else if (strcmp(fmt, "?B64") == 0) { const octet *p = va_arg(*ap, const octet *); @@ -1104,6 +1109,7 @@ static void a_resolve(admin *a, admin_resop *r, const char *tag, const char *fam = "ANY"; char *p; int i = 0, j; + struct addrinfo *ai, *ailist, aihint = { 0 }; /* --- Fill in the easy bits of address --- */ @@ -1168,11 +1174,28 @@ static void a_resolve(admin *a, admin_resop *r, const char *tag, /* --- If the name is numeric, do it the easy way --- */ - if (inet_aton(av[i], &r->sa.sin.sin_addr)) { - T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); ) - r->sa.sin.sin_family = AF_INET; - setport(&r->sa, r->port); - func(r, ARES_OK); + aihint.ai_family = AF_INET; + aihint.ai_socktype = SOCK_DGRAM; + aihint.ai_protocol = IPPROTO_UDP; + aihint.ai_flags = AI_NUMERICHOST; + if (!getaddrinfo(av[i], 0, &aihint, &ailist)) { + for (ai = ailist; ai; ai = ai->ai_next) { + if ((j = afix(ai->ai_family)) >= 0 && udpsock[j].fd >= 0) + break; + } + if (!ai) { + T( trace(T_ADMIN, "admin: resop %s failed: " + "no suitable addresses returned", BGTAG(r)); ) + a_bgfail(&r->bg, "resolve-error", "%s" , r->addr, A_END); + func(r, ARES_FAIL); + } else { + T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); ) + assert(ai->ai_addrlen <= sizeof(r->sa)); + memcpy(&r->sa, ai->ai_addr, ai->ai_addrlen); + setport(&r->sa, r->port); + func(r, ARES_OK); + } + freeaddrinfo(ailist); xfree(r->addr); a_bgrelease(&r->bg); return; diff --git a/server/peer.c b/server/peer.c index ea6e09bb..34bfb6c4 100644 --- a/server/peer.c +++ b/server/peer.c @@ -336,6 +336,10 @@ static void p_read(int fd, unsigned mode, void *v) ssize_t n; int ch; buf b, bb; +#ifndef NTRACE + int ix = -1; + char name[NI_MAXHOST], svc[NI_MAXSERV]; +#endif /* --- Read the data --- */ @@ -346,14 +350,18 @@ static void p_read(int fd, unsigned mode, void *v) a_warn("PEER", "-", "socket-read-error", "?ERRNO", A_END); return; } + IF_TRACING(T_PEER, { + ix = afix(a.sa.sa_family); + getnameinfo(&a.sa, sz, name, sizeof(name), svc, sizeof(svc), + NI_NUMERICHOST | NI_NUMERICSERV); + }) /* --- If the packet is a greeting, don't check peers --- */ if (n && buf_i[0] == (MSG_MISC | MISC_GREET)) { IF_TRACING(T_PEER, { - trace(T_PEER, "peer: greeting received from INET %s %u", - inet_ntoa(a.sin.sin_addr), - (unsigned)ntohs(a.sin.sin_port)); + trace(T_PEER, "peer: greeting received from %s %s %s", + aftab[ix].name, name, svc); trace_block(T_PACKET, "peer: greeting contents", buf_i, n); }) buf_init(&b, buf_i, n); @@ -379,11 +387,11 @@ static void p_read(int fd, unsigned mode, void *v) IF_TRACING(T_PEER, { if (p) { trace(T_PEER, - "peer: packet received from `%s' from address INET %s %d", - p_name(p), inet_ntoa(a.sin.sin_addr), ntohs(a.sin.sin_port)); + "peer: packet received from `%s' from address %s %s %s", + p_name(p), aftab[ix].name, name, svc); } else { - trace(T_PEER, "peer: packet received from unknown address INET %s %d", - inet_ntoa(a.sin.sin_addr), ntohs(a.sin.sin_port)); + trace(T_PEER, "peer: packet received from unknown address %s %s %s", + aftab[ix].name, name, svc); } trace_block(T_PACKET, "peer: packet contents", buf_i, n); }) @@ -772,45 +780,62 @@ const addr *p_addr(peer *p) { return (&p->spec.sa); } /* --- @p_init@ --- * * - * Arguments: @struct in_addr addr@ = address to bind to - * @unsigned port@ = port number to listen to + * Arguments: @struct addrinfo *ailist@ = addresses to bind to * * Returns: --- * * Use: Initializes the peer system; creates the socket. */ -void p_init(struct in_addr addr, unsigned port) +void p_init(struct addrinfo *ailist) { int fd; - struct sockaddr_in sin; int len = PKBUFSZ; + int i; + struct addrinfo *ai; + unsigned port, lastport = 0; + addr a; + socklen_t sz; - /* --- Note on socket buffer sizes --- * - * - * For some bizarre reason, Linux 2.2 (at least) doubles the socket buffer - * sizes I pass to @setsockopt@. I'm not putting special-case code here - * for Linux: BSD (at least TCPv2) does what I tell it rather than second- - * guessing me. - */ - - if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) - die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno)); - BURN(sin); - sin.sin_family = AF_INET; - sin.sin_addr = addr; - sin.sin_port = htons(port); - if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) - die(EXIT_FAILURE, "bind failed: %s", strerror(errno)); - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) || - setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len))) { - die(EXIT_FAILURE, "failed to set socket buffer sizes: %s", - strerror(errno)); + for (i = 0; i < NADDRFAM; i++) udpsock[i].fd = -1; + + for (ai = ailist; ai; ai = ai->ai_next) { + if ((i = afix(ai->ai_family)) < 0) continue; + if (udpsock[i].fd != -1) continue; + + /* --- Note on socket buffer sizes --- * + * + * For some bizarre reason, Linux 2.2 (at least) doubles the socket + * buffer sizes I pass to @setsockopt@. I'm not putting special-case + * code here for Linux: BSD (at least TCPv2) does what I tell it rather + * than second-guessing me. + */ + + if ((fd = socket(ai->ai_family, SOCK_DGRAM, 0)) < 0) + die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno)); + assert(ai->ai_addrlen <= sizeof(a)); + memcpy(&a, ai->ai_addr, ai->ai_addrlen); + if ((port = getport(&a)) == 0 && lastport) setport(&a, lastport); + if (bind(fd, &a.sa, addrsz(&a))) + die(EXIT_FAILURE, "bind failed: %s", strerror(errno)); + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) || + setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len))) { + die(EXIT_FAILURE, "failed to set socket buffer sizes: %s", + strerror(errno)); + } + fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); + sel_initfile(&sel, &udpsock[i], fd, SEL_READ, p_read, 0); + sel_addfile(&udpsock[i]); + T( trace(T_PEER, "peer: created %s socket", aftab[i].name); ) + if (!port) { + sz = sizeof(a); + if (getsockname(fd, &a.sa, &sz)) { + die(EXIT_FAILURE, "failed to read local socket address: %s", + strerror(errno)); + } + lastport = getport(&a); + } } - fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); - sel_initfile(&sel, &udpsock[AFIX_INET], fd, SEL_READ, p_read, 0); - sel_addfile(&udpsock[AFIX_INET]); - T( trace(T_PEER, "peer: created socket"); ) sym_create(&byname); am_create(&byaddr); diff --git a/server/tripe.c b/server/tripe.c index 58bf8a18..d8a6cc13 100644 --- a/server/tripe.c +++ b/server/tripe.c @@ -119,11 +119,11 @@ int main(int argc, char *argv[]) int csockmode = 0600; const char *dir = CONFIGDIR; const char *p; - unsigned port = TRIPE_PORT; - struct in_addr baddr = { INADDR_ANY }; + const char *bindhost = 0, *bindsvc = STR(TRIPE_PORT); + struct addrinfo aihint = { 0 }, *ailist; unsigned f = 0; int i; - int selerr = 0; + int err, selerr = 0; unsigned af; struct timeval tv; uid_t u = -1; @@ -141,6 +141,7 @@ int main(int argc, char *argv[]) if ((p = getenv("TRIPESOCK")) != 0) csock = p; tun_default = tunnels[0]; + aihint.ai_family = AF_INET; for (;;) { static const struct option opts[] = { @@ -199,25 +200,12 @@ int main(int argc, char *argv[]) f |= f_foreground; break; - case 'b': { - struct hostent *h = gethostbyname(optarg); - if (!h) - die(EXIT_FAILURE, "unknown host name `%s'", optarg); - memcpy(&baddr, h->h_addr, sizeof(struct in_addr)); - } break; - case 'p': { - char *p; - unsigned long i = strtoul(optarg, &p, 0); - if (*p) { - struct servent *s = getservbyname(optarg, "udp"); - if (!s) - die(EXIT_FAILURE, "unknown service name `%s'", optarg); - i = ntohs(s->s_port); - } - if (i >= 65536) - die(EXIT_FAILURE, "bad port number %lu", i); - port = i; - } break; + case 'b': + bindhost = optarg; + break; + case 'p': + bindsvc = optarg; + break; case 'n': { int i; for (i = 0;; i++) { @@ -273,6 +261,17 @@ int main(int argc, char *argv[]) if (!(~f & (f_daemon | f_foreground))) die(EXIT_FAILURE, "foreground operation for a daemon is silly"); + aihint.ai_protocol = IPPROTO_UDP; + aihint.ai_socktype = SOCK_DGRAM; + aihint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + if ((err = getaddrinfo(bindhost, bindsvc, &aihint, &ailist)) != 0) { + die(EXIT_FAILURE, "couldn't resolve hostname %c%s%c, port `%s': %s", + bindhost ? '`' : '<', + bindhost ? bindhost : "nil", + bindhost ? '\'' : '>', + bindsvc, gai_strerror(err)); + } + if (chdir(dir)) { die(EXIT_FAILURE, "can't set current directory to `%s': %s", dir, strerror(errno)); @@ -285,7 +284,7 @@ int main(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); for (i = 0; tunnels[i]; i++) tunnels[i]->init(); - p_init(baddr, port); + p_init(ailist); freeaddrinfo(ailist); if (!(f & f_daemon)) { af = AF_WARN; #ifndef NTRACE diff --git a/server/tripe.h b/server/tripe.h index dd8205be..fc866b8a 100644 --- a/server/tripe.h +++ b/server/tripe.h @@ -1585,15 +1585,14 @@ extern const addr *p_addr(peer */*p*/); /* --- @p_init@ --- * * - * Arguments: @struct in_addr addr@ = address to bind to - * @unsigned port@ = port number to listen to + * Arguments: @struct addrinfo *ailist@ = addresses to bind to * * Returns: --- * * Use: Initializes the peer system; creates the socket. */ -extern void p_init(struct in_addr /*addr*/, unsigned /*port*/); +extern void p_init(struct addrinfo */*ailist*/); /* --- @p_port@ --- * * -- 2.11.0