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