+static socklen_t addrsz(const addr *a)
+{
+ switch (a->sa.sa_family) {
+ case AF_INET: return sizeof(a->sin);
+ case AF_INET6: return sizeof(a->sin6);
+ default: abort();
+ }
+}
+
+static int knownafp(int af)
+{
+ switch (af) {
+ case AF_INET: case AF_INET6: return (1);
+ default: return (0);
+ }
+}
+
+static int initsock(int fd, int af)
+{
+ int yes = 1;
+
+ switch (af) {
+ case AF_INET: break;
+ case AF_INET6:
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)))
+ return (-1);
+ break;
+ default: abort();
+ }
+ return (0);
+}
+
+static const char *addrstr(const addr *a)
+{
+ static char buf[128];
+ socklen_t n = sizeof(buf);
+
+ if (getnameinfo(&a->sa, addrsz(a), buf, n, 0, 0, NI_NUMERICHOST))
+ return ("<addrstr failed>");
+ return (buf);
+}
+
+static int addreq(const addr *a, const addr *b)
+{
+ if (a->sa.sa_family != b->sa.sa_family) return (0);
+ switch (a->sa.sa_family) {
+ case AF_INET:
+ return (a->sin.sin_addr.s_addr == b->sin.sin_addr.s_addr);
+ case AF_INET6:
+ return (!memcmp(a->sin6.sin6_addr.s6_addr,
+ b->sin6.sin6_addr.s6_addr,
+ 16) &&
+ a->sin6.sin6_scope_id == b->sin6.sin6_scope_id);
+ default:
+ abort();
+ }
+}
+
+static void initaddr(addr *a, int af)