Dispatch to methods handling address-family specifics.
authorMark Wooding <mdw@distorted.org.uk>
Wed, 17 Oct 2012 23:11:56 +0000 (00:11 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 21 Oct 2012 15:08:58 +0000 (16:08 +0100)
This is a fairly invasive change to the program.  Rather than carry
around an address family type and switch on it throughout, carry around
a pointer to a vtable containing methods.  Methods needed by the system-
specific machinery are properly integrated.

Makefile.am
addr.c [new file with mode: 0644]
linux.c
policy.c
yaid.c
yaid.h

index 2c3f17b..ef046fb 100644 (file)
@@ -41,6 +41,7 @@ EXTRA_yaid_SOURCES     =
 yaid_LDADD              = $(mLib_LIBS)
 
 yaid_SOURCES           += yaid.c
+yaid_SOURCES           += addr.c
 yaid_SOURCES           += policy.c
 
 yaid_LDADD             += $(SYS).$(OBJEXT)
diff --git a/addr.c b/addr.c
new file mode 100644 (file)
index 0000000..25211df
--- /dev/null
+++ b/addr.c
@@ -0,0 +1,136 @@
+/* -*-c-*-
+ *
+ * Address-type specific functionality
+ *
+ * (c) 2012 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Yet Another Ident Daemon (YAID).
+ *
+ * YAID is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * YAID is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with YAID; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "yaid.h"
+
+/*----- IPv4 addresses ----------------------------------------------------*/
+
+static int addreq_ipv4(const union addr *a, const union addr *b)
+  { return a->ipv4.s_addr == b->ipv4.s_addr; }
+
+static int match_addrpat_ipv4(const struct addrpat *ap, const union addr *a)
+{
+  unsigned m;
+
+  if (!ap->len) return (1);
+  m = htonl((MASK32 << (32 - ap->len)) & MASK32);
+  return (((ap->addr.ipv4.s_addr ^ a->ipv4.s_addr) & m) == 0);
+}
+
+static void socket_to_sockaddr_ipv4(const struct socket *s,
+                                   void *sa, size_t *ssz)
+{
+  struct sockaddr_in *sin = sa;
+
+  sin->sin_family = AF_INET;
+  sin->sin_addr = s->addr.ipv4;
+  sin->sin_port = htons(s->port);
+  *ssz = sizeof(*sin);
+}
+
+static void sockaddr_to_addr_ipv4(const void *sa, union addr *a)
+  { const struct sockaddr_in *sin = sa; a->ipv4 = sin->sin_addr; }
+
+static int init_listen_socket_ipv4(int fd) { return (0); }
+
+static const union addr any_ipv4 = { .ipv4.s_addr = INADDR_ANY };
+
+/*----- IPv6 addresses ----------------------------------------------------*/
+
+static int addreq_ipv6(const union addr *a, const union addr *b)
+  { return !memcmp(a->ipv6.s6_addr, b->ipv6.s6_addr, 16); }
+
+static int match_addrpat_ipv6(const struct addrpat *ap, const union addr *a)
+{
+  unsigned i = 0, m, n = ap->len;
+
+  if (!n) return (1);
+  for (i = 0; n >= 8; i++, n -= 8)
+    if (ap->addr.ipv6.s6_addr[i] != a->ipv6.s6_addr[i]) return (0);
+  if (!n) return (1);
+  m = (MASK8 << (8 - n)) & MASK8;
+  return (((ap->addr.ipv6.s6_addr[i] ^ a->ipv6.s6_addr[i]) & m) == 0);
+}
+
+static void socket_to_sockaddr_ipv6(const struct socket *s,
+                                   void *sa, size_t *ssz)
+{
+  struct sockaddr_in6 *sin6 = sa;
+
+  sin6->sin6_family = AF_INET6;
+  sin6->sin6_addr = s->addr.ipv6;
+  sin6->sin6_port = htons(s->port);
+  sin6->sin6_flowinfo = 0;
+  sin6->sin6_scope_id = 0;
+  *ssz = sizeof(*sin6);
+}
+
+static void sockaddr_to_addr_ipv6(const void *sa, union addr *a)
+  { const struct sockaddr_in6 *sin6 = sa; a->ipv6 = sin6->sin6_addr; }
+
+static int init_listen_socket_ipv6(int fd)
+{
+  int yes = 1;
+
+  if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes)))
+    return (-1);
+  return (0);
+}
+
+static const union addr any_ipv6 = { .ipv6 = IN6ADDR_ANY_INIT };
+
+/*----- General utilities -------------------------------------------------*/
+
+int sockeq(const struct addrops *ao,
+          const struct socket *sa, const struct socket *sb)
+  { return (ao->addreq(&sa->addr, &sb->addr) && sa->port == sb->port); }
+
+void dputsock(dstr *d, const struct addrops *ao, const struct socket *s)
+{
+  char buf[ADDRLEN];
+
+  inet_ntop(ao->af, &s->addr, buf, sizeof(buf));
+  if (!s->port || ao->af != AF_INET6) dstr_puts(d, buf);
+  else { dstr_putc(d, '['); dstr_puts(d, buf); dstr_putc(d, ']'); }
+  if (s->port) dstr_putf(d, ":%d", s->port);
+}
+
+/*----- The operations table ----------------------------------------------*/
+
+const struct addrops addroptab[] = {
+#define DEFOPS(ty, TY, af, name, len)                                  \
+  { AF_##af, name, len, &any_##ty, &addrops_sys_##ty,                  \
+    addreq_##ty, match_addrpat_##ty,                                   \
+    socket_to_sockaddr_##ty, sockaddr_to_addr_##ty,                    \
+    init_listen_socket_##ty },
+ADDRTYPES(DEFOPS)
+  { 0 }
+#undef DEFOPS
+};
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/linux.c b/linux.c
index 91c03aa..252b69c 100644 (file)
--- a/linux.c
+++ b/linux.c
@@ -1,6 +1,6 @@
 /* -*-c-*-
  *
- * Discover the owner of a connection
+ * Discover the owner of a connection (Linux version)
  *
  * (c) 2012 Straylight/Edgeware
  */
 
 /*----- Static variables --------------------------------------------------*/
 
-const char *const errtok[] = {
-#define DEFTOK(err, tok) tok,
-  ERROR(DEFTOK)
-#undef DEFTOK
+struct addrops_sys {
+  const char *procfile;
+  int (*parseaddr)(char **, union addr *);
 };
 
-static int parseaddr4(char **pp, union addr *a)
+static int parseaddr_ipv4(char **pp, union addr *a)
   { a->ipv4.s_addr = strtoul(*pp, pp, 16); return (0); }
 
-static int addreq4(const union addr *a, const union addr *aa)
-  { return a->ipv4.s_addr == aa->ipv4.s_addr; }
+const struct addrops_sys addrops_sys_ipv4 = {
+  "/proc/net/tcp", parseaddr_ipv4
+};
 
-static int parseaddr6(char **pp, union addr *a)
+static int parseaddr_ipv6(char **pp, union addr *a)
 {
   int i, j;
   unsigned long y;
@@ -65,27 +65,13 @@ static int parseaddr6(char **pp, union addr *a)
   return (0);
 }
 
-static int addreq6(const union addr *a, const union addr *b)
-  { return !memcmp(a->ipv6.s6_addr, b->ipv6.s6_addr, 16); }
-
-static const struct addrfamily {
-  int af;
-  const char *procfile;
-  int (*parseaddr)(char **pp, union addr *a);
-  int (*addreq)(const union addr *a, const union addr *aa);
-} addrfamilytab[] = {
-  { AF_INET, "/proc/net/tcp", parseaddr4, addreq4 },
-  { AF_INET6, "/proc/net/tcp6", parseaddr6, addreq6 },
-  { -1 }
+const struct addrops_sys addrops_sys_ipv6 = {
+  "/proc/net/tcp6", parseaddr_ipv6
 };
 
 /*----- Main code ---------------------------------------------------------*/
 
-static int sockeq(const struct addrfamily *af,
-                 const struct socket *sa, const struct socket *sb)
-  { return (af->addreq(&sa->addr, &sb->addr) && sa->port == sb->port); }
-
-static int get_default_gw(int af, union addr *a)
+int get_default_gw(int af, union addr *a)
 {
   int fd;
   char buf[32768];
@@ -152,7 +138,6 @@ done:
 
 void identify(struct query *q)
 {
-  const struct addrfamily *af;
   FILE *fp = 0;
   dstr d = DSTR_INIT;
   char *p, *pp;
@@ -170,19 +155,13 @@ void identify(struct query *q)
   enum { LOC, REM, ST, UID, NFIELD };
   int f, ff[NFIELD];
 
-  for (af = addrfamilytab; af->af >= 0; af++)
-    if (af->af == q->af) goto found_af;
-  logmsg(q, LOG_ERR, "unexpected address family `%d'", q->af);
-  goto err_unk;
-found_af:;
-
-  if (get_default_gw(q->af, &s[0].addr) &&
-      af->addreq(&s[0].addr, &q->s[R].addr))
+  if (get_default_gw(q->ao->af, &s[0].addr) &&
+      q->ao->addreq(&s[0].addr, &q->s[R].addr))
     gwp = 1;
 
-  if ((fp = fopen(af->procfile, "r")) == 0) {
+  if ((fp = fopen(q->ao->sys->procfile, "r")) == 0) {
     logmsg(q, LOG_ERR, "failed to open `%s' for reading: %s",
-          af->procfile, strerror(errno));
+          q->ao->sys->procfile, strerror(errno));
     goto err_unk;
   }
 
@@ -194,7 +173,8 @@ found_af:;
 
   if (dstr_putline(&d, fp) == EOF) {
     logmsg(q, LOG_ERR, "failed to read header line from `%s': %s",
-          af->procfile, ferror(fp) ? strerror(errno) : "unexpected EOF");
+          q->ao->sys->procfile,
+          ferror(fp) ? strerror(errno) : "unexpected EOF");
     goto err_unk;
   }
 
@@ -218,7 +198,7 @@ found_af:;
   for (i = 0; i < NFIELD; i++) {
     if (ff[i] < 0) {
       logmsg(q, LOG_ERR, "failed to find required fields in `%s'",
-            af->procfile);
+            q->ao->sys->procfile);
       goto err_unk;
     }
   }
@@ -239,10 +219,10 @@ found_af:;
       continue;
 
     compare:
-      if (af->parseaddr(&p, &s[0].addr)) goto next_row;
+      if (q->ao->sys->parseaddr(&p, &s[0].addr)) goto next_row;
       if (*p != ':') break; p++;
       s[0].port = strtoul(p, 0, 16);
-      if (!sockeq(af, &q->s[i], &s[0]) &&
+      if (!sockeq(q->ao, &q->s[i], &s[0]) &&
          (i != R || !gwp || q->s[R].port != s[0].port))
        goto next_row;
     }
@@ -260,7 +240,7 @@ found_af:;
     goto err_unk;
   }
 
-  if (q->af == AF_INET) {
+  if (q->ao->af == AF_INET) {
     fclose(fp);
     if ((fp = fopen("/proc/net/ip_conntrack", "r")) == 0) {
       if (errno == ENOENT)
@@ -309,13 +289,13 @@ found_af:;
       {
        dstr dd = DSTR_INIT;
        dstr_putf(&dd, "%sestab ", (fl & F_ESTAB) ? " " : "!");
-       dputsock(&dd, af->af, &s[0]);
+       dputsock(&dd, q->ao, &s[0]);
        dstr_puts(&dd, "<->");
-       dputsock(&dd, af->af, &s[1]);
+       dputsock(&dd, q->ao, &s[1]);
        dstr_puts(&dd, " | ");
-       dputsock(&dd, af->af, &s[2]);
+       dputsock(&dd, q->ao, &s[2]);
        dstr_puts(&dd, "<->");
-       dputsock(&dd, af->af, &s[3]);
+       dputsock(&dd, q->ao, &s[3]);
        printf("parsed: %s\n", dd.buf);
        dstr_destroy(&dd);
       }
@@ -324,12 +304,12 @@ found_af:;
       if (!(fl & F_ESTAB)) continue;
 
       for (i = 0; i < 4; i++)
-       if (sockeq(af, &s[i], &q->s[L])) goto found_local;
+       if (sockeq(q->ao, &s[i], &q->s[L])) goto found_local;
       continue;
       putchar('.');
     found_local:
-      if (!sockeq(af, &s[i^1], &s[i^2]) ||
-         !sockeq(af, &s[i^1], &q->s[R]))
+      if (!sockeq(q->ao, &s[i^1], &s[i^2]) ||
+         !sockeq(q->ao, &s[i^1], &q->s[R]))
        continue;
       q->resp = R_NAT;
       q->u.nat = s[i^3];
index 398cd23..80e2bfd 100644 (file)
--- a/policy.c
+++ b/policy.c
 
 #include "yaid.h"
 
-/*----- Data structures ---------------------------------------------------*/
-
-/*----- Static variables --------------------------------------------------*/
-
 /*----- Main code ---------------------------------------------------------*/
 
 /* syntax: addrpat portpat addrpar portpat policy
@@ -57,12 +53,17 @@ static void free_action(struct action *a)
 void free_policy(struct policy *p)
   { free_action(&p->act); }
 
-static void print_addrpat(int af, const struct addrpat *ap)
+static void print_addrpat(const struct addrops *ao, const struct addrpat *ap)
 {
   char buf[ADDRLEN];
 
-  if (ap->len == 0) putchar('*');
-  else printf("%s/%u", inet_ntop(af, &ap->addr, buf, sizeof(buf)), ap->len);
+  if (ap->len == 0)
+    putchar('*');
+  else {
+    printf("%s/%u",
+          inet_ntop(ao->af, &ap->addr, buf, sizeof(buf)),
+          ap->len);
+  }
 }
 
 static void print_portpat(const struct portpat *pp)
@@ -72,8 +73,8 @@ static void print_portpat(const struct portpat *pp)
   else printf("%u-%u", pp->lo, pp->hi);
 }
 
-static void print_sockpat(int af, const struct sockpat *sp)
-  { print_addrpat(af, &sp->addr); putchar(' '); print_portpat(&sp->port); }
+static void print_sockpat(const struct addrops *ao, const struct sockpat *sp)
+  { print_addrpat(ao, &sp->addr); putchar(' '); print_portpat(&sp->port); }
 
 static const char *const acttab[] = {
 #define DEFACT(tag, name) name,
@@ -101,50 +102,26 @@ static void print_action(const struct action *act)
 
 void print_policy(const struct policy *p)
 {
-  print_sockpat(p->af, &p->sp[L]); putchar(' ');
-  print_sockpat(p->af, &p->sp[R]); putchar(' ');
+  print_sockpat(p->ao, &p->sp[L]); putchar(' ');
+  print_sockpat(p->ao, &p->sp[R]); putchar(' ');
   print_action(&p->act); putchar('\n');
 }
 
-static int match_addrpat(int af, const struct addrpat *ap,
-                        const union addr *a)
-{
-  if (!ap->len)
-    return (1);
-  switch (af) {
-    case AF_INET: {
-      unsigned mask = htonl((MASK32 << (32 - ap->len)) & MASK32);
-      return (((ap->addr.ipv4.s_addr ^ a->ipv4.s_addr) & mask) == 0);
-    }
-    case AF_INET6: {
-      unsigned i, m, n = ap->len;
-      for (i = 0; n >= 8; i++, n -= 8) {
-       if (ap->addr.ipv6.s6_addr[i] != a->ipv6.s6_addr[i])
-         return (0);
-      }
-      if (!n) return (1);
-      m = (MASK8 << (8 - n)) & MASK8;
-      return (((ap->addr.ipv6.s6_addr[i] ^ a->ipv6.s6_addr[i]) & m) == 0);
-    }
-  }
-  return (0);
-}
-
 static int match_portpat(const struct portpat *pp, unsigned port)
   { return (pp->lo <= port && port <= pp->hi); }
 
-static int match_sockpat(int af, const struct sockpat *sp,
-                        const struct socket *s)
+static int match_sockpat(const struct addrops *ao,
+                        const struct sockpat *sp, const struct socket *s)
 {
-  return (match_addrpat(af, &sp->addr, &s->addr) &&
+  return (ao->match_addrpat(&sp->addr, &s->addr) &&
          match_portpat(&sp->port, s->port));
 }
 
 int match_policy(const struct policy *p, const struct query *q)
 {
-  return ((!p->af || p->af == q->af) &&
-         match_sockpat(p->af, &p->sp[L], &q->s[L]) &&
-         match_sockpat(p->af, &p->sp[R], &q->s[R]));
+  return ((!p->ao || p->ao == q->ao) &&
+         match_sockpat(q->ao, &p->sp[L], &q->s[L]) &&
+         match_sockpat(q->ao, &p->sp[R], &q->s[R]));
 }
 
 static void nextline(FILE *fp)
@@ -255,12 +232,12 @@ static int parse_action(FILE *fp, struct action *act)
   return (0);
 }
 
-static int parse_sockpat(FILE *fp, int *afp, struct sockpat *sp)
+static int parse_sockpat(FILE *fp, const struct addrops **aop,
+                        struct sockpat *sp)
 {
   char buf[64];
   int t;
-  int af;
-  int alen;
+  const struct addrops *ao;
   long n;
   char *delim;
 
@@ -268,21 +245,18 @@ static int parse_sockpat(FILE *fp, int *afp, struct sockpat *sp)
   if (strcmp(buf, "*") == 0)
     sp->addr.len = 0;
   else {
-    if (strchr(buf, ':')) {
-      af = AF_INET6;
-      alen = 128;
-    } else {
-      af = AF_INET;
-      alen = 32;
-    }
-    if (!*afp) *afp = af;
-    else if (*afp != af) return (T_ERROR);
+    if (strchr(buf, ':'))
+      ao = &addroptab[ADDR_IPV6];
+    else
+      ao = &addroptab[ADDR_IPV4];
+    if (!*aop) *aop = ao;
+    else if (*aop != ao) return (T_ERROR);
     delim = strchr(buf, '/');
     if (delim) *delim++ = 0;
-    if (!inet_pton(af, buf, &sp->addr.addr)) return (T_ERROR);
-    if (!delim) n = alen;
+    if (!inet_pton(ao->af, buf, &sp->addr.addr)) return (T_ERROR);
+    if (!delim) n = ao->len;
     else n = strtol(delim, 0, 10);
-    if (n < 0 || n > alen) return (T_ERROR);
+    if (n < 0 || n > ao->len) return (T_ERROR);
     sp->addr.len = n;
   }
 
@@ -311,11 +285,11 @@ int parse_policy(FILE *fp, struct policy *p)
 {
   int t;
 
-  p->af = 0;
+  p->ao = 0;
   free_policy(p);
 
-  if ((t = parse_sockpat(fp, &p->af, &p->sp[L])) != 0) goto fail;
-  if ((t = parse_sockpat(fp, &p->af, &p->sp[R])) != 0) goto err;
+  if ((t = parse_sockpat(fp, &p->ao, &p->sp[L])) != 0) goto fail;
+  if ((t = parse_sockpat(fp, &p->ao, &p->sp[R])) != 0) goto err;
   if ((t = parse_action(fp, &p->act)) != 0) goto err;
   return (0);
 
diff --git a/yaid.c b/yaid.c
index 4bb44fd..f70c2a3 100644 (file)
--- a/yaid.c
+++ b/yaid.c
@@ -31,8 +31,7 @@
 /*----- Data structures ---------------------------------------------------*/
 
 struct listen {
-  int af;
-  const char *proto;
+  const struct addrops *ao;
   sel_file f;
 };
 
@@ -76,48 +75,6 @@ static int randfd;
 
 /*----- Main code ---------------------------------------------------------*/
 
-static void socket_to_sockaddr(int af, const struct socket *s,
-                              struct sockaddr *sa, size_t *ssz)
-{
-  sa->sa_family = af;
-  switch (af) {
-    case AF_INET: {
-      struct sockaddr_in *sin = (struct sockaddr_in *)sa;
-      sin->sin_addr = s->addr.ipv4;
-      sin->sin_port = htons(s->port);
-      *ssz = sizeof(*sin);
-    } break;
-    case AF_INET6: {
-      struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
-      sin6->sin6_addr = s->addr.ipv6;
-      sin6->sin6_port = htons(s->port);
-      sin6->sin6_flowinfo = 0;
-      sin6->sin6_scope_id = 0;
-      *ssz = sizeof(*sin6);
-    } break;
-    default: abort();
-  }
-}
-
-static void sockaddr_to_addr(const struct sockaddr *sa, union addr *a)
-{
-  switch (sa->sa_family) {
-    case AF_INET: a->ipv4 = ((struct sockaddr_in *)sa)->sin_addr; break;
-    case AF_INET6: a->ipv6 = ((struct sockaddr_in6 *)sa)->sin6_addr; break;
-    default: abort();
-  }
-}
-
-static void dputsock(dstr *d, int af, const struct socket *s)
-{
-  char buf[ADDRLEN];
-
-  inet_ntop(af, &s->addr, buf, sizeof(buf));
-  if (!s->port || af != AF_INET6) dstr_puts(d, buf);
-  else { dstr_putc(d, '['); dstr_puts(d, buf); dstr_putc(d, ']'); }
-  if (s->port) dstr_putf(d, ":%d", s->port);
-}
-
 void logmsg(const struct query *q, int prio, const char *msg, ...)
 {
   va_list ap;
@@ -125,9 +82,9 @@ void logmsg(const struct query *q, int prio, const char *msg, ...)
 
   va_start(ap, msg);
   if (q) {
-    dputsock(&d, q->af, &q->s[L]);
+    dputsock(&d, q->ao, &q->s[L]);
     dstr_puts(&d, " <-> ");
-    dputsock(&d, q->af, &q->s[R]);
+    dputsock(&d, q->ao, &q->s[R]);
     dstr_puts(&d, ": ");
   }
   dstr_vputf(&d, msg, &ap);
@@ -251,6 +208,12 @@ static void reply(struct client *c, const char *ty, const char *msg)
                  c->q.s[L].port, c->q.s[R].port, ty, msg);
 }
 
+const char *const errtok[] = {
+#define DEFTOK(err, tok) tok,
+  ERROR(DEFTOK)
+#undef DEFTOK
+};
+
 static void reply_error(struct client *c, unsigned err)
 {
   assert(err < E_LIMIT);
@@ -358,7 +321,7 @@ static void proxy_connected(int fd, void *p)
   if (fd < 0) {
     logmsg(&px->c->q, LOG_ERR,
           "failed to make %s proxy connection to %s: %s",
-          px->c->l->proto, px->nat, strerror(errno));
+          px->c->l->ao->name, px->nat, strerror(errno));
     reply_error(px->c, E_UNKNOWN);
     cancel_proxy(px);
     return;
@@ -380,33 +343,31 @@ static void proxy_query(struct client *c)
   struct sockaddr_storage ss;
   size_t ssz;
   struct proxy *px;
-  int o;
   int fd;
 
   px = xmalloc(sizeof(*px));
-  inet_ntop(c->q.af, &c->q.u.nat.addr, px->nat, sizeof(px->nat));
+  inet_ntop(c->q.ao->af, &c->q.u.nat.addr, px->nat, sizeof(px->nat));
 
-  if ((fd = socket(c->q.af, SOCK_STREAM, 0)) < 0) {
+  if ((fd = socket(c->q.ao->af, SOCK_STREAM, 0)) < 0) {
     logmsg(&c->q, LOG_ERR, "failed to make %s socket for proxy: %s",
-          c->l->proto, strerror(errno));
+          c->l->ao->name, strerror(errno));
     goto err_0;
   }
 
-  if ((o = fcntl(fd, F_GETFL)) < 0 ||
-      fcntl(fd, F_SETFL, o | O_NONBLOCK)) {
+  if (fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0)) {
     logmsg(&c->q, LOG_ERR, "failed to set %s proxy socket nonblocking: %s",
-          c->l->proto, strerror(errno));
+          c->l->ao->name, strerror(errno));
     goto err_1;
   }
 
   s = c->q.u.nat;
   s.port = 113;
-  socket_to_sockaddr(c->q.af, &s, (struct sockaddr *)&ss, &ssz);
+  c->l->ao->socket_to_sockaddr(&s, &ss, &ssz);
   selbuf_disable(&c->b);
   if (conn_init(&px->cn, &sel, fd, (struct sockaddr *)&ss, ssz,
                proxy_connected, px)) {
     logmsg(&c->q, LOG_ERR, "failed to make %s proxy connection to %s: %s",
-          c->l->proto, px->nat, strerror(errno));
+          c->l->ao->name, px->nat, strerror(errno));
     goto err_2;
   }
 
@@ -582,28 +543,28 @@ static void accept_client(int fd, unsigned mode, void *p)
   if ((sk = accept(fd, (struct sockaddr *)&ssr, &ssz)) < 0) {
     if (errno != EAGAIN && errno == EWOULDBLOCK) {
       logmsg(0, LOG_ERR, "failed to accept incoming %s connection: %s",
-            l->proto, strerror(errno));
+            l->ao->name, strerror(errno));
     }
     return;
   }
 
   c = xmalloc(sizeof(*c));
   c->l = l;
-  c->q.af = l->af;
-  sockaddr_to_addr((struct sockaddr *)&ssr, &c->q.s[R].addr);
+  c->q.ao = l->ao;
+  l->ao->sockaddr_to_addr(&ssr, &c->q.s[R].addr);
   ssz = sizeof(ssl);
   if (getsockname(sk, (struct sockaddr *)&ssl, &ssz)) {
     logmsg(0, LOG_ERR,
           "failed to read local address for incoming %s connection: %s",
-          l->proto, strerror(errno));
+          l->ao->name, strerror(errno));
     close(sk);
     xfree(c);
     return;
   }
-  sockaddr_to_addr((struct sockaddr *)&ssl, &c->q.s[L].addr);
+  l->ao->sockaddr_to_addr(&ssl, &c->q.s[L].addr);
   c->q.s[L].port = c->q.s[R].port = 0;
 
-  /* logmsg(&c->q, LOG_INFO, "accepted %s connection", l->proto); */
+  /* logmsg(&c->q, LOG_INFO, "accepted %s connection", l->ao->name); */
 
   selbuf_init(&c->b, &sel, sk, client_line, c);
   selbuf_setsize(&c->b, 1024);
@@ -612,53 +573,41 @@ static void accept_client(int fd, unsigned mode, void *p)
   init_writebuf(&c->wb, sk, done_client_write, c);
 }
 
