+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)
+{
+ a->sa.sa_family = af;
+ switch (af) {
+ case AF_INET:
+ a->sin.sin_addr.s_addr = INADDR_ANY;
+ a->sin.sin_port = 0;
+ break;
+ case AF_INET6:
+ memset(a->sin6.sin6_addr.s6_addr, 0, 16);
+ a->sin6.sin6_port = 0;
+ a->sin6.sin6_flowinfo = 0;
+ a->sin6.sin6_scope_id = 0;
+ break;
+ default:
+ abort();
+ }
+}
+
+#define caf_addr 1u
+#define caf_port 2u
+static void copyaddr(addr *a, const struct sockaddr *sa, unsigned f)
+{
+ const struct sockaddr_in *sin;
+ const struct sockaddr_in6 *sin6;
+
+ 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;
+ case AF_INET6:
+ sin6 = (const struct sockaddr_in6 *)sa;
+ if (f&caf_addr) {
+ a->sin6.sin6_addr = sin6->sin6_addr;
+ a->sin6.sin6_scope_id = sin6->sin6_scope_id;
+ }
+ if (f&caf_port) a->sin6.sin6_port = sin6->sin6_port;
+ /* ??? flowinfo? */
+ break;
+ default:
+ abort();
+ }
+}
+