From: Mark Wooding Date: Thu, 28 Sep 2017 00:41:43 +0000 (+0100) Subject: pkstream/pkstream.c: Use `getaddrinfo' to resolve addresses and services. X-Git-Tag: 1.5.0~41^2~48 X-Git-Url: https://git.distorted.org.uk/~mdw/tripe/commitdiff_plain/0bf142d2dc68ff6dc43c329332ec09ac28166905 pkstream/pkstream.c: Use `getaddrinfo' to resolve addresses and services. This will give us multiple addresses for simple queries, which we must do something sensible with: * for server bind and peer addresses, we accumulate them in our address vectors as before; * for client bind addresses, and local UDP addresses, we just take the first match, and hope that's good enough; and * for client connect addresses, and remote UDP addresses, we try to connect to each address in turn and take the first one that works. Some utility functions have been added or enhanced: * `pushaddr' has become `pushaddrs', and its job is now to push the addresses from a `struct addrinfo' chain onto an address vector; and * `copyaddr' has been introduced to do possible partial copies of addresses. Note that everything is still strictly IPv4 throughout. But almost all of the pieces are now in place... --- diff --git a/pkstream/pkstream.c b/pkstream/pkstream.c index 3a5f3fde..580bb73f 100644 --- a/pkstream/pkstream.c +++ b/pkstream/pkstream.c @@ -139,6 +139,24 @@ static void initaddr(addr *a) a->sin.sin_port = 0; } +#define caf_addr 1u +#define caf_port 2u +static void copyaddr(addr *a, const struct sockaddr *sa, unsigned f) +{ + const struct sockaddr_in *sin; + + a->sa.sa_family = sa->sa_family; + switch (sa->sa_family) { + case AF_INET: + sin = (const struct sockaddr_in *)sa; + if (f&caf_addr) a->sin.sin_addr = sin->sin_addr; + if (f&caf_port) a->sin.sin_port = sin->sin_port; + break; + default: + abort(); + } +} + static void dolisten(void); static void doclose(pkstream *p) @@ -310,21 +328,27 @@ static void dolisten(void) dolisten1(&DA(&cw.me)[i], &cw.sfv[i]); } -static void pushaddr(addr_v *av, const addr *a) +static void pushaddrs(addr_v *av, const struct addrinfo *ailist) { - DA_ENSURE(av, 1); - DA(av)[DA_LEN(av)] = *a; - DA_EXTEND(av, 1); + const struct addrinfo *ai; + size_t i, n; + + for (ai = ailist, n = 0; ai; ai = ai->ai_next) n++; + DA_ENSURE(av, n); + for (i = DA_LEN(av), ai = ailist; ai; ai = ai->ai_next) { + initaddr(&DA(av)[i]); + copyaddr(&DA(av)[i++], ai->ai_addr, caf_addr | caf_port); + } + DA_EXTEND(av, n); } #define paf_parse 1u -static void parseaddr(const char *host, const char *svc, unsigned f, addr *a) +static void parseaddr(const struct addrinfo *aihint, + const char *host, const char *svc, unsigned f, + struct addrinfo **ai_out) { char *alloc = 0, *sep; - struct hostent *h; - struct servent *s; - char *qq; - unsigned long n; + int err; if (f&paf_parse) { alloc = xstrdup(host); @@ -333,18 +357,15 @@ static void parseaddr(const char *host, const char *svc, unsigned f, addr *a) host = alloc; *sep = 0; svc = sep + 1; } - if (host) { - if ((h = gethostbyname(host)) == 0) die(1, "unknown host `%s'", host); - memcpy(&a->sin.sin_addr, h->h_addr, sizeof(a->sin.sin_addr)); - } - - if (svc) { - if ((n = strtoul(svc, &qq, 0)) > 0 && !*qq && n <= 0xffff) - a->sin.sin_port = htons(n); - else if ((s = getservbyname(svc, "tcp")) != 0) - a->sin.sin_port = s->s_port; + err = getaddrinfo(host, svc, aihint, ai_out); + if (err) { + if (host && svc) { + die(1, "failed to resolve hostname `%s', service `%s': %s", + host, svc, gai_strerror(err)); + } else if (host) + die(1, "failed to resolve hostname `%s': %s", host, gai_strerror(err)); else - die(1, "bad service name/number `%s'", svc); + die(1, "failed to resolve service `%s': %s", svc, gai_strerror(err)); } xfree(alloc); @@ -389,7 +410,7 @@ int main(int argc, char *argv[]) const char *bindsvc = 0; addr bindaddr; const char *connhost = 0; - addr tmpaddr; + struct addrinfo aihint = { 0 }, *ai, *ailist; int fd = -1; int len = 65536; size_t i, n; @@ -436,22 +457,28 @@ int main(int argc, char *argv[]) if (bindsvc && connhost) die(1, "can't listen and connect"); + aihint.ai_family = AF_INET; DA_CREATE(&cw.me); DA_CREATE(&cw.peer); n = DA_LEN(&bindhosts); if (n || bindsvc) { + aihint.ai_socktype = SOCK_STREAM; + aihint.ai_protocol = IPPROTO_TCP; + aihint.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; if (!n) { - initaddr(&tmpaddr); - parseaddr(0, bindsvc, 0, &tmpaddr); - pushaddr(&cw.me, &tmpaddr); + parseaddr(&aihint, 0, bindsvc, 0, &ailist); + pushaddrs(&cw.me, ailist); + freeaddrinfo(ailist); } else if (!bindsvc) { if (n != 1) die(1, "can only bind to one address as client"); + parseaddr(&aihint, DA(&bindhosts)[0], 0, 0, &ailist); ai = ailist; initaddr(&bindaddr); - parseaddr(DA(&bindhosts)[0], 0, 0, &bindaddr); + copyaddr(&bindaddr, ai->ai_addr, caf_addr); + freeaddrinfo(ailist); } else for (i = 0; i < n; i++) { - initaddr(&tmpaddr); - parseaddr(DA(&bindhosts)[i], bindsvc, 0, &tmpaddr); - pushaddr(&cw.me, &tmpaddr); + parseaddr(&aihint, DA(&bindhosts)[i], bindsvc, 0, &ailist); + pushaddrs(&cw.me, ailist); + freeaddrinfo(ailist); } if (bindsvc) { cw.f |= cwf_port; @@ -462,37 +489,53 @@ int main(int argc, char *argv[]) n = DA_LEN(&peerhosts); if (n) { + aihint.ai_socktype = SOCK_STREAM; + aihint.ai_protocol = IPPROTO_TCP; + aihint.ai_flags = AI_ADDRCONFIG; for (i = 0; i < n; i++) { - initaddr(&tmpaddr); - parseaddr(DA(&peerhosts)[0], 0, 0, &tmpaddr); - pushaddr(&cw.peer, &tmpaddr); + parseaddr(&aihint, DA(&peerhosts)[i], 0, 0, &ailist); + pushaddrs(&cw.peer, ailist); + freeaddrinfo(ailist); } } if (connhost) { - initaddr(&tmpaddr); - parseaddr(connhost, 0, paf_parse, &tmpaddr); - if ((fd = socket(tmpaddr.sa.sa_family, SOCK_STREAM, IPPROTO_TCP)) < 0 || - (DA_LEN(&bindhosts) && - bind(fd, &bindaddr.sa, addrsz(&bindaddr))) || - connect(fd, &tmpaddr.sa, addrsz(&tmpaddr))) - die(1, "couldn't connect to TCP server: %s", strerror(errno)); + aihint.ai_socktype = SOCK_STREAM; + aihint.ai_protocol = IPPROTO_TCP; + aihint.ai_flags = AI_ADDRCONFIG; + parseaddr(&aihint, connhost, 0, paf_parse, &ailist); + + for (ai = ailist; ai; ai = ai->ai_next) { + if ((fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP)) >= 0 && + (!DA_LEN(&bindhosts) || + !bind(fd, &bindaddr.sa, addrsz(&bindaddr))) && + !connect(fd, ai->ai_addr, ai->ai_addrlen)) + goto conn_tcp; + if (fd >= 0) close(fd); + } + die(1, "couldn't connect to TCP server: %s", strerror(errno)); + conn_tcp: if (nonblockify(fd) || cloexec(fd)) die(1, "couldn't connect to TCP server: %s", strerror(errno)); } - initaddr(&tmpaddr); - parseaddr(argv[optind], 0, paf_parse, &tmpaddr); - if ((fd_udp = socket(tmpaddr.sa.sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0 || + aihint.ai_socktype = SOCK_DGRAM; + aihint.ai_protocol = IPPROTO_UDP; + aihint.ai_flags = AI_ADDRCONFIG | AI_PASSIVE; + parseaddr(&aihint, argv[optind], 0, paf_parse, &ailist); ai = ailist; + if ((fd_udp = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP)) < 0 || nonblockify(fd_udp) || cloexec(fd_udp) || setsockopt(fd_udp, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) || setsockopt(fd_udp, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len)) || - bind(fd_udp, &tmpaddr.sa, addrsz(&tmpaddr))) - die(1, "couldn't set up UDP socket: %s", strerror(errno)); - initaddr(&tmpaddr); - parseaddr(argv[optind + 1], 0, paf_parse, &tmpaddr); - if (connect(fd_udp, &tmpaddr.sa, addrsz(&tmpaddr))) + bind(fd_udp, ai->ai_addr, ai->ai_addrlen)) die(1, "couldn't set up UDP socket: %s", strerror(errno)); + freeaddrinfo(ailist); + aihint.ai_flags = AI_ADDRCONFIG; + parseaddr(&aihint, argv[optind + 1], 0, paf_parse, &ailist); + for (ai = ailist; ai; ai = ai->ai_next) + if (!connect(fd_udp, ai->ai_addr, ai->ai_addrlen)) goto conn_udp; + die(1, "couldn't set up UDP socket: %s", strerror(errno)); +conn_udp: if (bindsvc) dolisten(); else if (connhost) dofwd(fd, fd);