-static int make_listening_socket(int af, int port, const char *proto)
+static int make_listening_socket(const struct addrops *ao, int port)
 {
   int fd;
-  int o;
+  int yes = 1;
+  struct socket s;
   struct sockaddr_storage ss;
   struct listen *l;
   size_t ssz;
 
-  if ((fd = socket(af, SOCK_STREAM, 0)) < 0) {
+  if ((fd = socket(ao->af, SOCK_STREAM, 0)) < 0) {
     if (errno == EAFNOSUPPORT) return (-1);
     die(1, "failed to create %s listening socket: %s",
-       proto, strerror(errno));
+       ao->name, strerror(errno));
   }
-  o = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &o, sizeof(o));
-  ss.ss_family = af;
-  switch (af) {
-    case AF_INET: {
-      struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
-      sin->sin_addr.s_addr = INADDR_ANY;
-      sin->sin_port = htons(port);
-      ssz = sizeof(*sin);
-    } break;
-    case AF_INET6: {
-      struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
-      o = 1; setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &o, sizeof(o));
-      sin6->sin6_family = AF_INET6;
-      sin6->sin6_addr = in6addr_any;
-      sin6->sin6_scope_id = 0;
-      sin6->sin6_flowinfo = 0;
-      ssz = sizeof(*sin6);
-    } break;
-    default:
-      abort();
+  setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
+  s.addr = *ao->any;
+  s.port = port;
+  ao->socket_to_sockaddr(&s, &ss, &ssz);
+  if (ao->init_listen_socket(fd)) {
+    die(1, "failed to initialize %s listening socket: %s",
+       ao->name, strerror(errno));
+  }
+  if (bind(fd, (struct sockaddr *)&ss, ssz)) {
+    die(1, "failed to bind %s listening socket: %s",
+       ao->name, strerror(errno));
   }
