Dispatch to methods handling address-family specifics.
[yaid] / addr.c
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 -------------------------------------------------*/