Use address-family specific lists of addr/mask entries instead of
authormillert <millert@openbsd.org>
Tue, 13 Jan 2015 21:42:59 +0000 (21:42 +0000)
committermillert <millert@openbsd.org>
Tue, 13 Jan 2015 21:42:59 +0000 (21:42 +0000)
a union that can store either ipv4 or ipv6.  The old method used
4x as much memory as was really needed for ipv4.  The spamd-setup
protocol has changed from: tag;message;a/m;a/m;a/m...\n
to :tag;message;af;count;a/m;a/m;a/m...[af;count;a/m;a/m;a/m]\n
OK phessler@ "nice" beck@

libexec/spamd-setup/spamd-setup.c
libexec/spamd/grey.c
libexec/spamd/sdl.c
libexec/spamd/sdl.h
libexec/spamd/spamd.c

index 7f8aa23..e14d60f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: spamd-setup.c,v 1.39 2014/10/09 02:43:43 deraadt Exp $ */
+/*     $OpenBSD: spamd-setup.c,v 1.40 2015/01/13 21:42:59 millert Exp $ */
 
 /*
  * Copyright (c) 2003 Bob Beck.  All rights reserved.
@@ -60,13 +60,12 @@ struct blacklist {
        struct bl *bl;
        size_t blc, bls;
        u_int8_t black;
-       int count;
 };
 
 u_int32_t       imask(u_int8_t);
 u_int8_t        maxblock(u_int32_t, u_int8_t);
 u_int8_t        maxdiff(u_int32_t, u_int32_t);
-struct cidr    *range2cidrlist(struct cidr *, int *, int *, u_int32_t,
+struct cidr    *range2cidrlist(struct cidr *, u_int *, u_int *, u_int32_t,
                     u_int32_t);
 void            cidr2range(struct cidr, u_int32_t *, u_int32_t *);
 char           *atop(u_int32_t);
@@ -78,8 +77,8 @@ char          *fix_quoted_colons(char *);
 void            do_message(FILE *, char *);
 struct bl      *add_blacklist(struct bl *, size_t *, size_t *, gzFile, int);
 int             cmpbl(const void *, const void *);
-struct cidr    *collapse_blacklist(struct bl *, size_t);
-int             configure_spamd(u_short, char *, char *, struct cidr *);
+struct cidr    *collapse_blacklist(struct bl *, size_t, u_int *);
+int             configure_spamd(u_short, char *, char *, struct cidr *, u_int);
 int             configure_pf(struct cidr *);
 int             getlist(char **, char *, struct blacklist *, struct blacklist *);
 __dead void     usage(void);
@@ -95,7 +94,7 @@ imask(u_int8_t b)
 {
        if (b == 0)
                return (0);
-       return (0xffffffff << (32 - b));
+       return (0xffffffffU << (32 - b));
 }
 
 u_int8_t
@@ -131,7 +130,7 @@ maxdiff(u_int32_t a, u_int32_t b)
 }
 
 struct cidr *
-range2cidrlist(struct cidr *list, int *cli, int *cls, u_int32_t start,
+range2cidrlist(struct cidr *list, u_int *cli, u_int *cls, u_int32_t start,
     u_int32_t end)
 {
        u_int8_t maxsize, diff;
@@ -536,9 +535,10 @@ cmpbl(const void *a, const void *b)
  * printable form to pfctl or spamd.
  */
 struct cidr *
-collapse_blacklist(struct bl *bl, size_t blc)
+collapse_blacklist(struct bl *bl, size_t blc, u_int *clc)
 {
-       int bs = 0, ws = 0, state=0, cli, cls, i;
+       int bs = 0, ws = 0, state=0;
+       u_int cli, cls, i;
        u_int32_t bstart = 0;
        struct cidr *cl;
        int laststate;
@@ -579,12 +579,13 @@ collapse_blacklist(struct bl *bl, size_t blc)
                laststate = state;
        }
        cl[cli].addr = 0;