-  if (bind(fd, (struct sockaddr *)&ss, ssz))
-    die(1, "failed to bind %s listening socket: %s", proto, strerror(errno));
-  if ((o = fcntl(fd, F_GETFL)) < 0 ||
-      fcntl(fd, F_SETFL, o | O_NONBLOCK)) {
+  if (fdflags(fd, O_NONBLOCK, O_NONBLOCK, 0, 0)) {
     die(1, "failed to set %s listening socket nonblocking: %s",
-       proto, strerror(errno));
+       ao->name, strerror(errno));
   }
   if (listen(fd, 5))
-    die(1, "failed to listen for %s: %s", proto, strerror(errno));
+    die(1, "failed to listen for %s: %s", ao->name, strerror(errno));
 
   l = xmalloc(sizeof(*l));
-  l->af = af;
-  l->proto = proto;
+  l->ao = ao;
   sel_initfile(&sel, &l->f, fd, SEL_READ, accept_client, l);
   sel_addfile(&l->f);
 
@@ -668,6 +617,8 @@ static int make_listening_socket(int af, int port, const char *proto)
 int main(int argc, char *argv[])
 {
   int port = 113;
+  const struct addrops *ao;
+  int any = 0;
 
   ego(argv[0]);
 
@@ -685,8 +636,9 @@ int main(int argc, char *argv[])
   }
 
   sel_init(&sel);
