summaryrefslogtreecommitdiffstats
path: root/spamd-setup.patch_1
diff options
context:
space:
mode:
authorMarius Halden <marius.h@lden.org>2016-01-11 01:37:26 +0100
committerMarius Halden <marius.h@lden.org>2016-01-11 01:37:26 +0100
commitafc2c4818834efb1fd45c267515ef26e674f63f8 (patch)
treeef1f8d103a0bd852f16057c2f6d7e76bb2a626fd /spamd-setup.patch_1
downloadspamd-patches-afc2c4818834efb1fd45c267515ef26e674f63f8.tar.gz
spamd-patches-afc2c4818834efb1fd45c267515ef26e674f63f8.tar.bz2
spamd-patches-afc2c4818834efb1fd45c267515ef26e674f63f8.tar.xz
initial
Diffstat (limited to 'spamd-setup.patch_1')
-rw-r--r--spamd-setup.patch_1939
1 files changed, 939 insertions, 0 deletions
diff --git a/spamd-setup.patch_1 b/spamd-setup.patch_1
new file mode 100644
index 0000000..08b37cc
--- /dev/null
+++ b/spamd-setup.patch_1
@@ -0,0 +1,939 @@
+Index: spamd-setup.c
+===================================================================
+RCS file: /cvs/src/libexec/spamd-setup/spamd-setup.c,v
+retrieving revision 1.47
+diff -u -p -u -r1.47 spamd-setup.c
+--- spamd-setup.c 12 Dec 2015 20:09:28 -0000 1.47
++++ spamd-setup.c 10 Jan 2016 19:23:12 -0000
+@@ -30,6 +30,7 @@
+ #include <errno.h>
+ #include <fcntl.h>
+ #include <stdio.h>
++#include <stdint.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <unistd.h>
+@@ -43,12 +44,20 @@
+ #define SPAMD_ARG_MAX 256 /* max # of args to an exec */
+
+ struct cidr {
+- u_int32_t addr;
++ sa_family_t ai_family;
++ union {
++ u_int32_t addr4;
++ u_int8_t addr6[16];
++ } addr;
+ u_int8_t bits;
+ };
+
+ struct bl {
+- u_int32_t addr;
++ sa_family_t ai_family;
++ union {
++ u_int32_t addr4;
++ u_int8_t addr6[16];
++ } addr;
+ int8_t b;
+ int8_t w;
+ };
+@@ -58,6 +67,8 @@ struct blacklist {
+ char *message;
+ struct bl *bl;
+ size_t blc, bls;
++ struct bl *bl6;
++ size_t blc6, bls6;
+ u_int8_t black;
+ };
+
+@@ -66,19 +77,30 @@ 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 *, u_int *, u_int *, u_int32_t,
+ u_int32_t);
++struct cidr *range2cidrlist6(struct cidr *, u_int *, u_int *, u_int8_t *,
++ u_int8_t *);
+ void cidr2range(struct cidr, u_int32_t *, u_int32_t *);
++void cidr2range6(struct cidr, u_int8_t *, u_int8_t *);
+ char *atop(u_int32_t);
+-int parse_netblock(char *, struct bl *, struct bl *, int);
++int parse_netblock(char *, struct bl *, struct bl *, struct bl*,
++ struct bl *, int);
+ int open_child(char *, char **);
+ int fileget(char *);
+ int open_file(char *, char *);
+ char *fix_quoted_colons(char *);
+ void do_message(FILE *, char *);
+-struct bl *add_blacklist(struct bl *, size_t *, size_t *, gzFile, int);
++int add_blacklist(struct bl **, size_t *, size_t *, struct bl **,
++ size_t *, size_t *, gzFile, int);
++int cmp_ipv6(const u_int8_t *, const u_int8_t *);
+ int cmpbl(const void *, const void *);
+-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 cmpbl6(const void *, const void *);
++int collapse_blacklist(struct bl *, size_t, struct bl *, size_t,
++ struct cidr **, u_int *, struct cidr **, u_int *);
++struct cidr *collapse_blacklist4(struct bl *, size_t, u_int *);
++struct cidr *collapse_blacklist6(struct bl *, size_t, u_int *);
++int configure_spamd(u_short, char *, char *, struct cidr *, u_int,
++ struct cidr *, u_int);
++int configure_pf(struct cidr *, u_int, struct cidr *, u_int);
+ int getlist(char **, char *, struct blacklist *, struct blacklist *);
+ __dead void usage(void);
+
+@@ -89,6 +111,74 @@ int greyonly = 1;
+ extern char *__progname;
+
+ #define MAXIMUM(a,b) (((a)>(b))?(a):(b))
++#define MINIMUM(a,b) (((a)<(b))?(a):(b))
++
++void
++ipv6_add(u_int8_t *addr, u_int8_t num)
++{
++ int i;
++ u_int8_t tmp;
++
++ for (i = 15; i >= 0; i--) {
++ if (num == 0)
++ break;
++
++ if (num > (UINT8_MAX - addr[i]))
++ tmp = 1;
++ else
++ tmp = 0;
++
++ addr[i] += num;
++
++ num = tmp;
++ }
++}
++
++void
++ipv6_add2(u_int8_t *addr, u_int8_t *src)
++{
++ int i, j;
++ u_int8_t tmp, num;
++
++ for (i = 15; i >= 0; i--) {
++ num = src[i];
++
++ for (j = i; j >= 0; j--) {
++ if (num == 0)
++ break;
++
++ if (num > (UINT8_MAX - addr[j]))
++ tmp = 1;
++ else
++ tmp = 0;
++
++ addr[j] += num;
++
++ num = tmp;
++ }
++ }
++}
++
++void
++ipv6_sub(u_int8_t *addr, u_int8_t num)
++{
++ int i;
++ u_int8_t tmp;
++
++ for (i = 15; i >= 0; i--) {
++ if (num == 0)
++ break;
++
++ if (num > addr[i])
++ tmp = 1;
++ else
++ tmp = 0;
++
++ addr[i] -= num;
++
++ num = tmp;
++ }
++}
+
+ u_int32_t
+ imask(u_int8_t b)
+@@ -98,6 +188,24 @@ imask(u_int8_t b)
+ return (0xffffffffU << (32 - b));
+ }
+
++void
++imask6(u_int8_t *m, u_int8_t b)
++{
++ int i;
++ u_int8_t tmp;
++ memset(m, 0, sizeof(u_int8_t) * 16);
++
++ for (i = 0; i < 16; i++) {
++ if (b == 0)
++ break;
++
++ tmp = MINIMUM(b, 8);
++ b -= tmp;
++
++ m[i] = (0xffU << (8 - tmp));
++ }
++}
++
+ u_int8_t
+ maxblock(u_int32_t addr, u_int8_t bits)
+ {
+@@ -114,6 +222,26 @@ maxblock(u_int32_t addr, u_int8_t bits)
+ }
+
+ u_int8_t
++maxblock6(u_int8_t *addr, u_int8_t bits)
++{
++ u_int8_t m[16];
++ int i;
++
++ while (bits > 0) {
++ imask6(m, bits - 1);
++
++ for (i = 0; i < 16; i++) {
++ if ((addr[i] & m[i]) != addr[i]) {
++ return bits;
++ }
++ }
++
++ bits--;
++ }
++ return (bits);
++}
++
++u_int8_t
+ maxdiff(u_int32_t a, u_int32_t b)
+ {
+ u_int8_t bits = 0;
+@@ -130,6 +258,28 @@ maxdiff(u_int32_t a, u_int32_t b)
+ return (bits);
+ }
+
++u_int8_t
++maxdiff6(u_int8_t *a, u_int8_t *b)
++{
++ u_int8_t bits = 0;
++ u_int8_t m[16], tmp[16];
++ int i;
++
++ memcpy(tmp, b, sizeof(tmp));
++
++ ipv6_add(tmp, 1);
++ while (bits < 128) {
++ imask6(m, bits);
++
++ for (i = 0; i < 15; i++) {
++ if ((a[i] & m[i]) != (tmp[i] & m[i]))
++ return (bits);
++ }
++ bits++;
++ }
++ return (bits);
++}
++
+ struct cidr *
+ range2cidrlist(struct cidr *list, u_int *cli, u_int *cls, u_int32_t start,
+ u_int32_t end)
+@@ -150,7 +300,7 @@ range2cidrlist(struct cidr *list, u_int
+ list = tmp;
+ *cls += 32;
+ }
+- list[*cli].addr = start;
++ list[*cli].addr.addr4 = start;
+ list[*cli].bits = maxsize;
+ (*cli)++;
+ start = start + (1 << (32 - maxsize));
+@@ -158,11 +308,102 @@ range2cidrlist(struct cidr *list, u_int
+ return (list);
+ }
+
++struct cidr *
++range2cidrlist6(struct cidr *list, u_int *cli, u_int *cls, u_int8_t *start,
++ u_int8_t *end)
++{
++ u_int8_t maxsize, diff, btmp;
++ struct cidr *tmp;
++ int i;
++
++ while (cmp_ipv6(end, start) >= 0) {
++ /*{ // XXX
++ char s[INET6_ADDRSTRLEN], e[INET6_ADDRSTRLEN];
++
++ inet_ntop(AF_INET6, start, s, sizeof(s));
++ inet_ntop(AF_INET6, end, e, sizeof(e));
++
++ fprintf(stderr, "DEBUG: s=%s, e=%s\n", s, e);
++ }*/
++ maxsize = maxblock6(start, 128);
++ diff = maxdiff6(start, end);
++
++ maxsize = MAXIMUM(maxsize, diff);
++ if (*cls <= *cli + 1) {
++ tmp = reallocarray(list, *cls + 32,
++ sizeof(struct cidr));
++ if (tmp == NULL)
++ err(1, NULL);
++ list = tmp;
++ *cls += 32;
++ }
++ memcpy(list[*cli].addr.addr6, start,
++ sizeof(list[*cli].addr.addr6));
++ list[*cli].bits = maxsize;
++ (*cli)++;
++
++ maxsize = 128 - maxsize;
++ u_int8_t mask[16];
++ memset(mask, 0, sizeof(mask));
++
++ for (i = 15; i >= 0; i--) {
++ btmp = ((maxsize < 8) ? maxsize : 8);
++ maxsize -= btmp;
++
++ mask[i] = ((1 << btmp) - 1);
++
++ if (maxsize == 0)
++ break;
++ }
++
++ ipv6_add(mask, 1);
++ ipv6_add2(start, mask);
++
++ /* { // XXX
++ char m[INET6_ADDRSTRLEN];
++
++ inet_ntop(AF_INET6, mask, m, sizeof(m));
++
++ fprintf(stderr, "DEBUG: m=%s\n", m);
++ }*/
++ }
++
++ return (list);
++}
++
+ void
+ cidr2range(struct cidr cidr, u_int32_t *start, u_int32_t *end)
+ {
+- *start = cidr.addr;
+- *end = cidr.addr + (1 << (32 - cidr.bits)) - 1;
++ *start = cidr.addr.addr4;
++ *end = cidr.addr.addr4 + (1 << (32 - cidr.bits)) - 1;
++}
++
++void
++cidr2range6(struct cidr cidr, u_int8_t *start, u_int8_t *end)
++{
++ int i;
++ u_int8_t tmp, bits = 128 - cidr.bits;
++ u_int8_t mask[16];
++ memset(mask, 0, sizeof(mask));
++
++ for (i = 0; i < 16; i++)
++ end[i] = start[i] = cidr.addr.addr6[i];
++ //start[i] = cidr.addr.addr6[i];
++
++ for (i = 15; i >= 0; i--) {
++ if (bits == 0) {
++ //end[i] = start[i];
++ break;
++ } else {
++ tmp = MINIMUM(bits, 8);
++ bits -= tmp;
++
++ //end[i] = start[i] + ((1 << tmp) - 1); // XXX
++ mask[i] = ((1 << tmp) - 1);
++ }
++ }
++
++ ipv6_add2(end, mask);
+ }
+
+ char *
+@@ -176,11 +417,13 @@ atop(u_int32_t addr)
+ }
+
+ int
+-parse_netblock(char *buf, struct bl *start, struct bl *end, int white)
++parse_netblock(char *buf, struct bl *start, struct bl *end, struct bl *start6,
++ struct bl *end6, int white)
+ {
+- char astring[16], astring2[16];
++ char astring[40], astring2[40];
+ unsigned maskbits;
+ struct cidr c;
++ int ret = 0;
+
+ /* skip leading spaces */
+ while (*buf == ' ')
+@@ -189,56 +432,130 @@ parse_netblock(char *buf, struct bl *sta
+ if (*buf == '#')
+ return (0);
+ /* otherwise, look for a netblock of some sort */
+- if (sscanf(buf, "%15[^/]/%u", astring, &maskbits) == 2) {
+- /* looks like a cidr */
++ if (sscanf(buf, "%15[0123456789.]/%u", astring, &maskbits) == 2) {
++ /* looks like IPv4 cidr */
+ memset(&c.addr, 0, sizeof(c.addr));
+- if (inet_net_pton(AF_INET, astring, &c.addr, sizeof(c.addr))
+- == -1)
++ if (inet_net_pton(AF_INET, astring, &c.addr.addr4,
++ sizeof(c.addr.addr4)) == -1)
+ return (0);
+- c.addr = ntohl(c.addr);
++ c.ai_family = AF_INET;
++ c.addr.addr4 = ntohl(c.addr.addr4);
+ if (maskbits > 32)
+ return (0);
+ c.bits = maskbits;
+- cidr2range(c, &start->addr, &end->addr);
+- end->addr += 1;
++ cidr2range(c, &start->addr.addr4, &end->addr.addr4);
++ start->ai_family = end->ai_family = AF_INET;
++ end->addr.addr4 += 1;
++
++ ret = AF_INET;
++ } else if (sscanf(buf, "%39[0123456789ABCDEFabcdef:]/%u",
++ astring, &maskbits) == 2) {
++ /* looks like IPv6 cidr */
++ memset(&c.addr, 0, sizeof(c.addr));
++ if (inet_net_pton(AF_INET6, astring, &c.addr.addr6,
++ sizeof(c.addr.addr6)) == -1)
++ return (0);
++ c.ai_family = AF_INET6;
++ if (maskbits > 128)
++ return (0);
++ c.bits = maskbits;
++ cidr2range6(c, start6->addr.addr6, end6->addr.addr6);
++ start6->ai_family = end6->ai_family = AF_INET6;
++ ipv6_add(end6->addr.addr6, 1);
++
++ ret = AF_INET6;
+ } else if (sscanf(buf, "%15[0123456789.]%*[ -]%15[0123456789.]",
+ astring, astring2) == 2) {
+- /* looks like start - end */
++ /* looks like IPv4 start - end */
+ memset(&start->addr, 0, sizeof(start->addr));
+ memset(&end->addr, 0, sizeof(end->addr));
+- if (inet_net_pton(AF_INET, astring, &start->addr,
+- sizeof(start->addr)) == -1)
++ if (inet_net_pton(AF_INET, astring, &start->addr.addr4,
++ sizeof(start->addr.addr4)) == -1)
+ return (0);
+- start->addr = ntohl(start->addr);
+- if (inet_net_pton(AF_INET, astring2, &end->addr,
+- sizeof(end->addr)) == -1)
++ start->ai_family = AF_INET;
++ start->addr.addr4 = ntohl(start->addr.addr4);
++ if (inet_net_pton(AF_INET, astring2, &end->addr.addr4,
++ sizeof(end->addr.addr4)) == -1)
+ return (0);
+- end->addr = ntohl(end->addr) + 1;
++ end->ai_family = AF_INET;
++ end->addr.addr4 = ntohl(end->addr.addr4) + 1;
+ if (start > end)
+ return (0);
++
++ ret = AF_INET;
++ } else if (sscanf(buf, "%39[0123456789ABCDEFabcdef:]%*[ -]"
++ "%39[0123456789ABCDEFabcdef:]", astring, astring2) == 2) {
++ /* loks like IPv6 start - end */
++ memset(&start6->addr, 0, sizeof(start6->addr));
++ memset(&end6->addr, 0, sizeof(end6->addr));
++ if (inet_net_pton(AF_INET6, astring, &start6->addr.addr6,
++ sizeof(start6->addr.addr6)) == -1)
++ return (0);
++ start6->ai_family = AF_INET6;
++ if (inet_net_pton(AF_INET6, astring2, &end6->addr.addr6,
++ sizeof(end6->addr.addr6)) == -1)
++ return (0);
++ end6->ai_family = AF_INET6;
++ ipv6_add(end6->addr.addr6, 1);
++ if (start6 > end6)
++ return (0);
++
++ ret = AF_INET6;
+ } else if (sscanf(buf, "%15[0123456789.]", astring) == 1) {
+- /* just a single address */
++ /* just a single IPv4 address */
+ memset(&start->addr, 0, sizeof(start->addr));
+- if (inet_net_pton(AF_INET, astring, &start->addr,
+- sizeof(start->addr)) == -1)
++ if (inet_net_pton(AF_INET, astring, &start->addr.addr4,
++ sizeof(start->addr.addr4)) == -1)
+ return (0);
+- start->addr = ntohl(start->addr);
+- end->addr = start->addr + 1;
++ start->ai_family = AF_INET;
++ end->ai_family = AF_INET;
++ start->addr.addr4 = ntohl(start->addr.addr4);
++ end->addr.addr4 = start->addr.addr4 + 1;
++
++ ret = AF_INET;
++ } else if (sscanf(buf, "%39[0123456789ABCDEFabcdef:]", astring) == 1) {
++ /* just a single IPv6 address */
++ memset(&start6->addr, 0, sizeof(start6->addr));
++ if (inet_net_pton(AF_INET6, astring, &start6->addr.addr6,
++ sizeof(start6->addr.addr6)) == -1)
++ return (0);
++ start6->ai_family = AF_INET6;
++ end6->ai_family = AF_INET6;
++ memcpy(&end6->addr.addr6, &start6->addr.addr6,
++ sizeof(start6->addr.addr6));
++
++ ipv6_add(end6->addr.addr6, 1);
++
++ ret = AF_INET6;
+ } else
+ return (0);
+
+ if (white) {
+- start->b = 0;
+- start->w = 1;
+- end->b = 0;
+- end->w = -1;
++ if (ret == AF_INET) {
++ start->b = 0;
++ start->w = 1;
++ end->b = 0;
++ end->w = -1;
++ } else {
++ start6->b = 0;
++ start6->w = 1;
++ end->b = 0;
++ end->w = -1;
++ }
+ } else {
+- start->b = 1;
+- start->w = 0;
+- end->b = -1;
+- end->w = 0;
++ if (ret == AF_INET) {
++ start->b = 1;
++ start->w = 0;
++ end->b = -1;
++ end->w = 0;
++ } else {
++ start6->b = 1;
++ start6->w = 0;
++ end6->b = -1;
++ end6->w = 0;
++ }
+ }
+- return (1);
++ return (ret);
+ }
+
+ int
+@@ -445,8 +762,9 @@ do_message(FILE *sdc, char *msg)
+ }
+
+ /* retrieve a list from fd. add to blacklist bl */
+-struct bl *
+-add_blacklist(struct bl *bl, size_t *blc, size_t *bls, gzFile gzf, int white)
++int
++add_blacklist(struct bl **bl, size_t *blc, size_t *bls, struct bl **bl6,
++ size_t *blc6, size_t *bls6, gzFile gzf, int white)
+ {
+ int i, n, start, bu = 0, bs = 0, serrno = 0;
+ char *buf = NULL, *tmp;
+@@ -481,30 +799,55 @@ add_blacklist(struct bl *bl, size_t *blc
+ /* we assume that there is an IP for every 14 bytes */
+ if (*blc + bu / 7 >= *bls) {
+ *bls += bu / 7;
+- blt = reallocarray(bl, *bls, sizeof(struct bl));
++ blt = reallocarray(*bl, *bls, sizeof(struct bl));
+ if (blt == NULL) {
+ *bls -= bu / 7;
+ serrno = errno;
+ goto bldone;
+ }
+- bl = blt;
++ *bl = blt;
++ }
++ if (*blc6 + bu / 7 >= *bls6) {
++ *bls6 += bu / 7;
++ blt = reallocarray(*bl6, *bls6, sizeof(struct bl));
++ if (blt == NULL) {
++ *bls6 -= bu / 7;
++ serrno = errno;
++ goto bldone;
++ }
++ *bl6 = blt;
+ }
+ for (i = 0; i <= bu; i++) {
+ if (*blc + 1 >= *bls) {
+ *bls += 1024;
+- blt = reallocarray(bl, *bls, sizeof(struct bl));
++ blt = reallocarray(*bl, *bls, sizeof(struct bl));
+ if (blt == NULL) {
+ *bls -= 1024;
+ serrno = errno;
+ goto bldone;
+ }
+- bl = blt;
++ *bl = blt;
++ }
++ if (*blc6 + 1 >= *bls6) {
++ *bls6 += 1024;
++ blt = reallocarray(*bl6, *bls6, sizeof(struct bl));
++ if (blt == NULL) {
++ *bls6 -= 1024;
++ serrno = errno;
++ goto bldone;
++ }
++ *bl6 = blt;
+ }
+ if (i == bu || buf[i] == '\n') {
+ buf[i] = '\0';
+- if (parse_netblock(buf + start,
+- bl + *blc, bl + *blc + 1, white))
++ n = parse_netblock(buf + start,
++ *bl + *blc, *bl + *blc + 1,
++ *bl6 + *blc6, *bl6 + *blc6 + 1, white);
++
++ if (n == AF_INET)
+ *blc += 2;
++ else if (n == AF_INET6)
++ *blc6 += 2;
+ start = i + 1;
+ }
+ }
+@@ -512,29 +855,78 @@ add_blacklist(struct bl *bl, size_t *blc
+ errno = EIO;
+ bldone:
+ free(buf);
+- if (serrno)
++ if (serrno) {
+ errno = serrno;
+- return (bl);
++ return (0);
++ }
++ return (1);;
++}
++
++int
++cmp_ipv6(const u_int8_t *a, const u_int8_t *b)
++{
++ int i;
++ for (i = 0; i < 16; i++) {
++ if (a[i] > b[i])
++ return (1);
++ if (a[i] < b[i])
++ return (-1);
++ }
++ return (0);
+ }
+
+ int
+ cmpbl(const void *a, const void *b)
+ {
+- if (((struct bl *)a)->addr > ((struct bl *) b)->addr)
++ if (((struct bl *)a)->addr.addr4 > ((struct bl *) b)->addr.addr4)
+ return (1);
+- if (((struct bl *)a)->addr < ((struct bl *) b)->addr)
++ if (((struct bl *)a)->addr.addr4 < ((struct bl *) b)->addr.addr4)
+ return (-1);
+ return (0);
+ }
+
++int
++cmpbl6(const void *a, const void *b)
++{
++ u_int8_t *addr1, *addr2;
++
++ addr1 = ((struct bl *)a)->addr.addr6;
++ addr2 = ((struct bl *)b)->addr.addr6;
++
++ return cmp_ipv6(addr1, addr2);
++}
++
+ /*
+ * collapse_blacklist takes blacklist/whitelist entries sorts, removes
+ * overlaps and whitelist portions, and returns netblocks to blacklist
+ * as lists of nonoverlapping cidr blocks suitable for feeding in
+ * printable form to pfctl or spamd.
+ */
++
++int
++collapse_blacklist(struct bl *bl, size_t blc, struct bl *bl6, size_t blc6,
++ struct cidr **cidr, u_int *clc, struct cidr **cidr6, u_int *clc6)
++{
++ if (blc == 0 && blc6 == 0)
++ return (0);
++
++ if (blc > 0) {
++ *cidr = collapse_blacklist4(bl, blc, clc);
++ if (*cidr == NULL)
++ return (0);
++ }
++
++ if (blc6 > 0) {
++ *cidr6 = collapse_blacklist6(bl6, blc6, clc6);
++ if (*cidr6 == NULL)
++ return (0);
++ }
++
++ return (1);
++}
++
+ struct cidr *
+-collapse_blacklist(struct bl *bl, size_t blc, u_int *clc)
++collapse_blacklist4(struct bl *bl, size_t blc, u_int *clc)
+ {
+ int bs = 0, ws = 0, state=0;
+ u_int cli, cls, i;
+@@ -558,13 +950,13 @@ collapse_blacklist(struct bl *bl, size_t
+ qsort(bl, blc, sizeof(struct bl), cmpbl);
+ for (i = 0; i < blc;) {
+ laststate = state;
+- addr = bl[i].addr;
++ addr = bl[i].addr.addr4;
+
+ do {
+ bs += bl[i].b;
+ ws += bl[i].w;
+ i++;
+- } while (bl[i].addr == addr);
++ } while (bl[i].addr.addr4 == addr);
+ if (state == 1 && bs == 0)
+ state = 0;
+ else if (state == 0 && bs > 0)
+@@ -581,18 +973,74 @@ collapse_blacklist(struct bl *bl, size_t
+ }
+ laststate = state;
+ }
+- cl[cli].addr = 0;
++ cl[cli].addr.addr4 = 0;
+ *clc = cli;
+ return (cl);
+ }
+
++struct cidr *
++collapse_blacklist6(struct bl *bl6, size_t blc6, u_int *clc6)
++{
++ int bs = 0, ws = 0, state = 0;
++ u_int cli, cls, i;
++ struct cidr *cl;
++ int laststate;
++ u_int8_t bstart[16];
++ u_int8_t addr[16];
++
++ memset(bstart, 0, sizeof(bstart));
++
++ if (blc6 == 0)
++ return (NULL);
++
++ cli = 0;
++ cls = (blc6 / 2) + (blc6 / 20) + 1;
++ cl = reallocarray(NULL, cls, sizeof(struct cidr));
++ if (cl == NULL)
++ return (NULL);
++ qsort(bl6, blc6, sizeof(struct bl), cmpbl6);
++
++ for (i = 0; i < blc6;) {
++ laststate = state;
++ memcpy(addr, bl6[i].addr.addr6, sizeof(addr));
++
++ do {
++ bs += bl6[i].b;
++ ws += bl6[i].w;
++ i++;
++ } while (cmp_ipv6(bl6[i].addr.addr6, addr) == 0);
++ if (state == 1 && bs == 0)
++ state = 0;
++ else if (state == 0 && bs > 0)
++ state = 1;
++ if (ws > 0)
++ state = 0;
++ if (laststate == 0 && state == 1) {
++ /* start blacklist */
++ memcpy(bstart, addr, sizeof(addr));
++ }
++ if (laststate == 1 && state == 0) {
++ /* end blacklist */
++ ipv6_sub(addr, 1);
++ cl = range2cidrlist6(cl, &cli, &cls, bstart, addr);
++ }
++ laststate = state;
++ }
++
++ memset(&cl[cli].addr.addr6, 0, sizeof(cl[cli].addr.addr6));
++ *clc6 = cli;
++
++ return (cl);
++}
++
+ int
+ configure_spamd(u_short dport, char *name, char *message,
+- struct cidr *blacklists, u_int count)
++ struct cidr *blacklists, u_int count, struct cidr *blacklists6, u_int count6)
+ {
+- int lport = IPPORT_RESERVED - 1, s;
++ int lport = IPPORT_RESERVED - 1, s, i;
+ struct sockaddr_in sin;
+ FILE* sdc;
++ char buf[INET6_ADDRSTRLEN];
+
+ s = rresvport(&lport);
+ if (s == -1)
+@@ -612,10 +1060,14 @@ configure_spamd(u_short dport, char *nam
+ 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);
+- blacklists++;
++ for (i = 0; i < count; i++) {
++ fprintf(sdc, ";%s/%u", atop(blacklists[i].addr.addr4),
++ blacklists[i].bits);
++ }
++ fprintf(sdc, ";inet6;%u", count6);
++ for (i = 0; i < count6; i++) {
++ inet_ntop(AF_INET6, blacklists6[i].addr.addr6, buf, sizeof(buf));
++ fprintf(sdc, ";%s/%u", buf, blacklists6[i].bits);
+ }
+ fputc('\n', sdc);
+ fclose(sdc);
+@@ -625,12 +1077,14 @@ configure_spamd(u_short dport, char *nam
+
+
+ int
+-configure_pf(struct cidr *blacklists)
++configure_pf(struct cidr *blacklists, u_int count, struct cidr *blacklists6,
++ u_int count6)
+ {
+ char *argv[9]= {"pfctl", "-q", "-t", "spamd", "-T", "replace",
+ "-f" "-", NULL};
++ char buf[INET6_ADDRSTRLEN];
+ static FILE *pf = NULL;
+- int pdes[2];
++ int pdes[2], i;
+
+ if (pf == NULL) {
+ if (pipe(pdes) != 0)
+@@ -659,10 +1113,13 @@ configure_pf(struct cidr *blacklists)
+ return (-1);
+ }
+ }
+- while (blacklists->addr != 0) {
+- fprintf(pf, "%s/%u\n", atop(blacklists->addr),
+- blacklists->bits);
+- blacklists++;
++ for (i = 0; i < count; i++) {
++ fprintf(pf, "%s/%u\n", atop(blacklists[i].addr.addr4),
++ blacklists[i].bits);
++ }
++ for (i = 0; i < count; i++) {
++ inet_ntop(AF_INET6, blacklists6[i].addr.addr6, buf, sizeof(buf));
++ fprintf(pf, "%s/%u\n", buf, blacklists6[i].bits);
+ }
+ return (0);
+ }
+@@ -672,9 +1129,10 @@ getlist(char ** db_array, char *name, st
+ struct blacklist *blistnew)
+ {
+ char *buf, *method, *file, *message;
+- int fd, black = 0, serror;
+- size_t blc, bls;
++ int fd, black = 0, serror, ret;
++ size_t blc, bls, blc6, bls6;
+ struct bl *bl = NULL;
++ struct bl *bl6 = NULL;
+ gzFile gzf;
+
+ if (cgetent(&buf, db_array, name) != 0)
+@@ -686,12 +1144,18 @@ getlist(char ** db_array, char *name, st
+ blc = blistnew->blc;
+ bls = blistnew->bls;
+ bl = blistnew->bl;
++ blc6 = blistnew->blc6;
++ bls6 = blistnew->bls6;
++ bl6 = blistnew->bl6;
+ } else if (cgetcap(buf, "white", ':') != NULL) {
+ /* apply to most recent blacklist */
+ black = 0;
+ blc = blist->blc;
+ bls = blist->bls;
+ bl = blist->bl;
++ blc6 = blist->blc6;
++ bls6 = blist->bls6;
++ bl6 = blist->bl6;
+ } else
+ errx(1, "Must have \"black\" or \"white\" in %s", name);
+
+@@ -730,10 +1194,10 @@ getlist(char ** db_array, char *name, st
+ errx(1, "gzdopen");
+ }
+ free(buf);
+- bl = add_blacklist(bl, &blc, &bls, gzf, !black);
++ ret = add_blacklist(&bl, &blc, &bls, &bl6, &blc6, &bls6, gzf, !black);
+ serror = errno;
+ gzclose(gzf);
+- if (bl == NULL) {
++ if (!ret) {
+ errno = serror;
+ warn("Could not add %slist %s", black ? "black" : "white",
+ name);
+@@ -742,13 +1206,16 @@ getlist(char ** db_array, char *name, st
+ if (black) {
+ if (debug)
+ fprintf(stderr, "blacklist %s %zu entries\n",
+- name, blc / 2);
++ name, ((blc / 2) + (blc6 / 2)));
+ blistnew->message = message;
+ blistnew->name = name;
+ blistnew->black = black;
+ blistnew->bl = bl;
+ blistnew->blc = blc;
+ blistnew->bls = bls;
++ blistnew->bl6 = bl6;
++ blistnew->blc6 = blc6;
++ blistnew->bls6 = bls6;
+ } else {
+ /* whitelist applied to last active blacklist */
+ if (debug)
+@@ -757,6 +1224,9 @@ getlist(char ** db_array, char *name, st
+ blist->bl = bl;
+ blist->blc = blc;
+ blist->bls = bls;
++ blist->bl6 = bl6;
++ blist->blc6 = blc6;
++ blist->bls6 = bls6;
+ }
+ return (black);
+ }
+@@ -764,22 +1234,24 @@ getlist(char ** db_array, char *name, st
+ void
+ send_blacklist(struct blacklist *blist, in_port_t port)
+ {
+- struct cidr *cidrs;
+- u_int clc;
++ struct cidr *cidrs = NULL, *cidrs6 = NULL;
++ u_int clc = 0, clc6 = 0;
+
+- if (blist->blc > 0) {
+- cidrs = collapse_blacklist(blist->bl, blist->blc, &clc);
+- if (cidrs == NULL)
++ if (blist->blc > 0 || blist->blc6 > 0) {
++ if (!collapse_blacklist(blist->bl, blist->blc, blist->bl6,
++ blist->blc6, &cidrs, &clc, &cidrs6, &clc6))
+ err(1, NULL);
+ if (!dryrun) {
+ if (configure_spamd(port, blist->name,
+- blist->message, cidrs, clc) == -1)
++ blist->message, cidrs, clc, cidrs6, clc6) == -1)
+ err(1, "Can't connect to spamd on port %d",
+ port);
+- if (!greyonly && configure_pf(cidrs) == -1)
++ if (!greyonly && configure_pf(cidrs, clc, cidrs6, clc6)
++ == -1)
+ err(1, "pfctl failed");
+ }
+ free(cidrs);
++ free(cidrs6);
+ free(blist->bl);
+ }
+ }