+}
+
+/* Extract the address part from SA and store it in A. */
+static void ipaddr_from_sockaddr(ipaddr *a, const struct sockaddr *sa)
+{
+ switch (sa->sa_family) {
+ case AF_INET: a->v4 = SIN(sa)->sin_addr; break;
+ case AF_INET6: a->v6 = SIN6(sa)->sin6_addr; break;
+ default: abort();
+ }
+}
+
+/* Store the address A in SA. */
+static void ipaddr_to_sockaddr(struct sockaddr *sa, const ipaddr *a)
+{
+ switch (sa->sa_family) {
+ case AF_INET:
+ SIN(sa)->sin_addr = a->v4;
+ break;
+ case AF_INET6:
+ SIN6(sa)->sin6_addr = a->v6;
+ SIN6(sa)->sin6_scope_id = 0;
+ SIN6(sa)->sin6_flowinfo = 0;
+ break;
+ default:
+ abort();
+ }
+}
+
+/* Copy a whole socket address about. */
+static void copy_sockaddr(struct sockaddr *sa_dst,
+ const struct sockaddr *sa_src)
+ { memcpy(sa_dst, sa_src, family_socklen(sa_src->sa_family)); }
+
+/* Answer whether two addresses are equal. */
+static int ipaddr_equal_p(int af, const ipaddr *a, const ipaddr *b)
+{
+ switch (af) {
+ case AF_INET: return (a->v4.s_addr == b->v4.s_addr);
+ case AF_INET6: return (memcmp(a->v6.s6_addr, b->v6.s6_addr, 16) == 0);
+ default: abort();
+ }
+}
+
+/* Answer whether the address part of SA is between A and B (inclusive). We
+ * assume that SA has the correct address family.
+ */
+static int sockaddr_in_range_p(const struct sockaddr *sa,
+ const ipaddr *a, const ipaddr *b)
+{
+ switch (sa->sa_family) {
+ case AF_INET: {
+ unsigned long addr = ntohl(SIN(sa)->sin_addr.s_addr);
+ return (ntohl(a->v4.s_addr) <= addr &&
+ addr <= ntohl(b->v4.s_addr));
+ } break;
+ case AF_INET6: {
+ const uint8_t *ss = SIN6(sa)->sin6_addr.s6_addr;
+ const uint8_t *aa = a->v6.s6_addr, *bb = b->v6.s6_addr;
+ int h = 1, l = 1;
+ int i;
+
+ for (i = 0; h && l && i < 16; i++, ss++, aa++, bb++) {
+ if (*ss < *aa || *bb < *ss) return (0);
+ if (*aa < *ss) l = 0;
+ if (*ss < *bb) h = 0;
+ }
+ return (1);
+ } break;
+ default:
+ abort();
+ }
+}
+
+/* Fill in SA with the appropriate wildcard address. */
+static void wildcard_address(int af, struct sockaddr *sa)
+{
+ switch (af) {
+ case AF_INET: {
+ struct sockaddr_in *sin = SIN(sa);
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_port = 0;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ } break;
+ case AF_INET6: {
+ struct sockaddr_in6 *sin6 = SIN6(sa);
+ memset(sin6, 0, sizeof(*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = 0;
+ sin6->sin6_addr = in6addr_any;
+ sin6->sin6_scope_id = 0;
+ sin6->sin6_flowinfo = 0;
+ } break;
+ default:
+ abort();
+ }
+}
+
+/* Mask the address A, forcing all but the top PLEN bits to zero or one
+ * according to HIGHP.
+ */
+static void mask_address(int af, ipaddr *a, int plen, int highp)
+{
+ switch (af) {
+ case AF_INET: {
+ unsigned long addr = ntohl(a->v4.s_addr);
+ unsigned long mask = plen ? ~0ul << (32 - plen) : 0;
+ addr &= mask;
+ if (highp) addr |= ~mask;
+ a->v4.s_addr = htonl(addr & 0xffffffff);
+ } break;
+ case AF_INET6: {
+ int i = plen/8;
+ unsigned m = (0xff << (8 - plen%8)) & 0xff;
+ unsigned s = highp ? 0xff : 0;
+ if (m) {
+ a->v6.s6_addr[i] = (a->v6.s6_addr[i] & m) | (s & ~m);
+ i++;
+ }
+ for (; i < 16; i++) a->v6.s6_addr[i] = s;
+ } break;
+ default:
+ abort();
+ }
+}
+
+/* Write a presentation form of SA to BUF, a buffer of length SZ. LEN is the
+ * address length; if it's zero, look it up based on the address family.
+ * Return a pointer to the string (which might, in an emergency, be a static
+ * string rather than your buffer).
+ */
+static char *present_sockaddr(const struct sockaddr *sa, socklen_t len,
+ char *buf, size_t sz)
+{
+#define WANT(n_) do { if (sz < (n_)) goto nospace; } while (0)
+#define PUTC(c_) do { *buf++ = (c_); sz--; } while (0)
+
+ if (!sa) return "<null-address>";
+ if (!sz) return "<no-space-in-buffer>";
+ if (!len) len = family_socklen(sa->sa_family);
+
+ switch (sa->sa_family) {
+ case AF_UNIX: {
+ struct sockaddr_un *sun = SUN(sa);
+ char *p = sun->sun_path;
+ size_t n = len - offsetof(struct sockaddr_un, sun_path);
+
+ assert(n);
+ if (*p == 0) {
+ WANT(1); PUTC('@');
+ p++; n--;
+ while (n) {
+ switch (*p) {
+ case 0: WANT(2); PUTC('\\'); PUTC('0'); break;
+ case '\a': WANT(2); PUTC('\\'); PUTC('a'); break;
+ case '\n': WANT(2); PUTC('\\'); PUTC('n'); break;
+ case '\r': WANT(2); PUTC('\\'); PUTC('r'); break;
+ case '\t': WANT(2); PUTC('\\'); PUTC('t'); break;
+ case '\v': WANT(2); PUTC('\\'); PUTC('v'); break;
+ case '\\': WANT(2); PUTC('\\'); PUTC('\\'); break;
+ default:
+ if (*p > ' ' && *p <= '~')
+ { WANT(1); PUTC(*p); }
+ else {
+ WANT(4); PUTC('\\'); PUTC('x');
+ PUTC((*p >> 4)&0xf); PUTC((*p >> 0)&0xf);
+ }
+ break;
+ }
+ p++; n--;
+ }
+ } else {
+ if (*p != '/') { WANT(2); PUTC('.'); PUTC('/'); }
+ while (n && *p) { WANT(1); PUTC(*p); p++; n--; }
+ }
+ WANT(1); PUTC(0);
+ } break;
+ case AF_INET: case AF_INET6: {
+ char addrbuf[NI_MAXHOST], portbuf[NI_MAXSERV];
+ int err = getnameinfo(sa, len,
+ addrbuf, sizeof(addrbuf),
+ portbuf, sizeof(portbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+ assert(!err);
+ snprintf(buf, sz, strchr(addrbuf, ':') ? "[%s]:%s" : "%s:%s",
+ addrbuf, portbuf);
+ } break;
+ default:
+ snprintf(buf, sz, "<unknown-address-family %d>", sa->sa_family);
+ break;
+ }
+ return (buf);
+
+nospace:
+ buf[sz - 1] = 0;
+ return (buf);
+}
+
+/* Guess the family of a textual socket address. */
+static int guess_address_family(const char *p)
+ { return (strchr(p, ':') ? AF_INET6 : AF_INET); }
+
+/* Parse a socket address P and write the result to SA. */
+static int parse_sockaddr(struct sockaddr *sa, const char *p)
+{
+ char buf[ADDRBUFSZ];
+ char *q;
+ struct addrinfo *ai, ai_hint = { 0 };
+
+ if (strlen(p) >= sizeof(buf) - 1) return (-1);
+ strcpy(buf, p); p = buf;
+ if (*p != '[') {
+ if ((q = strchr(p, ':')) == 0) return (-1);
+ *q++ = 0;
+ } else {
+ p++;
+ if ((q = strchr(p, ']')) == 0) return (-1);
+ *q++ = 0;
+ if (*q != ':') return (-1);
+ q++;
+ }
+
+ ai_hint.ai_family = AF_UNSPEC;
+ ai_hint.ai_socktype = SOCK_DGRAM;
+ ai_hint.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
+ if (getaddrinfo(p, q, &ai_hint, &ai)) return (-1);
+ memcpy(sa, ai->ai_addr, ai->ai_addrlen);
+ freeaddrinfo(ai);
+ return (0);
+}
+
+/*----- Access control lists ----------------------------------------------*/
+
+#ifdef DEBUG
+
+static void dump_addrrange(int af, const ipaddr *min, const ipaddr *max)
+{
+ char buf[ADDRBUFSZ];
+ const char *p;
+ int plen;
+
+ plen = common_prefix_length(af, min, max);
+ p = inet_ntop(af, min, buf, sizeof(buf));
+ fprintf(stderr, strchr(p, ':') ? "[%s]" : "%s", p);
+ if (plen < 0) {
+ p = inet_ntop(af, &max, buf, sizeof(buf));
+ fprintf(stderr, strchr(p, ':') ? "-[%s]" : "-%s", p);
+ } else if (plen < address_width(af))
+ fprintf(stderr, "/%d", plen);
+}
+
+/* Write to standard error a description of the ACL node A. */
+static void dump_aclnode(const aclnode *a)
+{
+ fprintf(stderr, "noip(%d): %c ", getpid(), a->act ? '+' : '-');
+ dump_addrrange(a->af, &a->minaddr, &a->maxaddr);