diff options
author | Marius Halden <marius.h@lden.org> | 2016-01-11 01:37:26 +0100 |
---|---|---|
committer | Marius Halden <marius.h@lden.org> | 2016-01-11 01:37:26 +0100 |
commit | afc2c4818834efb1fd45c267515ef26e674f63f8 (patch) | |
tree | ef1f8d103a0bd852f16057c2f6d7e76bb2a626fd | |
download | spamd-patches-afc2c4818834efb1fd45c267515ef26e674f63f8.tar.gz spamd-patches-afc2c4818834efb1fd45c267515ef26e674f63f8.tar.bz2 spamd-patches-afc2c4818834efb1fd45c267515ef26e674f63f8.tar.xz |
initial
-rw-r--r-- | data_structures | 27 | ||||
-rw-r--r-- | spamd-setup.patch | 940 | ||||
-rw-r--r-- | spamd-setup.patch_1 | 939 | ||||
-rw-r--r-- | spamd.patch | 938 | ||||
-rw-r--r-- | spamlogd.patch | 78 |
5 files changed, 2922 insertions, 0 deletions
diff --git a/data_structures b/data_structures new file mode 100644 index 0000000..3a1c570 --- /dev/null +++ b/data_structures @@ -0,0 +1,27 @@ + +struct ipv6 { + union { + struct in6_addr addr; + u_int32_t addr32[4]; + } _ipv6; +#define addr32 _ipv6.addr32 +}; + +struct cidr { + sa_family_t ai_family; + union { + u_int32_t addr4; + struct ipv6 addr6; + }; + u_int8_t bits; +}; + +struct bl { + sa_family_t ai_family; + union { + u_int32_t addr4; + struct ipv6 addr6; + }; + int8_t b; + int8_t w; +}; diff --git a/spamd-setup.patch b/spamd-setup.patch new file mode 100644 index 0000000..403fc19 --- /dev/null +++ b/spamd-setup.patch @@ -0,0 +1,940 @@ +? spamd-setup +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 22:08:43 -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> +@@ -42,13 +43,29 @@ + #define PATH_SPAMD_CONF "/etc/mail/spamd.conf" + #define SPAMD_ARG_MAX 256 /* max # of args to an exec */ + ++struct ipv6 { ++ union { ++ u_int8_t addr8[16]; ++ u_int16_t addr16[8]; ++ u_int32_t addr32[4]; ++ }; ++}; ++ + struct cidr { +- u_int32_t addr; ++ //sa_family_t ai_family; ++ union { ++ u_int32_t addr4; ++ struct ipv6 addr6; ++ }; + u_int8_t bits; + }; + + struct bl { +- u_int32_t addr; ++ //sa_family_t ai_family; ++ union { ++ u_int32_t addr4; ++ struct ipv6 addr6; ++ }; + int8_t b; + int8_t w; + }; +@@ -58,6 +75,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 +85,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 *, struct ipv6 *, ++ struct ipv6 *); + void cidr2range(struct cidr, u_int32_t *, u_int32_t *); ++void cidr2range6(struct cidr, struct ipv6 *, struct ipv6 *); + 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 struct ipv6 *, const struct ipv6 *); + 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 +119,77 @@ 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(struct ipv6 *addr, u_int8_t num) ++{ ++ int i; ++ u_int8_t carry; ++ u_int32_t tmp; ++ ++ for (i = 3; i >= 0; i--) { ++ if (num == 0) ++ break; ++ ++ tmp = ntohl(addr->addr32[i]); ++ if (num > (UINT32_MAX - tmp)) ++ carry = 1; ++ else ++ carry = 0; ++ ++ addr->addr32[i] = htonl(tmp + num); ++ num = carry; ++ } ++} ++ ++void ++ipv6_add2(struct ipv6 *addr, struct ipv6 *src) ++{ ++ int i, j; ++ u_int8_t carry; ++ u_int32_t num, tmp; ++ ++ for (i = 3; i >= 0; i--) { ++ num = ntohl(src->addr32[i]); ++ ++ for (j = i; j >= 0; j--) { ++ if (num == 0) ++ break; ++ ++ tmp = ntohl(addr->addr32[j]); ++ if (num > (UINT32_MAX - tmp)) ++ carry = 1; ++ else ++ carry = 0; ++ ++ addr->addr32[j] = htonl(tmp + num); ++ num = carry; ++ } ++ } ++} ++ ++void ++ipv6_sub(struct ipv6 *addr, u_int8_t num) ++{ ++ int i; ++ u_int8_t carry; ++ u_int32_t tmp; ++ ++ for (i = 3; i >= 0; i--) { ++ if (num == 0) ++ break; ++ ++ tmp = ntohl(addr->addr32[i]); ++ if (num > tmp) ++ carry = 1; ++ else ++ carry = 0; ++ ++ addr->addr32[i] = htonl(tmp - num); ++ num = carry; ++ } ++} + + u_int32_t + imask(u_int8_t b) +@@ -98,6 +199,24 @@ imask(u_int8_t b) + return (0xffffffffU << (32 - b)); + } + ++void ++imask6(struct ipv6 *m, u_int8_t b) ++{ ++ int i; ++ u_int8_t tmp; ++ memset(m, 0, sizeof(struct ipv6)); ++ ++ for (i = 0; i < 4; i++) { ++ if (b == 0) ++ break; ++ ++ tmp = MINIMUM(b, 32); ++ b -= tmp; ++ ++ m->addr32[i] = htonl(0xffffffffU << (32 - tmp)); ++ } ++} ++ + u_int8_t + maxblock(u_int32_t addr, u_int8_t bits) + { +@@ -114,6 +233,26 @@ maxblock(u_int32_t addr, u_int8_t bits) + } + + u_int8_t ++maxblock6(struct ipv6 *addr, u_int8_t bits) ++{ ++ struct ipv6 m; ++ int i; ++ ++ while (bits > 0) { ++ imask6(&m, bits - 1); ++ ++ for (i = 0; i < 4; i++) { ++ if ((addr->addr32[i] & m.addr32[i]) != addr->addr32[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 +269,28 @@ maxdiff(u_int32_t a, u_int32_t b) + return (bits); + } + ++u_int8_t ++maxdiff6(struct ipv6 *a, struct ipv6 *b) ++{ ++ u_int8_t bits = 0; ++ struct ipv6 m, tmp; ++ int i; ++ ++ memcpy(&tmp, b, sizeof(tmp)); ++ ++ ipv6_add(&tmp, 1); ++ while (bits < 128) { ++ imask6(&m, bits); ++ ++ for (i = 0; i < 4; i++) { ++ if ((a->addr32[i] & m.addr32[i]) != (tmp.addr32[i] & m.addr32[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 +311,7 @@ range2cidrlist(struct cidr *list, u_int + list = tmp; + *cls += 32; + } +- list[*cli].addr = start; ++ list[*cli].addr4 = start; + list[*cli].bits = maxsize; + (*cli)++; + start = start + (1 << (32 - maxsize)); +@@ -158,11 +319,81 @@ range2cidrlist(struct cidr *list, u_int + return (list); + } + ++struct cidr * ++range2cidrlist6(struct cidr *list, u_int *cli, u_int *cls, struct ipv6 *start, ++ struct ipv6 *end) ++{ ++ u_int8_t maxsize, diff, btmp; ++ struct cidr *tmp; ++ struct ipv6 mask; ++ int i; ++ ++ while (cmp_ipv6(end, start) >= 0) { ++ 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].addr6, start, ++ sizeof(list[*cli].addr6)); ++ list[*cli].bits = maxsize; ++ (*cli)++; ++ ++ maxsize = 128 - maxsize; ++ ++ memset(&mask, 0, sizeof(mask)); ++ for (i = 15; i >= 0; i--) { ++ btmp = MINIMUM(maxsize, 8); ++ maxsize -= btmp; ++ ++ mask.addr8[i] = ((1 << btmp) - 1); ++ ++ if (maxsize == 0) ++ break; ++ } ++ ++ ipv6_add(&mask, 1); ++ ipv6_add2(start, &mask); ++ } ++ ++ 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.addr4; ++ *end = cidr.addr4 + (1 << (32 - cidr.bits)) - 1; ++} ++ ++void ++cidr2range6(struct cidr cidr, struct ipv6 *start, struct ipv6 *end) ++{ ++ int i; ++ u_int8_t tmp, bits = 128 - cidr.bits; ++ struct ipv6 mask; ++ memset(&mask, 0, sizeof(mask)); ++ ++ memcpy(start, &cidr.addr6, sizeof(cidr.addr6)); ++ memcpy(end, &cidr.addr6, sizeof(cidr.addr6)); ++ ++ for (i = 15; i >= 0; i--) { ++ if (bits == 0) ++ break; ++ ++ tmp = MINIMUM(bits, 8); ++ bits -= tmp; ++ mask.addr8[i] = ((1 << tmp) - 1); ++ } ++ ++ ipv6_add2(end, &mask); + } + + char * +@@ -176,11 +407,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 +422,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 */ +- memset(&c.addr, 0, sizeof(c.addr)); +- if (inet_net_pton(AF_INET, astring, &c.addr, sizeof(c.addr)) +- == -1) ++ if (sscanf(buf, "%15[0123456789.]/%u", astring, &maskbits) == 2) { ++ /* looks like IPv4 cidr */ ++ memset(&c.addr4, 0, sizeof(c.addr4)); ++ if (inet_net_pton(AF_INET, astring, &c.addr4, ++ sizeof(c.addr4)) == -1) + return (0); +- c.addr = ntohl(c.addr); ++ //c.ai_family = AF_INET; ++ c.addr4 = ntohl(c.addr4); + if (maskbits > 32) + return (0); + c.bits = maskbits; +- cidr2range(c, &start->addr, &end->addr); +- end->addr += 1; ++ cidr2range(c, &start->addr4, &end->addr4); ++ //start->ai_family = end->ai_family = AF_INET; ++ end->addr4 += 1; ++ ++ ret = AF_INET; ++ } else if (sscanf(buf, "%39[0123456789ABCDEFabcdef:]/%u", ++ astring, &maskbits) == 2) { ++ /* looks like IPv6 cidr */ ++ memset(&c.addr6, 0, sizeof(c.addr6)); ++ if (inet_net_pton(AF_INET6, astring, &c.addr6, ++ sizeof(c.addr6)) == -1) ++ return (0); ++ //c.ai_family = AF_INET6; ++ if (maskbits > 128) ++ return (0); ++ c.bits = maskbits; ++ cidr2range6(c, &start6->addr6, &end6->addr6); ++ //start6->ai_family = end6->ai_family = AF_INET6; ++ ipv6_add(&end6->addr6, 1); ++ ++ ret = AF_INET6; + } else if (sscanf(buf, "%15[0123456789.]%*[ -]%15[0123456789.]", + astring, astring2) == 2) { +- /* looks like 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) +- return (0); +- start->addr = ntohl(start->addr); +- if (inet_net_pton(AF_INET, astring2, &end->addr, +- sizeof(end->addr)) == -1) ++ /* looks like IPv4 start - end */ ++ memset(&start->addr4, 0, sizeof(start->addr4)); ++ memset(&end->addr4, 0, sizeof(end->addr4)); ++ if (inet_net_pton(AF_INET, astring, &start->addr4, ++ sizeof(start->addr4)) == -1) + return (0); +- end->addr = ntohl(end->addr) + 1; ++ //start->ai_family = AF_INET; ++ start->addr4 = ntohl(start->addr4); ++ if (inet_net_pton(AF_INET, astring2, &end->addr4, ++ sizeof(end->addr4)) == -1) ++ return (0); ++ //end->ai_family = AF_INET; ++ end->addr4 = ntohl(end->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->addr6, 0, sizeof(start6->addr6)); ++ memset(&end6->addr6, 0, sizeof(end6->addr6)); ++ if (inet_net_pton(AF_INET6, astring, &start6->addr6, ++ sizeof(start6->addr6)) == -1) ++ return (0); ++ //start6->ai_family = AF_INET6; ++ if (inet_net_pton(AF_INET6, astring2, &end6->addr6, ++ sizeof(end6->addr6)) == -1) ++ return (0); ++ //end6->ai_family = AF_INET6; ++ ipv6_add(&end6->addr6, 1); ++ if (start6 > end6) ++ return (0); ++ ++ ret = AF_INET6; + } else if (sscanf(buf, "%15[0123456789.]", astring) == 1) { +- /* just a single address */ +- memset(&start->addr, 0, sizeof(start->addr)); +- if (inet_net_pton(AF_INET, astring, &start->addr, +- sizeof(start->addr)) == -1) ++ /* just a single IPv4 address */ ++ memset(&start->addr4, 0, sizeof(start->addr4)); ++ if (inet_net_pton(AF_INET, astring, &start->addr4, ++ sizeof(start->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->addr4 = ntohl(start->addr4); ++ end->addr4 = start->addr4 + 1; ++ ++ ret = AF_INET; ++ } else if (sscanf(buf, "%39[0123456789ABCDEFabcdef:]", astring) == 1) { ++ /* just a single IPv6 address */ ++ memset(&start6->addr6, 0, sizeof(start6->addr6)); ++ if (inet_net_pton(AF_INET6, astring, &start6->addr6, ++ sizeof(start6->addr6)) == -1) ++ return (0); ++ //start6->ai_family = AF_INET6; ++ //end6->ai_family = AF_INET6; ++ memcpy(&end6->addr6, &start6->addr6, ++ sizeof(start6->addr6)); ++ ++ ipv6_add(&end6->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 +752,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 +789,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 +845,83 @@ 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 struct ipv6 *a, const struct ipv6 *b) ++{ ++ int i; ++ u_int32_t atmp, btmp; ++ ++ for (i = 0; i < 4; i++) { ++ atmp = ntohl(a->addr32[i]); ++ btmp = ntohl(b->addr32[i]); ++ ++ if (atmp > btmp) ++ return (1); ++ if (atmp < btmp) ++ 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)->addr4 > ((struct bl *) b)->addr4) + return (1); +- if (((struct bl *)a)->addr < ((struct bl *) b)->addr) ++ if (((struct bl *)a)->addr4 < ((struct bl *) b)->addr4) + return (-1); + return (0); + } + ++int ++cmpbl6(const void *a, const void *b) ++{ ++ struct ipv6 *addr1, *addr2; ++ ++ addr1 = &((struct bl *)a)->addr6; ++ addr2 = &((struct bl *)b)->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 +945,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].addr4; + + do { + bs += bl[i].b; + ws += bl[i].w; + i++; +- } while (bl[i].addr == addr); ++ } while (bl[i].addr4 == addr); + if (state == 1 && bs == 0) + state = 0; + else if (state == 0 && bs > 0) +@@ -581,18 +968,73 @@ collapse_blacklist(struct bl *bl, size_t + } + laststate = state; + } +- cl[cli].addr = 0; ++ cl[cli].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; ++ struct ipv6 bstart, addr; ++ ++ 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].addr6, sizeof(addr)); ++ ++ do { ++ bs += bl6[i].b; ++ ws += bl6[i].w; ++ i++; ++ } while (cmp_ipv6(&bl6[i].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].addr6, 0, sizeof(cl[cli].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 +1054,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].addr4), ++ blacklists[i].bits); ++ } ++ fprintf(sdc, ";inet6;%u", count6); ++ for (i = 0; i < count6; i++) { ++ inet_ntop(AF_INET6, &blacklists6[i].addr6, buf, sizeof(buf)); ++ fprintf(sdc, ";%s/%u", buf, blacklists6[i].bits); + } + fputc('\n', sdc); + fclose(sdc); +@@ -625,12 +1071,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 +1107,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].addr4), ++ blacklists[i].bits); ++ } ++ for (i = 0; i < count; i++) { ++ inet_ntop(AF_INET6, &blacklists6[i].addr6, buf, sizeof(buf)); ++ fprintf(pf, "%s/%u\n", buf, blacklists6[i].bits); + } + return (0); + } +@@ -672,9 +1123,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 +1138,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 +1188,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 +1200,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 +1218,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 +1228,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); + } + } 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); + } + } diff --git a/spamd.patch b/spamd.patch new file mode 100644 index 0000000..3e7c986 --- /dev/null +++ b/spamd.patch @@ -0,0 +1,938 @@ +Index: grey.c +=================================================================== +RCS file: /cvs/src/libexec/spamd/grey.c,v +retrieving revision 1.62 +diff -u -r1.62 grey.c +--- grey.c 10 Dec 2015 16:06:29 -0000 1.62 ++++ grey.c 31 Dec 2015 20:25:24 -0000 +@@ -54,8 +54,10 @@ + #define satosin(sa) ((struct sockaddr_in *)(sa)) + #define satosin6(sa) ((struct sockaddr_in6 *)(sa)) + +-void configure_spamd(char **, u_int, FILE *); +-int configure_pf(char **, int); ++struct listentry; ++ ++void configure_spamd(struct listentry *, u_int, FILE *); ++int configure_pf(struct listentry *, int); + char *dequotetolower(const char *); + void readsuffixlists(void); + void freeaddrlists(void); +@@ -69,11 +71,10 @@ + int greyreader(void); + void greyscanner(void); + +- + u_int whitecount, whitealloc; + u_int trapcount, trapalloc; +-char **whitelist; +-char **traplist; ++struct listentry *whitelist; ++struct listentry *traplist; + + char *traplist_name = "spamd-greytrap"; + char *traplist_msg = "\"Your address %A has mailed to spamtraps here\\n\""; +@@ -89,6 +90,18 @@ + int act; + }; + ++struct listentry { ++ char *entry; ++ int af; ++}; ++ ++struct address_list { ++ int count; ++ char *addrs; ++ size_t size; ++ size_t len; ++}; ++ + #define DBC_ADD 1 + #define DBC_DEL 2 + +@@ -124,6 +137,37 @@ + _exit(1); + } + ++static int ++add_address(struct address_list *rec, struct listentry *addr) ++{ ++ size_t entry_len = strlen(addr->entry); ++ size_t extra_len = (addr->af == AF_INET6) ? 6 : 5; /* semicolon, prefix and nul */ ++ if (rec->len + entry_len + extra_len >= rec->size) { ++ char *tmp = reallocarray(rec->addrs, rec->size + 1024, ++ sizeof(char)); ++ if (tmp == NULL) { ++ syslog_r(LOG_ERR, &sdata, ++ "configure_spamd: malloc failed"); ++ return(-1); ++ } ++ rec->addrs = tmp; ++ if (rec->size == 0) ++ rec->addrs[0] = '\0'; ++ rec->size += 1024; ++ } ++ ++ strlcat(rec->addrs, ";", rec->size); ++ strlcat(rec->addrs, addr->entry, rec->size); ++ if (addr->af == AF_INET6) ++ strlcat(rec->addrs, "/128", rec->size); ++ else ++ strlcat(rec->addrs, "/32", rec->size); ++ rec->len += entry_len; ++ rec->count++; ++ ++ return(0); ++} ++ + /* + * Greatly simplified version from spamd_setup.c - only + * sends one blacklist to an already open stream. Has no need +@@ -131,24 +175,36 @@ + * host hits. + */ + void +-configure_spamd(char **addrs, u_int count, FILE *sdc) ++configure_spamd(struct listentry *addrs, u_int count, FILE *sdc) + { + u_int i; ++ struct address_list inet = { 0, NULL, 0, 0 }; ++ struct address_list inet6 = { 0, NULL, 0, 0 }; + +- /* XXX - doesn't support IPV6 yet */ + fprintf(sdc, "%s;", traplist_name); + if (count != 0) { +- fprintf(sdc, "%s;inet;%u", traplist_msg, count); +- for (i = 0; i < count; i++) +- fprintf(sdc, ";%s/32", addrs[i]); ++ for (i = 0; i < count; i++) { ++ if (addrs[i].af == AF_INET6) ++ (void)add_address(&inet6, &addrs[i]); ++ else ++ (void)add_address(&inet, &addrs[i]); ++ } ++ ++ fprintf(sdc, "%s", traplist_msg); ++ if (inet.count > 0) ++ fprintf(sdc, ";inet;%u%s", inet.count, inet.addrs); ++ if (inet6.count > 0) ++ fprintf(sdc, ";inet6;%u%s", inet6.count, inet6.addrs); + } ++ free(inet.addrs); ++ free(inet6.addrs); + fputc('\n', sdc); + if (fflush(sdc) == EOF) + syslog_r(LOG_DEBUG, &sdata, "configure_spamd: fflush failed (%m)"); + } + + int +-configure_pf(char **addrs, int count) ++configure_pf(struct listentry *addrs, int count) + { + FILE *pf = NULL; + int i, pdes[2], status; +@@ -210,8 +266,9 @@ + return(-1); + } + for (i = 0; i < count; i++) +- if (addrs[i] != NULL) +- fprintf(pf, "%s/32\n", addrs[i]); ++ if (addrs[i].entry != NULL) ++ fprintf(pf, "%s/%s\n", addrs[i].entry, ++ (addrs[i].af == AF_INET6) ? "128" : "32"); + fclose(pf); + + waitpid(pid, &status, 0); +@@ -305,14 +362,14 @@ + + if (whitelist != NULL) + for (i = 0; i < whitecount; i++) { +- free(whitelist[i]); +- whitelist[i] = NULL; ++ free(whitelist[i].entry); ++ whitelist[i].entry = NULL; + } + whitecount = 0; + if (traplist != NULL) { + for (i = 0; i < trapcount; i++) { +- free(traplist[i]); +- traplist[i] = NULL; ++ free(traplist[i].entry); ++ traplist[i].entry = NULL; + } + } + trapcount = 0; +@@ -325,17 +382,17 @@ + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); +- hints.ai_family = AF_INET; /*for now*/ ++ hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_protocol = IPPROTO_UDP; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(addr, NULL, &hints, &res) == 0) { + if (whitecount == whitealloc) { +- char **tmp; ++ struct listentry *tmp; + + tmp = reallocarray(whitelist, +- whitealloc + 1024, sizeof(char *)); ++ whitealloc + 1024, sizeof(struct listentry)); + if (tmp == NULL) { + freeaddrinfo(res); + return(-1); +@@ -343,11 +400,12 @@ + whitelist = tmp; + whitealloc += 1024; + } +- whitelist[whitecount] = strdup(addr); +- if (whitelist[whitecount] == NULL) { ++ whitelist[whitecount].entry = strdup(addr); ++ if (whitelist[whitecount].entry == NULL) { + freeaddrinfo(res); + return(-1); + } ++ whitelist[whitecount].af = res->ai_family; + whitecount++; + freeaddrinfo(res); + } else +@@ -362,17 +420,17 @@ + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); +- hints.ai_family = AF_INET; /*for now*/ ++ hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_protocol = IPPROTO_UDP; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(addr, NULL, &hints, &res) == 0) { + if (trapcount == trapalloc) { +- char **tmp; ++ struct listentry *tmp; + + tmp = reallocarray(traplist, +- trapalloc + 1024, sizeof(char *)); ++ trapalloc + 1024, sizeof(struct listentry)); + if (tmp == NULL) { + freeaddrinfo(res); + return(-1); +@@ -380,11 +438,12 @@ + traplist = tmp; + trapalloc += 1024; + } +- traplist[trapcount] = strdup(addr); +- if (traplist[trapcount] == NULL) { ++ traplist[trapcount].entry = strdup(addr); ++ if (traplist[trapcount].entry == NULL) { + freeaddrinfo(res); + return(-1); + } ++ traplist[trapcount].af = res->ai_family; + trapcount++; + freeaddrinfo(res); + } else +@@ -719,6 +778,7 @@ + gd.bcount++; + } else + gd.pcount++; ++ gd.expire = expire; + memset(&dbk, 0, sizeof(dbk)); + dbk.size = strlen(ip); + dbk.data = ip; +@@ -880,13 +940,13 @@ + int + twread(char *buf) + { +- if ((strncmp(buf, "WHITE:", 6) == 0) || +- (strncmp(buf, "TRAP:", 5) == 0)) { ++ if ((strncmp(buf, "WHITE;", 6) == 0) || ++ (strncmp(buf, "TRAP;", 5) == 0)) { + char **ap, *argv[5]; + int argc = 0; + + for (ap = argv; +- ap < &argv[4] && (*ap = strsep(&buf, ":")) != NULL;) { ++ ap < &argv[4] && (*ap = strsep(&buf, ";")) != NULL;) { + if (**ap != '\0') + ap++; + argc++; +@@ -910,7 +970,7 @@ + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); +- hints.ai_family = AF_INET; /*for now*/ ++ hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_protocol = IPPROTO_UDP; /*dummy*/ + hints.ai_flags = AI_NUMERICHOST; +Index: spamd.c +=================================================================== +RCS file: /cvs/src/libexec/spamd/spamd.c,v +retrieving revision 1.137 +diff -u -r1.137 spamd.c +--- spamd.c 12 Dec 2015 20:09:28 -0000 1.137 ++++ spamd.c 31 Dec 2015 20:25:24 -0000 +@@ -56,8 +56,8 @@ + int il; + struct sockaddr_storage ss; + void *ia; +- char addr[32]; +- char caddr[32]; ++ char addr[40]; ++ char caddr[40]; + char helo[MAX_MAIL], mail[MAX_MAIL], rcpt[MAX_MAIL]; + struct sdlist **blacklists; + struct tls *cctx; +@@ -113,6 +113,7 @@ + int read_configline(FILE *); + void spamd_tls_init(void); + void check_spamd_db(void); ++void accept_connection(int, struct sockaddr *, socklen_t, struct con *); + + char hostname[HOST_NAME_MAX+1]; + struct syslog_data sdata = SYSLOG_DATA_INIT; +@@ -709,9 +710,6 @@ + time_t tt; + int error; + +- if (sa->sa_family != AF_INET) +- errx(1, "not supported yet"); +- + time(&tt); + free(cp->obuf); + free(cp->blacklists); +@@ -723,7 +721,10 @@ + cp->pfd->fd = fd; + memcpy(&cp->ss, sa, sa->sa_len); + cp->af = sa->sa_family; +- cp->ia = &((struct sockaddr_in *)&cp->ss)->sin_addr; ++ if (sa->sa_family == AF_INET) ++ cp->ia = &((struct sockaddr_in *)&cp->ss)->sin_addr; ++ else ++ cp->ia = &((struct sockaddr_in6 *)&cp->ss)->sin6_addr; + cp->blacklists = sdl_lookup(blacklists, cp->af, cp->ia); + cp->stutter = (greylist && !grey_stutter && cp->blacklists == NULL) ? + 0 : stutter; +@@ -1208,13 +1209,54 @@ + return(maxfiles - 200); + } + ++void ++accept_connection(int sfd, struct sockaddr *sin, ++ socklen_t sinlen, struct con *con) ++{ ++ int s2, i; ++ ++ s2 = accept(sfd, sin, &sinlen); ++ if (s2 == -1) { ++ switch (errno) { ++ case EINTR: ++ case ECONNABORTED: ++ break; ++ case EMFILE: ++ case ENFILE: ++ slowdowntill = time(NULL) + 1; ++ break; ++ default: ++ errx(1, "accept"); ++ } ++ } else { ++ /* Check if we hit the chosen fd limit */ ++ for (i = 0; i < maxcon; i++) ++ if (con->pfd->fd == -1) ++ break; ++ if (i == maxcon) { ++ close(s2); ++ slowdowntill = 0; ++ } else { ++ initcon(&con[i], s2, sin); ++ syslog_r(LOG_INFO, &sdata, ++ "%s: connected (%d/%d)%s%s", ++ con[i].addr, clients, blackcount, ++ ((con[i].lists == NULL) ? "" : ++ ", lists:"), ++ ((con[i].lists == NULL) ? "": ++ con[i].lists)); ++ } ++ } ++} ++ + /* Symbolic indexes for pfd[] below */ + #define PFD_SMTPLISTEN 0 +-#define PFD_CONFLISTEN 1 +-#define PFD_SYNCFD 2 +-#define PFD_CONFFD 3 +-#define PFD_TRAPFD 4 +-#define PFD_FIRSTCON 5 ++#define PFD_SMTPLISTEN6 1 ++#define PFD_CONFLISTEN 2 ++#define PFD_SYNCFD 3 ++#define PFD_CONFFD 4 ++#define PFD_TRAPFD 5 ++#define PFD_FIRSTCON 6 + + int + main(int argc, char *argv[]) +@@ -1222,15 +1264,24 @@ + struct pollfd *pfd; + struct sockaddr_in sin; + struct sockaddr_in lin; +- int ch, smtplisten, conflisten, syncfd = -1, i, one = 1; ++ struct sockaddr_in6 sin6; ++ int ch, smtplisten, smtplisten6, conflisten, syncfd = -1, i, one = 1; + u_short port; + long long passt, greyt, whitet; + struct servent *ent; + struct rlimit rlp; + char *bind_address = NULL; ++ char *bind6_address = NULL; + const char *errstr; + char *sync_iface = NULL; + char *sync_baddr = NULL; ++ struct addrinfo hints, *res; ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_DGRAM; /* dummy */ ++ hints.ai_protocol = IPPROTO_UDP; /* dummy */ ++ hints.ai_flags = AI_NUMERICHOST; + + tzset(); + openlog_r("spamd", LOG_PID | LOG_NDELAY, LOG_DAEMON, &sdata); +@@ -1262,7 +1313,13 @@ + nreply = "550"; + break; + case 'l': +- bind_address = optarg; ++ if (getaddrinfo(optarg, NULL, &hints, &res) != 0) ++ errx(1, "-l %s: invalid address", optarg); ++ if (res->ai_family == AF_INET6) ++ bind6_address = optarg; ++ else ++ bind_address = optarg; ++ freeaddrinfo(res); + break; + case 'B': + maxblack = strtonum(optarg, 0, INT_MAX, &errstr); +@@ -1413,6 +1470,14 @@ + sizeof(one)) == -1) + return (-1); + ++ smtplisten6 = socket(AF_INET6, SOCK_STREAM, 0); ++ if (smtplisten6 == -1) ++ err(1, "socket"); ++ ++ if (setsockopt(smtplisten6, SOL_SOCKET, SO_REUSEADDR, &one, ++ sizeof(sin6)) == -1) ++ return (-1); ++ + conflisten = socket(AF_INET, SOCK_STREAM, 0); + if (conflisten == -1) + err(1, "socket"); +@@ -1434,6 +1499,19 @@ + if (bind(smtplisten, (struct sockaddr *)&sin, sizeof sin) == -1) + err(1, "bind"); + ++ memset(&sin6, 0, sizeof sin6); ++ sin6.sin6_len = sizeof(sin6); ++ if (bind6_address) { ++ if (inet_pton(AF_INET6, bind6_address, &sin6.sin6_addr) != 1) ++ err(1, "inet_pton"); ++ } else ++ sin6.sin6_addr = (struct in6_addr)IN6ADDR_LOOPBACK_INIT; ++ sin6.sin6_family = AF_INET6; ++ sin6.sin6_port = htons(port); ++ ++ if (bind(smtplisten6, (struct sockaddr *)&sin6, sizeof sin6) == -1) ++ err(1, "bind"); ++ + memset(&lin, 0, sizeof sin); + lin.sin_len = sizeof(sin); + lin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); +@@ -1532,6 +1610,9 @@ + if (listen(smtplisten, 10) == -1) + err(1, "listen"); + ++ if (listen(smtplisten6, 10) == -1) ++ err(1, "listen"); ++ + if (listen(conflisten, 10) == -1) + err(1, "listen"); + +@@ -1557,6 +1638,7 @@ + + /* events and pfd entries for con[] are filled in below. */ + pfd[PFD_SMTPLISTEN].fd = smtplisten; ++ pfd[PFD_SMTPLISTEN6].fd = smtplisten6; + pfd[PFD_CONFLISTEN].fd = conflisten; + + while (1) { +@@ -1595,11 +1677,13 @@ + numcon = i + 1; + } + pfd[PFD_SMTPLISTEN].events = 0; ++ pfd[PFD_SMTPLISTEN6].events = 0; + pfd[PFD_CONFLISTEN].events = 0; + pfd[PFD_CONFFD].events = 0; + pfd[PFD_CONFFD].fd = conffd; + if (slowdowntill == 0) { + pfd[PFD_SMTPLISTEN].events = POLLIN; ++ pfd[PFD_SMTPLISTEN6].events = POLLIN; + + /* only one active config conn at a time */ + if (conffd == -1) +@@ -1647,45 +1731,12 @@ + handlew(&con[i], clients + 5 < maxcon); + } + } +- if (pfd[PFD_SMTPLISTEN].revents & (POLLIN|POLLHUP)) { +- socklen_t sinlen; +- int s2; +- +- sinlen = sizeof(sin); +- s2 = accept(smtplisten, (struct sockaddr *)&sin, &sinlen); +- if (s2 == -1) { +- switch (errno) { +- case EINTR: +- case ECONNABORTED: +- break; +- case EMFILE: +- case ENFILE: +- slowdowntill = time(NULL) + 1; +- break; +- default: +- errx(1, "accept"); +- } +- } else { +- /* Check if we hit the chosen fd limit */ +- for (i = 0; i < maxcon; i++) +- if (con[i].pfd->fd == -1) +- break; +- if (i == maxcon) { +- close(s2); +- slowdowntill = 0; +- } else { +- initcon(&con[i], s2, +- (struct sockaddr *)&sin); +- syslog_r(LOG_INFO, &sdata, +- "%s: connected (%d/%d)%s%s", +- con[i].addr, clients, blackcount, +- ((con[i].lists == NULL) ? "" : +- ", lists:"), +- ((con[i].lists == NULL) ? "": +- con[i].lists)); +- } +- } +- } ++ if (pfd[PFD_SMTPLISTEN].revents & (POLLIN|POLLHUP)) ++ accept_connection(smtplisten, (struct sockaddr *)&sin, ++ sizeof(sin), con); ++ if (pfd[PFD_SMTPLISTEN6].revents & (POLLIN|POLLHUP)) ++ accept_connection(smtplisten6, (struct sockaddr *)&sin6, ++ sizeof(sin6), con); + if (pfd[PFD_CONFLISTEN].revents & (POLLIN|POLLHUP)) { + socklen_t sinlen; + +Index: sync.c +=================================================================== +RCS file: /cvs/src/libexec/spamd/sync.c,v +retrieving revision 1.11 +diff -u -r1.11 sync.c +--- sync.c 23 Nov 2014 21:19:47 -0000 1.11 ++++ sync.c 31 Dec 2015 20:25:24 -0000 +@@ -241,11 +241,15 @@ + struct sockaddr_in addr; + struct spam_synctlv_hdr *tlv; + struct spam_synctlv_grey *sg; ++ struct spam_synctlv_grey6 *sg6; + struct spam_synctlv_addr *sd; ++ struct spam_synctlv_addr6 *sd6; + u_int8_t buf[SPAM_SYNC_MAXSIZE]; + u_int8_t hmac[2][SPAM_SYNC_HMAC_LEN]; + struct in_addr ip; ++ struct in6_addr ip6; + char *from, *to, *helo; ++ char ipstr[40] = { '\0' }; + u_int8_t *p; + socklen_t addr_len; + ssize_t len; +@@ -268,7 +272,7 @@ + hdr = (struct spam_synchdr *)buf; + if (len < sizeof(struct spam_synchdr) || + hdr->sh_version != SPAM_SYNC_VERSION || +- hdr->sh_af != AF_INET || ++ (hdr->sh_af != AF_INET && hdr->sh_af != AF_INET6) || + len < ntohs(hdr->sh_length)) + goto trunc; + len = ntohs(hdr->sh_length); +@@ -296,72 +300,120 @@ + + switch (ntohs(tlv->st_type)) { + case SPAM_SYNC_GREY: +- sg = (struct spam_synctlv_grey *)tlv; +- if ((sizeof(*sg) + +- ntohs(sg->sg_from_length) + +- ntohs(sg->sg_to_length) + +- ntohs(sg->sg_helo_length)) > +- ntohs(tlv->st_length)) +- goto trunc; +- +- ip.s_addr = sg->sg_ip; +- from = (char *)(sg + 1); +- to = from + ntohs(sg->sg_from_length); +- helo = to + ntohs(sg->sg_to_length); ++ if (hdr->sh_af == AF_INET) { ++ sg = (struct spam_synctlv_grey *)tlv; ++ if ((sizeof(*sg) + ++ ntohs(sg->sg_from_length) + ++ ntohs(sg->sg_to_length) + ++ ntohs(sg->sg_helo_length)) > ++ ntohs(tlv->st_length)) ++ goto trunc; ++ ++ ip.s_addr = sg->sg_ip; ++ inet_ntop(AF_INET, &ip, ipstr, sizeof(ipstr)); ++ ++ from = (char *)(sg + 1); ++ to = from + ntohs(sg->sg_from_length); ++ helo = to + ntohs(sg->sg_to_length); ++ } else { ++ sg6 = (struct spam_synctlv_grey6 *)tlv; ++ if ((sizeof(*sg6)) + ++ ntohs(sg6->sg6_from_length) + ++ ntohs(sg6->sg6_to_length) + ++ ntohs(sg6->sg6_helo_length) > ++ ntohs(tlv->st_length)) ++ goto trunc; ++ ++ memcpy(&ip6.s6_addr, &sg6->sg6_ip, sizeof(ip6.s6_addr)); ++ inet_ntop(AF_INET6, &ip6, ipstr, sizeof(ipstr)); ++ ++ from = (char *)(sg6 + 1); ++ to = from + ntohs(sg6->sg6_from_length); ++ helo = to + ntohs(sg6->sg6_to_length); ++ } ++ + if (debug) { + fprintf(stderr, "%s(sync): " + "received grey entry ", + inet_ntoa(addr.sin_addr)); + fprintf(stderr, "helo %s ip %s " + "from %s to %s\n", +- helo, inet_ntoa(ip), from, to); ++ helo, ipstr, from, to); + } + if (greylist) { + /* send this info to the greylister */ + fprintf(grey, + "SYNC\nHE:%s\nIP:%s\nFR:%s\nTO:%s\n", +- helo, inet_ntoa(ip), from, to); ++ helo, ipstr, from, to); + fflush(grey); + } + break; + case SPAM_SYNC_WHITE: +- sd = (struct spam_synctlv_addr *)tlv; +- if (sizeof(*sd) != ntohs(tlv->st_length)) +- goto trunc; ++ if (hdr->sh_af == AF_INET) { ++ sd = (struct spam_synctlv_addr *)tlv; ++ if (sizeof(*sd) != ntohs(tlv->st_length)) ++ goto trunc; ++ ++ ip.s_addr = sd->sd_ip; ++ inet_ntop(AF_INET, &ip, ipstr, sizeof(ipstr)); ++ ++ expire = ntohl(sd->sd_expire); ++ } else { ++ sd6 = (struct spam_synctlv_addr6 *)tlv; ++ if (sizeof(*sd6) != ntohs(tlv->st_length)) ++ goto trunc; ++ ++ memcpy(&ip6.s6_addr, &sd6->sd6_ip, sizeof(ip6.s6_addr)); ++ inet_ntop(AF_INET6, &ip6, ipstr, sizeof(ipstr)); ++ ++ expire = ntohl(sd6->sd6_expire); ++ } + +- ip.s_addr = sd->sd_ip; +- expire = ntohl(sd->sd_expire); + if (debug) { + fprintf(stderr, "%s(sync): " + "received white entry ", + inet_ntoa(addr.sin_addr)); +- fprintf(stderr, "ip %s ", inet_ntoa(ip)); ++ fprintf(stderr, "ip %s ", ipstr); + } + if (greylist) { + /* send this info to the greylister */ +- fprintf(grey, "WHITE:%s:", inet_ntoa(ip)); +- fprintf(grey, "%s:%u\n", ++ fprintf(grey, "WHITE;%s;", ipstr); ++ fprintf(grey, "%s;%u\n", + inet_ntoa(addr.sin_addr), expire); + fflush(grey); + } + break; + case SPAM_SYNC_TRAPPED: +- sd = (struct spam_synctlv_addr *)tlv; +- if (sizeof(*sd) != ntohs(tlv->st_length)) +- goto trunc; ++ if (hdr->sh_af == AF_INET) { ++ sd = (struct spam_synctlv_addr *)tlv; ++ if (sizeof(*sd) != ntohs(tlv->st_length)) ++ goto trunc; ++ ++ ip.s_addr = sd->sd_ip; ++ inet_ntop(AF_INET, &ip, ipstr, sizeof(ipstr)); ++ ++ expire = ntohl(sd->sd_expire); ++ } else { ++ sd6 = (struct spam_synctlv_addr6 *)tlv; ++ if (sizeof(*sd6) != ntohs(tlv->st_length)) ++ goto trunc; ++ ++ memcpy(&ip6.s6_addr, &sd6->sd6_ip, sizeof(ip6.s6_addr)); ++ inet_ntop(AF_INET6, &ip6, ipstr, sizeof(ipstr)); ++ ++ expire = ntohl(sd6->sd6_expire); ++ } + +- ip.s_addr = sd->sd_ip; +- expire = ntohl(sd->sd_expire); + if (debug) { + fprintf(stderr, "%s(sync): " + "received trapped entry ", + inet_ntoa(addr.sin_addr)); +- fprintf(stderr, "ip %s ", inet_ntoa(ip)); ++ fprintf(stderr, "ip %s ", ipstr); + } + if (greylist) { + /* send this info to the greylister */ +- fprintf(grey, "TRAP:%s:", inet_ntoa(ip)); +- fprintf(grey, "%s:%u\n", ++ fprintf(grey, "TRAP;%s;", ipstr); ++ fprintf(grey, "%s;%u\n", + inet_ntoa(addr.sin_addr), expire); + fflush(grey); + } +@@ -420,7 +472,10 @@ + struct iovec iov[7]; + struct spam_synchdr hdr; + struct spam_synctlv_grey sg; ++ struct spam_synctlv_grey6 sg6; ++ + struct spam_synctlv_hdr end; ++ struct addrinfo hints, *res; + u_int16_t sglen, fromlen, tolen, helolen, padlen; + char pad[SPAM_ALIGNBYTES]; + int i = 0; +@@ -434,8 +489,20 @@ + + bzero(&hdr, sizeof(hdr)); + bzero(&sg, sizeof(sg)); ++ bzero(&sg6, sizeof(sg6)); + bzero(&pad, sizeof(pad)); + ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_DGRAM; /* dummy */ ++ hints.ai_protocol = IPPROTO_UDP; /* dummy */ ++ hints.ai_flags = AI_NUMERICHOST; ++ ++ if (getaddrinfo(ip, NULL, &hints, &res) != 0) { ++ warnx("Invalid ip address: %s", ip); ++ return; ++ } ++ + fromlen = strlen(from) + 1; + tolen = strlen(to) + 1; + helolen = strlen(helo) + 1; +@@ -443,12 +510,15 @@ + HMAC_CTX_init(&ctx); + HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1()); + +- sglen = sizeof(sg) + fromlen + tolen + helolen; ++ if (res->ai_family == AF_INET) ++ sglen = sizeof(sg) + fromlen + tolen + helolen; ++ else ++ sglen = sizeof(sg6) + fromlen + tolen + helolen; + padlen = SPAM_ALIGN(sglen) - sglen; + + /* Add SPAM sync packet header */ + hdr.sh_version = SPAM_SYNC_VERSION; +- hdr.sh_af = AF_INET; ++ hdr.sh_af = res->ai_family; + hdr.sh_counter = htonl(sync_counter++); + hdr.sh_length = htons(sizeof(hdr) + sglen + padlen + sizeof(end)); + iov[i].iov_base = &hdr; +@@ -456,16 +526,35 @@ + HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); + i++; + +- /* Add single SPAM sync greylisting entry */ +- sg.sg_type = htons(SPAM_SYNC_GREY); +- sg.sg_length = htons(sglen + padlen); +- sg.sg_timestamp = htonl(now); +- sg.sg_ip = inet_addr(ip); +- sg.sg_from_length = htons(fromlen); +- sg.sg_to_length = htons(tolen); +- sg.sg_helo_length = htons(helolen); +- iov[i].iov_base = &sg; +- iov[i].iov_len = sizeof(sg); ++ if (res->ai_family == AF_INET) { ++ /* Add single SPAM sync greylisting entry */ ++ sg.sg_type = htons(SPAM_SYNC_GREY); ++ sg.sg_length = htons(sglen + padlen); ++ sg.sg_timestamp = htonl(now); ++ ++ inet_pton(AF_INET, ip, &sg.sg_ip); ++ ++ sg.sg_from_length = htons(fromlen); ++ sg.sg_to_length = htons(tolen); ++ sg.sg_helo_length = htons(helolen); ++ iov[i].iov_base = &sg; ++ iov[i].iov_len = sizeof(sg); ++ } else { ++ /* Add single SPAM sync greylisting entry */ ++ sg6.sg6_type = htons(SPAM_SYNC_GREY); ++ sg6.sg6_length = htons(sglen + padlen); ++ sg6.sg6_timestamp = htonl(now); ++ ++ inet_pton(AF_INET6, ip, &sg6.sg6_ip); ++ ++ sg6.sg6_from_length = htons(fromlen); ++ sg6.sg6_to_length = htons(tolen); ++ sg6.sg6_helo_length = htons(helolen); ++ iov[i].iov_base = &sg6; ++ iov[i].iov_len = sizeof(sg6); ++ } ++ freeaddrinfo(res); ++ + HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); + i++; + +@@ -510,7 +599,9 @@ + struct iovec iov[3]; + struct spam_synchdr hdr; + struct spam_synctlv_addr sd; ++ struct spam_synctlv_addr6 sd6; + struct spam_synctlv_hdr end; ++ struct addrinfo hints, *res; + int i = 0; + HMAC_CTX ctx; + u_int hmac_len; +@@ -521,28 +612,60 @@ + + bzero(&hdr, sizeof(hdr)); + bzero(&sd, sizeof(sd)); ++ bzero(&sd6, sizeof(sd6)); ++ ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_DGRAM; /* dummy */ ++ hints.ai_protocol = IPPROTO_UDP; /* dummy */ ++ hints.ai_flags = AI_NUMERICHOST; ++ ++ if (getaddrinfo(ip, NULL, &hints, &res) != 0) { ++ warnx("Invalid ip address: %s", ip); ++ return; ++ } + + HMAC_CTX_init(&ctx); + HMAC_Init(&ctx, sync_key, strlen(sync_key), EVP_sha1()); + + /* Add SPAM sync packet header */ + hdr.sh_version = SPAM_SYNC_VERSION; +- hdr.sh_af = AF_INET; ++ hdr.sh_af = res->ai_family; + hdr.sh_counter = htonl(sync_counter++); +- hdr.sh_length = htons(sizeof(hdr) + sizeof(sd) + sizeof(end)); ++ if (res->ai_family == AF_INET) ++ hdr.sh_length = htons(sizeof(hdr) + sizeof(sd) + sizeof(end)); ++ else ++ hdr.sh_length = htons(sizeof(hdr) + sizeof(sd6) + sizeof(end)); + iov[i].iov_base = &hdr; + iov[i].iov_len = sizeof(hdr); + HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); + i++; + +- /* Add single SPAM sync address entry */ +- sd.sd_type = htons(type); +- sd.sd_length = htons(sizeof(sd)); +- sd.sd_timestamp = htonl(now); +- sd.sd_expire = htonl(expire); +- sd.sd_ip = inet_addr(ip); +- iov[i].iov_base = &sd; +- iov[i].iov_len = sizeof(sd); ++ if (res->ai_family == AF_INET) { ++ /* Add single SPAM sync address entry */ ++ sd.sd_type = htons(type); ++ sd.sd_length = htons(sizeof(sd)); ++ sd.sd_timestamp = htonl(now); ++ sd.sd_expire = htonl(expire); ++ ++ inet_pton(AF_INET, ip, &sd.sd_ip); ++ ++ iov[i].iov_base = &sd; ++ iov[i].iov_len = sizeof(sd); ++ } else { ++ /* Add single SPAM sync address entry */ ++ sd6.sd6_type = htons(type); ++ sd6.sd6_length = htons(sizeof(sd)); ++ sd6.sd6_timestamp = htonl(now); ++ sd6.sd6_expire = htonl(expire); ++ ++ inet_pton(AF_INET6, ip, &sd6.sd6_ip); ++ ++ iov[i].iov_base = &sd6; ++ iov[i].iov_len = sizeof(sd6); ++ } ++ freeaddrinfo(res); ++ + HMAC_Update(&ctx, iov[i].iov_base, iov[i].iov_len); + i++; + +Index: sync.h +=================================================================== +RCS file: /cvs/src/libexec/spamd/sync.h,v +retrieving revision 1.3 +diff -u -r1.3 sync.h +--- sync.h 22 May 2008 19:54:11 -0000 1.3 ++++ sync.h 31 Dec 2015 20:25:24 -0000 +@@ -68,12 +68,31 @@ + /* strings go here, then packet code re-aligns packet */ + } __packed; + ++struct spam_synctlv_grey6 { ++ u_int16_t sg6_type; ++ u_int16_t sg6_length; ++ u_int32_t sg6_timestamp; ++ u_int32_t sg6_ip[4]; ++ u_int16_t sg6_from_length; ++ u_int16_t sg6_to_length; ++ u_int16_t sg6_helo_length; ++ /* strings go here, then packet code re-aligns packet */ ++} __packed; ++ + struct spam_synctlv_addr { + u_int16_t sd_type; + u_int16_t sd_length; + u_int32_t sd_timestamp; + u_int32_t sd_expire; + u_int32_t sd_ip; ++} __packed; ++ ++struct spam_synctlv_addr6 { ++ u_int16_t sd6_type; ++ u_int16_t sd6_length; ++ u_int32_t sd6_timestamp; ++ u_int32_t sd6_expire; ++ u_int32_t sd6_ip[4]; + } __packed; + + #define SPAM_SYNC_END 0x0000 diff --git a/spamlogd.patch b/spamlogd.patch new file mode 100644 index 0000000..0d6527f --- /dev/null +++ b/spamlogd.patch @@ -0,0 +1,78 @@ +Index: spamlogd.c +=================================================================== +RCS file: /cvs/src/libexec/spamlogd/spamlogd.c,v +retrieving revision 1.26 +diff -u -r1.26 spamlogd.c +--- spamlogd.c 11 Dec 2015 17:16:52 -0000 1.26 ++++ spamlogd.c 31 Dec 2015 15:48:03 -0000 +@@ -32,6 +32,7 @@ + + #include <netinet/in.h> + #include <netinet/ip.h> ++#include <netinet/ip6.h> + #include <arpa/inet.h> + + #include <net/pfvar.h> +@@ -110,8 +111,9 @@ + init_pcap(void) + { + struct bpf_program bpfp; +- char filter[PCAPFSIZ] = "ip and port 25 and action pass " +- "and tcp[13]&0x12=0x2"; ++ char filter[PCAPFSIZ] = "tcp and port 25 and action pass " ++ "and ((ip and tcp[13] & 0x12 = 0x2) or " ++ "(ip6 and ip6[6] = 0x6 and ip6[53] & 0x12 = 0x2))"; + + if ((hpcap = pcap_open_live(pflogif, PCAPSNAP, 1, PCAPTIMO, + errbuf)) == NULL) { +@@ -155,6 +157,7 @@ + u_int8_t hdrlen; + u_int32_t caplen = h->caplen; + const struct ip *ip = NULL; ++ const struct ip6_hdr *ip6 = NULL; + const struct pfloghdr *hdr; + char ipstraddr[40] = { '\0' }; + +@@ -185,6 +188,14 @@ + else if (hdr->dir == PF_OUT && !flag_inbound) + inet_ntop(af, &ip->ip_dst, ipstraddr, + sizeof(ipstraddr)); ++ } else if (af == AF_INET6) { ++ ip6 = (const struct ip6_hdr *)(sp + hdrlen); ++ if (hdr->dir == PF_IN) ++ inet_ntop(af, &ip6->ip6_src, ipstraddr, ++ sizeof(ipstraddr)); ++ else if (hdr->dir == PF_OUT && !flag_inbound) ++ inet_ntop(af, &ip6->ip6_dst, ipstraddr, ++ sizeof(ipstraddr)); + } + + if (ipstraddr[0] != '\0') { +@@ -205,7 +216,12 @@ + struct gdata gd; + time_t now; + int r; +- struct in_addr ia; ++ struct addrinfo hints, *res; ++ memset(&hints, 0, sizeof(hints)); ++ hints.ai_family = PF_UNSPEC; ++ hints.ai_socktype = SOCK_DGRAM; /*dummy*/ ++ hints.ai_protocol = IPPROTO_UDP; /*dummy*/ ++ hints.ai_flags = AI_NUMERICHOST; + + now = time(NULL); + memset(&hashinfo, 0, sizeof(hashinfo)); +@@ -215,10 +231,12 @@ + strerror(errno)); + return (-1); + } +- if (inet_pton(AF_INET, ip, &ia) != 1) { ++ if (getaddrinfo(ip, NULL, &hints, &res) != 0) { + logmsg(LOG_NOTICE, "Invalid IP address %s", ip); + goto bad; + } ++ freeaddrinfo(res); ++ + memset(&dbk, 0, sizeof(dbk)); + dbk.size = strlen(ip); + dbk.data = ip; |