+       *clc = cli;
        return (cl);
 }
 
 int
 configure_spamd(u_short dport, char *name, char *message,
-    struct cidr *blacklists)
+    struct cidr *blacklists, u_int count)
 {
        int lport = IPPORT_RESERVED - 1, s;
        struct sockaddr_in sin;
@@ -605,8 +606,9 @@ configure_spamd(u_short dport, char *name, char *message,
                close(s);
                return (-1);
        }
-       fprintf(sdc, "%s", name);
+       fputs(name, sdc);
        do_message(sdc, message);
+       fprintf(sdc, ";inet;%u", count);
        while (blacklists->addr != 0) {
                fprintf(sdc, ";%s/%u", atop(blacklists->addr),
                    blacklists->bits);
@@ -757,14 +759,15 @@ void
 send_blacklist(struct blacklist *blist, in_port_t port)
 {
        struct cidr *cidrs;
+       u_int clc;
 
        if (blist->blc > 0) {
-               cidrs = collapse_blacklist(blist->bl, blist->blc);
+               cidrs = collapse_blacklist(blist->bl, blist->blc, &clc);
                if (cidrs == NULL)
                        errx(1, "malloc failed");
                if (!dryrun) {
                        if (configure_spamd(port, blist->name,
-                           blist->message, cidrs) == -1)
+                           blist->message, cidrs, clc) == -1)
                                err(1, "Can't connect to spamd on port %d",
                                    port);
                        if (!greyonly && configure_pf(cidrs) == -1)
index 4b8bf36..71b9f35 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: grey.c,v 1.57 2014/11/23 21:19:47 guenther Exp $      */
+/*     $OpenBSD: grey.c,v 1.58 2015/01/13 21:42:59 millert Exp $       */
 
 /*
  * Copyright (c) 2004-2006 Bob Beck.  All rights reserved.
@@ -58,7 +58,7 @@ int server_lookup4(struct sockaddr_in *, struct sockaddr_in *,
 int server_lookup6(struct sockaddr_in6 *, struct sockaddr_in6 *,
     struct sockaddr_in6 *);
 
-void   configure_spamd(char **, size_t, FILE *);
+void   configure_spamd(char **, u_int, FILE *);
 int    server_lookup(struct sockaddr *, struct sockaddr *,
            struct sockaddr *);
 int    configure_pf(char **, int);
@@ -76,8 +76,8 @@ int   greyreader(void);
 void   greyscanner(void);
 
 
-size_t whitecount, whitealloc;
-size_t trapcount, trapalloc;
+u_int whitecount, whitealloc;
+u_int trapcount, trapalloc;
 char **whitelist;
 char **traplist;
 
@@ -137,17 +137,18 @@ sig_term_chld(int sig)
  * host hits.
  */
 void
-configure_spamd(char **addrs, size_t count, FILE *sdc)
+configure_spamd(char **addrs, u_int count, FILE *sdc)
 {
-       size_t i;
+       u_int i;
 
+       /* XXX - doesn't support IPV6 yet */
        fprintf(sdc, "%s;", traplist_name);
        if (count != 0) {
-               fprintf(sdc, "%s;", traplist_msg);
+               fprintf(sdc, "%s;inet;%u", traplist_msg, count);
                for (i = 0; i < count; i++)
-                       fprintf(sdc, "%s/32;", addrs[i]);
+                       fprintf(sdc, ";%s/32", addrs[i]);
        }
-       fprintf(sdc, "\n");
+       fputc('\n', sdc);
        if (fflush(sdc) == EOF)
                syslog_r(LOG_DEBUG, &sdata, "configure_spamd: fflush failed (%m)");
 }
index a056286..ae2826d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sdl.c,v 1.20 2015/01/08 22:10:08 millert Exp $ */
+/*     $OpenBSD: sdl.c,v 1.21 2015/01/13 21:42:59 millert Exp $ */
 
 /*
  * Copyright (c) 2003-2007 Bob Beck.  All rights reserved.
 
 static void sdl_free(struct sdlist *);
 static void sdl_clear(struct sdlist *);
-int match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
-    sa_family_t af);
 
 extern int debug;
 struct sdlist *blacklists = NULL;
 int blc = 0, blu = 0;
 
 int
-sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc)
+sdl_add(char *sdname, char *sdstring, char **v4, u_int nv4, char **v6, u_int nv6)
 {
        int i, idx = -1;
        char astring[40];
+       char *addr = NULL;
        unsigned int maskbits;
-       struct sdaddr *m, *n;
 
        /*
         * if a blacklist of same tag name is already there, replace it,
@@ -67,12 +65,12 @@ sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc)
        }
        if (idx != -1) {
                if (debug > 0)
-                       printf("replacing list %s; %d new entries\n",
-                           blacklists[idx].tag, addrc);
+                       printf("replacing list %s; %u new entries\n",
+                           blacklists[idx].tag, nv4 + nv6);
                sdl_free(&blacklists[idx]);
        } else {
                if (debug > 0)
-                       printf("adding list %s; %d entries\n", sdname, addrc);
+                       printf("adding list %s; %u entries\n", sdname, nv4 + nv6);
                if (blu == blc) {
                        struct sdlist *tmp;
 
@@ -92,62 +90,95 @@ sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc)
        if ((blacklists[idx].string = strdup(sdstring)) == NULL)
                goto misc_error;
 
-       blacklists[idx].naddrs = addrc;
-
        /*
-        * Cycle through addrs, converting. We assume they are correctly
-        * formatted v4 and v6 addrs, if they don't all convert correctly, the
-        * add fails. Each address should be address/maskbits
+        * Cycle through addrs by family, converting. We assume they are
+        * correctly formatted v4 and v6 addrs, if they don't all convert
+        * correctly, the add fails. Each address should be address/maskbits.
         */
-       blacklists[idx].addrs = calloc(addrc, sizeof(struct sdentry));
-       if (blacklists[idx].addrs == NULL)
-               goto misc_error;
+       if (nv4 != 0) {
+               blacklists[idx].v4.naddrs = nv4;
+               blacklists[idx].v4.addrs = reallocarray(NULL, nv4,
+                   sizeof(struct sdentry_v4));
+               if (blacklists[idx].v4.addrs == NULL)
+                       goto misc_error;
+               for (i = 0; i < nv4; i++) {
+                       struct in_addr *m, *n;
+                       int j;
 
-       for (i = 0; i < addrc; i++) {
-               int j, k, af;
-
-               n = &blacklists[idx].addrs[i].sda;
-               m = &blacklists[idx].addrs[i].sdm;
-
-               j = sscanf(addrs[i], "%39[^/]/%u", astring, &maskbits);
-               if (j != 2)
-                       goto parse_error;
-               if (maskbits > 128)
-                       goto parse_error;
-               /*
-                * sanity check! we don't allow a 0 mask -
-                * don't blacklist the entire net.
-                */
-               if (maskbits == 0)
-                       goto parse_error;
-               if (strchr(astring, ':') != NULL)
-                       af = AF_INET6;
-               else
-                       af = AF_INET;
-               if (af == AF_INET && maskbits > 32)
-                       goto parse_error;
-               j = inet_pton(af, astring, n);
-               if (j != 1)
-                       goto parse_error;
-               if (debug > 0)
-                       printf("added %s/%u\n", astring, maskbits);
-
-               /* set mask, borrowed from pf */
-               k = 0;
-               for (j = 0; j < 4; j++)
-                       m->addr32[j] = 0;
-               while (maskbits >= 32) {
-                       m->addr32[k++] = 0xffffffff;
-                       maskbits -= 32;
+                       n = &blacklists[idx].v4.addrs[i].sda;
+                       m = &blacklists[idx].v4.addrs[i].sdm;
+
+                       addr = v4[i];
+                       j = sscanf(addr, "%15[^/]/%u", astring, &maskbits);
+                       if (j != 2)
+                               goto parse_error;
+                       /*
+                        * sanity check! we don't allow a 0 mask -
+                        * don't blacklist the entire net.
+                        */
+                       if (maskbits == 0 || maskbits > 32)
+                               goto parse_error;
+                       j = inet_pton(AF_INET, astring, n);
+                       if (j != 1)
+                               goto parse_error;
+                       if (debug > 0)
+                               printf("added %s/%u\n", astring, maskbits);
+
+                       /* set mask. */
+                       m->s_addr = 0xffffffffU << (32 - maskbits);
+                       m->s_addr = htonl(m->s_addr);
+
+                       /* mask off address bits that won't ever be used */
+                       n->s_addr = n->s_addr & m->s_addr;
+               }
+       }
+       if (nv6 != 0) {
+               blacklists[idx].v6.naddrs = nv6;
+               blacklists[idx].v6.addrs = reallocarray(NULL, nv6,
+                   sizeof(struct sdentry_v6));
+               if (blacklists[idx].v6.addrs == NULL)
+                       goto misc_error;
+
+               for (i = 0; i < nv6; i++) {
+                       int j, k;
+                       struct sdaddr_v6 *m, *n;
+
+                       n = &blacklists[idx].v6.addrs[i].sda;
+                       m = &blacklists[idx].v6.addrs[i].sdm;
+
+                       addr = v6[i];
+                       j = sscanf(addr, "%39[^/]/%u", astring, &maskbits);
+                       if (j != 2)
+                               goto parse_error;
+                       /*
+                        * sanity check! we don't allow a 0 mask -
+                        * don't blacklist the entire net.
+                        */
+                       if (maskbits == 0 || maskbits > 128)
+                               goto parse_error;
+                       j = inet_pton(AF_INET6, astring, n);
+                       if (j != 1)
+                               goto parse_error;
+                       if (debug > 0)
+                               printf("added %s/%u\n", astring, maskbits);
+
+                       /* set mask, borrowed from pf */
+                       k = 0;
+                       for (j = 0; j < 4; j++)
+                               m->addr32[j] = 0;
+                       while (maskbits >= 32) {
+                               m->addr32[k++] = 0xffffffffU;
+                               maskbits -= 32;
+                       }
+                       for (j = 31; j > 31 - maskbits; --j)
+                               m->addr32[k] |= (1 << j);
+                       if (maskbits)
+                               m->addr32[k] = htonl(m->addr32[k]);
+
+                       /* mask off address bits that won't ever be used */
+                       for (j = 0; j < 4; j++)
+                               n->addr32[j] = n->addr32[j] & m->addr32[j];
                }
-               for (j = 31; j > 31 - maskbits; --j)
-                       m->addr32[k] |= (1 << j);
-               if (maskbits)
-                       m->addr32[k] = htonl(m->addr32[k]);
-
-               /* mask off address bits that won't ever be used */
-               for (j = 0; j < 4; j++)
-                       n->addr32[j] = n->addr32[j] & m->addr32[j];
        }
        if (idx == blu) {
                blu++;
@@ -156,7 +187,7 @@ sdl_add(char *sdname, char *sdstring, char ** addrs, int addrc)
        return (0);
  parse_error:
        if (debug > 0)
-               printf("sdl_add: parse error, \"%s\"\n", addrs[i]);
+               printf("sdl_add: parse error, \"%s\"\n", addr);
  misc_error:
        sdl_free(&blacklists[idx]);
        if (idx != blu) {
@@ -181,11 +212,15 @@ sdl_del(char *sdname)
        if (idx != -1) {
                if (debug > 0)
                        printf("clearing list %s\n", sdname);
+               /* Must preserve tag. */
                free(blacklists[idx].string);
-               free(blacklists[idx].addrs);
+               free(blacklists[idx].v4.addrs);
+               free(blacklists[idx].v6.addrs);
                blacklists[idx].string = NULL;
-               blacklists[idx].addrs = NULL;
-               blacklists[idx].naddrs = 0;
+               blacklists[idx].v4.addrs = NULL;
+               blacklists[idx].v6.addrs = NULL;
+               blacklists[idx].v4.naddrs = 0;
+               blacklists[idx].v6.naddrs = 0;
        }
 }
 
@@ -194,74 +229,62 @@ sdl_del(char *sdname)
  * otherwise return 0. It is assumed that address a has been
  * pre-masked out, we only need to mask b.
  */
-int
-match_addr(struct sdaddr *a, struct sdaddr *m, struct sdaddr *b,
-    sa_family_t af)
+static int
+match_addr_v4(struct in_addr *a, struct in_addr *m, struct in_addr *b)
 {
-       int     match = 0;
-
-       switch (af) {
-       case AF_INET:
-               if ((a->addr32[0]) ==
-                   (b->addr32[0] & m->addr32[0]))
-                       match++;
-               break;
-       case AF_INET6:
-               if (((a->addr32[0]) ==
-                   (b->addr32[0] & m->addr32[0])) &&
-                   ((a->addr32[1]) ==
-                   (b->addr32[1] & m->addr32[1])) &&
-                   ((a->addr32[2]) ==
-                   (b->addr32[2] & m->addr32[2])) &&
-                   ((a->addr32[3]) ==
-                   (b->addr32[3] & m->addr32[3])))
-                       match++;
-               break;
-       }
-       return (match);
+       if (a->s_addr == (b->s_addr & m->s_addr))
+               return (1);
+       return (0);
 }
 
-
 /*
- * Given an address and address family
- * return list of pointers to matching nodes. or NULL if none.
+ * Return 1 if the addresses a (with mask m) matches address b
+ * otherwise return 0. It is assumed that address a has been
+ * pre-masked out, we only need to mask b.
  */
-struct sdlist **
-sdl_lookup(struct sdlist *head, int af, void * src)
+static int
+match_addr_v6(struct sdaddr_v6 *a, struct sdaddr_v6 *m, struct sdaddr_v6 *b)
+{
+       if (((a->addr32[0]) == (b->addr32[0] & m->addr32[0])) &&
+           ((a->addr32[1]) == (b->addr32[1] & m->addr32[1])) &&
+           ((a->addr32[2]) == (b->addr32[2] & m->addr32[2])) &&
+           ((a->addr32[3]) == (b->addr32[3] & m->addr32[3])))
+               return (1);
+       return (0);
+}
+
+#define grow_sdlist(sd, c, l) do {                                            \
+       if (c == l) {                                                          \
+               struct sdlist **tmp;                                           \
+                                                                              \
+               tmp = reallocarray(sd, l + 128, sizeof(struct sdlist *));      \
+               if (tmp == NULL) {                                             \
+                       /*                                                     \
+                        * XXX out of memory - return what we have             \
+                        */                                                    \
+                       return (sdnew);                                        \
+               }                                                              \
+               sd = tmp;                                                      \
+               l += 128;                                                      \
+       }                                                                      \
+} while (0)
+
+static struct sdlist **
+sdl_lookup_v4(struct sdlist *sdl, struct in_addr *src)
 {
+       struct sdentry_v4 *entry;
        int i, matches = 0;
-       struct sdlist *sdl;
-       struct sdentry *sda;
-       struct sdaddr *source = (struct sdaddr *) src;
        int sdnewlen = 0;
        struct sdlist **sdnew = NULL;
 
-       if (head == NULL)
-               return (NULL);
-       else
-               sdl = head;
        while (sdl->tag != NULL) {
-               for (i = 0; i < sdl->naddrs; i++) {
-                       sda = sdl->addrs + i;
-                       if (match_addr(&sda->sda, &sda->sdm, source, af)) {
-                               if (matches == sdnewlen) {
-                                       struct sdlist **tmp;
-
-                                       tmp = reallocarray(sdnew,
-                                           sdnewlen + 128,
-                                           sizeof(struct sdlist *));
-                                       if (tmp == NULL)
-                                               /*
-                                                * XXX out of memory -
-                                                * return what we have
-                                                */
-                                               return (sdnew);
-                                       sdnew = tmp;
-                                       sdnewlen += 128;
-                               }
-                               sdnew[matches]= sdl;
+               for (i = 0; i < sdl->v4.naddrs; i++) {
+                       entry = &sdl->v4.addrs[i];
+                       if (match_addr_v4(&entry->sda, &entry->sdm, src)) {
+                               grow_sdlist(sdnew, matches, sdnewlen);
+                               sdnew[matches] = sdl;
                                matches++;
-                               sdnew[matches]=NULL;
+                               sdnew[matches] = NULL;
                                break;
                        }
                }
@@ -270,12 +293,57 @@ sdl_lookup(struct sdlist *head, int af, void * src)
        return (sdnew);
 }
 
+static struct sdlist **
+sdl_lookup_v6(struct sdlist *sdl, struct sdaddr_v6 *src)
+{
+       struct sdentry_v6 *entry;
+       int i, matches = 0;
+       int sdnewlen = 0;
+       struct sdlist **sdnew = NULL;
+
+       while (sdl->tag != NULL) {
+               for (i = 0; i < sdl->v6.naddrs; i++) {
+                       entry = &sdl->v6.addrs[i];
+                       if (match_addr_v6(&entry->sda, &entry->sdm, src)) {
+                               grow_sdlist(sdnew, matches, sdnewlen);
+                               sdnew[matches] = sdl;
+                               matches++;
+                               sdnew[matches] = NULL;
+                               break;
+                       }
+               }
+               sdl++;
+       }
+       return (sdnew);
+}
+
+/*
+ * Given an address and address family
+ * return list of pointers to matching nodes. or NULL if none.
+ */
+struct sdlist **
+sdl_lookup(struct sdlist *head, int af, void *src)
+{
+       if (head == NULL)
+               return (NULL);
+
+       switch (af) {
+       case AF_INET:
+               return (sdl_lookup_v4(head, src));
+       case AF_INET6:
+               return (sdl_lookup_v6(head, src));
+       default:
+               return (NULL);
+       }
+}
+
 static void
 sdl_free(struct sdlist *sdl)
 {
        free(sdl->tag);
        free(sdl->string);
-       free(sdl->addrs);
+       free(sdl->v4.addrs);
+       free(sdl->v6.addrs);
        sdl_clear(sdl);
 }
 
@@ -284,7 +352,8 @@ sdl_clear(struct sdlist *sdl)
 {
        sdl->tag = NULL;
        sdl->string = NULL;
-       sdl->addrs = NULL;
-       sdl->naddrs = 0;
+       sdl->v4.addrs = NULL;
+       sdl->v4.naddrs = 0;
+       sdl->v6.addrs = NULL;
+       sdl->v6.naddrs = 0;
 }
-
index c50370b..86cd388 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sdl.h,v 1.6 2007/11/03 19:16:07 beck Exp $ */
+/*     $OpenBSD: sdl.h,v 1.7 2015/01/13 21:42:59 millert Exp $ */
 
 /*
  * Copyright (c) 2003-2007 Bob Beck.  All rights reserved.
 #include <sys/types.h>
 #include <sys/socket.h>
 
-/* spamd source list */
-struct sdlist {
-       char *tag;      /* sdlist source name */
-       char *string;   /* Format (451) string with no smtp code or \r\n */
-       struct sdentry *addrs;
-       size_t naddrs;
+/* spamd netblock (black) list entry (ipv4) */
+struct sdentry_v4 {
+       struct in_addr sda;
+       struct in_addr sdm;
 };
 
-/* yeah. Stolen from pf */
-struct sdaddr {
+struct sdentries_v4 {
+       struct sdentry_v4 *addrs;
+       u_int naddrs;
+};
+
+struct sdaddr_v6 {
        union {
-               struct in_addr          v4;
-               struct in6_addr         v6;
-               u_int8_t                addr8[16];
-               u_int16_t               addr16[8];
+               struct in6_addr         addr;
                u_int32_t               addr32[4];
        } _sda;             /* 128-bit address */
-#define v4     _sda.v4
-#define v6     _sda.v6
-#define addr8  _sda.addr8
-#define addr16 _sda.addr16
-#define addr32 _sda.addr32
+#define addr32  _sda.addr32
 };
 
-/* spamd netblock (black) list */
-struct sdentry {
-       struct sdaddr sda;
-       struct sdaddr sdm;
+/* spamd netblock (black) list entry (ipv6) */
+struct sdentry_v6 {
+       struct sdaddr_v6 sda;
+       struct sdaddr_v6 sdm;
 };
 
+struct sdentries_v6 {
+       struct sdentry_v6 *addrs;
+       u_int naddrs;
+};
+
+/* spamd source list */
+struct sdlist {
+       char *tag;      /* sdlist source name */
+       char *string;   /* Format (451) string with no smtp code or \r\n */
+       struct sdentries_v4 v4;
+       struct sdentries_v6 v6;
+};
 
-extern int     sdl_add(char *, char *, char **, int);
-extern void    sdl_del(char *);
-extern struct sdlist **sdl_lookup(struct sdlist *head,
-           int af, void * src);
+int    sdl_add(char *, char *, char **, u_int, char **, u_int);
+void   sdl_del(char *);
+struct sdlist **sdl_lookup(struct sdlist *head, int af, void * src);
 
 #endif /* _SDL_H_ */
index b419ef3..e96839f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: spamd.c,v 1.118 2014/12/30 23:27:23 millert Exp $     */
+/*     $OpenBSD: spamd.c,v 1.119 2015/01/13 21:42:59 millert Exp $     */
 
 /*
  * Copyright (c) 2002-2007 Bob Beck.  All rights reserved.
@@ -29,6 +29,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
+#include <limits.h>
 #include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -184,11 +185,12 @@ grow_obuf(struct con *cp, int off)
 int
 parse_configline(char *line)
 {
-       char *cp, prev, *name, *msg;
-       static char **av = NULL;
-       static size_t ac = 0;
-       size_t au = 0;
+       char *cp, prev, *name, *msg, *tmp;
+       char **v4 = NULL, **v6 = NULL;
+       const char *errstr;
+       u_int nv4 = 0, nv6 = 0;
        int mdone = 0;
+       sa_family_t af;
 
        name = line;
 
@@ -219,11 +221,16 @@ parse_configline(char *line)
                                if (*cp == ';') {
                                        mdone = 1;
                                        *cp = '\0';
-                               } else
+                               } else {
+                                       if (debug > 0)
+                                               printf("bad message: %s\n", msg);
                                        goto parse_error;
+                               }
                        }
                        break;
                case '\0':
+                       if (debug > 0)
+                               printf("bad message: %s\n", msg);
                        goto parse_error;
                default:
                        prev = '\0';
@@ -231,35 +238,89 @@ parse_configline(char *line)
                }
        }
 
-       do {
-               if (ac == au) {
-                       char **tmp;
+       while ((tmp = strsep(&cp, ";")) != NULL) {
+               char **av;
+               u_int au, ac;
 
-                       tmp = reallocarray(av, ac + 2048, sizeof(char *));
-                       if (tmp == NULL) {
-                               free(av);
-                               av = NULL;
-                               ac = 0;
-                               return (-1);
+               if (*tmp == '\0')
+                       continue;
+
+               if (strncmp(tmp, "inet", 4) != 0)
+                       goto parse_error;
+               switch (tmp[4]) {
+               case '\0':
+                       af = AF_INET;
+                       break;
+               case '6':
+                       if (tmp[5] == '\0') {
+                               af = AF_INET6;
+                               break;
                        }
-                       av = tmp;
-                       ac += 2048;
+                       /* FALLTHROUGH */
+               default:
+                       if (debug > 0)
+                               printf("unsupported address family: %s\n", tmp);
+                       goto parse_error;
                }
-       } while ((av[au++] = strsep(&cp, ";")) != NULL);
 
-       /* toss empty last entry to allow for trailing ; */
-       while (au > 0 && (av[au - 1] == NULL || av[au - 1][0] == '\0'))
-               au--;
+               tmp = strsep(&cp, ";");
+               if (tmp == NULL) {
+                       if (debug > 0)
+                               printf("missing address count\n");
+                       goto parse_error;
+               }
+               ac = strtonum(tmp, 0, UINT_MAX, &errstr);
+               if (errstr != NULL) {
+                       if (debug > 0)
+                               printf("count \"%s\" is %s\n", tmp, errstr);
+                       goto parse_error;
+               }
 
-       if (au < 1)
+               av = reallocarray(NULL, ac, sizeof(char *));
+               for (au = 0; au < ac; au++) {
+                       tmp = strsep(&cp, ";");
+                       if (tmp == NULL) {
+                               if (debug > 0)
+                                       printf("expected %u addrs, got %u\n",
+                                           ac, au + 1);
+                               free(av);
+                               goto parse_error;
+                       }
+                       if (*tmp == '\0')
+                               continue;
+                       av[au] = tmp;
+               }
+               if (af == AF_INET) {
+                       if (debug > 0)
+                               printf("duplicate inet\n");
+                       if (v4 != NULL)
+                               goto parse_error;
+                       v4 = av;
+                       nv4 = ac;
+               } else {
+                       if (debug > 0)
+                               printf("duplicate inet6\n");
+                       if (v6 != NULL)
+                               goto parse_error;
+                       v6 = av;
+                       nv6 = ac;
+               }
+       }
+       if (nv4 == 0 && nv6 == 0) {
+               if (debug > 0)
+                       printf("no addresses\n");
                goto parse_error;
-       else
-               sdl_add(name, msg, av, au);
+       }
+       sdl_add(name, msg, v4, nv4, v6, nv6);
+       free(v4);
+       free(v6);
        return (0);
 
 parse_error:
        if (debug > 0)
-               printf("bogus config line - need 'tag;message;a/m;a/m;a/m...'\n");
+               printf("bogus config line - need 'tag;message;af;count;a/m;a/m;a/m...'\n");
+       free(v4);
+       free(v6);
        return (-1);
 }