-  if (make_listening_socket(AF_INET, port, "IPv4") &&
-      make_listening_socket(AF_INET6, port, "IPv6"))
+  for (ao = addroptab; ao->name; ao++)
+    if (!make_listening_socket(ao, port)) any = 1;
+  if (!any)
     die(1, "no IP protocols supported");
 
   for (;;)
diff --git a/yaid.h b/yaid.h
index e0d13c1..85ad397 100644 (file)
--- a/yaid.h
+++ b/yaid.h
@@ -33,6 +33,8 @@
 
 /*----- Header files ------------------------------------------------------*/
 
+#include "config.h"
+
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-
 #include <syslog.h>
 
 #include <mLib/bits.h>
 #include <mLib/conn.h>
 #include <mLib/darray.h>
 #include <mLib/dstr.h>
+#include <mLib/fdflags.h>
 #include <mLib/fwatch.h>
 #include <mLib/quis.h>
 #include <mLib/report.h>
 #include <mLib/sel.h>
 #include <mLib/selbuf.h>
 
+/*----- System specifics --------------------------------------------------*/
+
+#define SYS_UNDEF 0
+#define SYS_LINUX 1
+
+#if SYS == SYS_LINUX
+#  include <linux/netlink.h>
+#  include <linux/rtnetlink.h>
+#else
+#  error "Unsupported operating system: sorry.  Patches welcome!"
+#endif
+
 /*----- Data structures ---------------------------------------------------*/
 
 #define ADDRLEN 64
@@ -81,6 +93,50 @@ struct socket {
   unsigned port;
 };
 
+struct addrpat {
+  unsigned len;
+  union addr addr;
+};
+
+struct portpat {
+  unsigned lo, hi;
+};
+
+struct sockpat {
+  struct addrpat addr;
+  struct portpat port;
+};
+
+#define ADDRTYPES(_)                                                   \
+  _(ipv4, IPV4, INET, "IPv4", 32)                                      \
+  _(ipv6, IPV6, INET6, "IPv6", 128)
+
+struct addrops {
+  int af;
+  const char *name;
+  unsigned len;
+  const union addr *any;
+  const struct addrops_sys *sys;
+  int (*addreq)(const union addr *, const union addr *);
+  int (*match_addrpat)(const struct addrpat *, const union addr *);
+  void (*socket_to_sockaddr)(const struct socket *s, void *, size_t *);
+  void (*sockaddr_to_addr)(const void *, union addr *);
+  int (*init_listen_socket)(int);
+};
+
+enum {
+#define DEFADDR(ty, TY, af, name, len) ADDR_##TY,
+  ADDRTYPES(DEFADDR)
+#undef DEFADDR
+  ADDR_LIMIT
+};
+
+extern const struct addrops addroptab[];
+#define OPS_SYS(ty, TY, af, name, len)                                 \
+  extern const struct addrops_sys addrops_sys_##ty;
+ADDRTYPES(OPS_SYS)
+#undef OPS_SYS
+
 enum { L, R, NDIR };
 
 #define RESPONSE(_)                                                    \
@@ -110,7 +166,7 @@ enum {
 };
 
 struct query {
-  int af;
+  const struct addrops *ao;
   struct socket s[NDIR];
   unsigned resp;
   union {
@@ -131,20 +187,6 @@ enum {
   T_ERROR
 };
 
-struct addrpat {
-  unsigned len;
-  union addr addr;
-};
-
-struct portpat {
-  unsigned lo, hi;
-};
-
-struct sockpat {
-  struct addrpat addr;
-  struct portpat port;
-};
-
 #define ACTIONS(_)                                                     \
   _(USER, "user")                                                      \
   _(TOKEN, "token")                                                    \
@@ -169,7 +211,7 @@ struct action {
 };
 
 struct policy {
-  int af;
+  const struct addrops *ao;
   struct sockpat sp[NDIR];
   struct action act;
 };
@@ -189,6 +231,10 @@ DA_DECL(policy_v, struct policy);
 
 /*----- Functions provided ------------------------------------------------*/
 
+int sockeq(const struct addrops *ao,
+          const struct socket *sa, const struct socket *sb);
+void dputsock(dstr *d, const struct addrops *ao, const struct socket *s);
+
 void logmsg(const struct query *q, int prio, const char *msg, ...);
 
 void identify(struct query *q);