Hello, Here's a patch for smtpctl spf resolution, adding support for target specified as a hostname + cidr.
Yes, SPF lets you specify targets like a:example.com/24. Due to the async and recursive nature of DNS resolution in spfwalk.c, it's kind of hard to pass data around without too much memory allocation, so I decided to just pass cidr(s) as integer value instead of keeping the original string and passing pointers around. Comments welcome, thanks! Index: usr.sbin/smtpd/spfwalk.c =================================================================== RCS file: /var/cvs/src/usr.sbin/smtpd/spfwalk.c,v retrieving revision 1.15 diff -u -r1.15 spfwalk.c --- usr.sbin/smtpd/spfwalk.c 11 Nov 2019 17:20:25 -0000 1.15 +++ usr.sbin/smtpd/spfwalk.c 12 Nov 2019 12:43:25 -0000 @@ -40,15 +40,24 @@ #include "unpack_dns.h" #include "parser.h" +struct sender { + void (*cb)(struct dns_rr *, struct sender *); + char *target; + int cidr4; + int cidr6; +}; + +void *xmalloc(size_t); int spfwalk(int, struct parameter *); -static void dispatch_txt(struct dns_rr *); -static void dispatch_mx(struct dns_rr *); -static void dispatch_a(struct dns_rr *); -static void dispatch_aaaa(struct dns_rr *); -static void lookup_record(int, const char *, void (*)(struct dns_rr *)); +static void dispatch_txt(struct dns_rr *, struct sender *); +static void dispatch_mx(struct dns_rr *, struct sender *); +static void dispatch_a(struct dns_rr *, struct sender *); +static void dispatch_aaaa(struct dns_rr *, struct sender *); +static void lookup_record(int, struct sender *); static void dispatch_record(struct asr_result *, void *); static ssize_t parse_txt(const char *, size_t, char *, size_t); +static int parse_sender(struct sender *); int ip_v4 = 0; int ip_v6 = 0; @@ -59,6 +68,7 @@ int spfwalk(int argc, struct parameter *argv) { + struct sender sdr; const char *ip_family = NULL; char *line = NULL; size_t linesize = 0; @@ -81,12 +91,17 @@ dict_init(&seen); event_init(); + sdr.cidr4 = sdr.cidr6 = -1; + sdr.cb = dispatch_txt; + while ((linelen = getline(&line, &linesize, stdin)) != -1) { while (linelen-- > 0 && isspace(line[linelen])) line[linelen] = '\0'; - if (linelen > 0) - lookup_record(T_TXT, line, dispatch_txt); + if (linelen > 0) { + sdr.target = line; + lookup_record(T_TXT, &sdr); + } } free(line); @@ -100,20 +115,23 @@ } void -lookup_record(int type, const char *record, void (*cb)(struct dns_rr *)) +lookup_record(int type, struct sender *sdr) { struct asr_query *as; + struct sender *nsdr; - as = res_query_async(record, C_IN, type, NULL); + as = res_query_async(sdr->target, C_IN, type, NULL); if (as == NULL) err(1, "res_query_async"); - event_asr_run(as, dispatch_record, cb); + nsdr = xmalloc(sizeof(*nsdr)); + *nsdr = *sdr; + event_asr_run(as, dispatch_record, (void *)nsdr); } void dispatch_record(struct asr_result *ar, void *arg) { - void (*cb)(struct dns_rr *) = arg; + struct sender *sdr = arg; struct unpack pack; struct dns_header h; struct dns_query q; @@ -121,7 +139,7 @@ /* best effort */ if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA) - return; + goto end; unpack_init(&pack, ar->ar_data, ar->ar_datalen); unpack_header(&pack, &h); @@ -130,19 +148,22 @@ for (; h.ancount; h.ancount--) { unpack_rr(&pack, &rr); /**/ - cb(&rr); + sdr->cb(&rr, sdr); } +end: + free(sdr); } void -dispatch_txt(struct dns_rr *rr) +dispatch_txt(struct dns_rr *rr, struct sender *sdr) { + char buf[4096]; + char *argv[512]; + char buf2[512]; + struct sender lsdr; struct in6_addr ina; - char buf[4096]; - char buf2[512]; - char *in = buf; - char *argv[512]; - char **ap = argv; + char **ap = argv; + char *in = buf; char *end; ssize_t n; @@ -173,6 +194,8 @@ if (**ap == '+' || **ap == '?') (*ap)++; + lsdr.cidr4 = lsdr.cidr6 = -1; + if (strncasecmp("ip4:", *ap, 4) == 0) { if ((ip_v4 == 1 || ip_both == 1) && inet_net_pton(AF_INET, *(ap) + 4, @@ -190,35 +213,55 @@ if (strcasecmp("a", *ap) == 0) { print_dname(rr->rr_dname, buf2, sizeof(buf2)); buf2[strlen(buf2) - 1] = '\0'; - lookup_record(T_A, buf2, dispatch_a); - lookup_record(T_AAAA, buf2, dispatch_aaaa); + lsdr.target = buf2; + lsdr.cb = dispatch_a; + lookup_record(T_A, &lsdr); + lsdr.cb = dispatch_aaaa; + lookup_record(T_AAAA, &lsdr); continue; } if (strncasecmp("a:", *ap, 2) == 0) { - lookup_record(T_A, *(ap) + 2, dispatch_a); - lookup_record(T_AAAA, *(ap) + 2, dispatch_aaaa); + lsdr.target = *(ap) + 2; + if (parse_sender(&lsdr) < 0) + continue; + lsdr.cb = dispatch_a; + lookup_record(T_A, &lsdr); + lsdr.cb = dispatch_aaaa; + lookup_record(T_AAAA, &lsdr); continue; } if (strncasecmp("exists:", *ap, 7) == 0) { - lookup_record(T_A, *(ap) + 7, dispatch_a); + lsdr.target = *(ap) + 7; + lsdr.cb = dispatch_a; + lookup_record(T_A, &lsdr); continue; } if (strncasecmp("include:", *ap, 8) == 0) { - lookup_record(T_TXT, *(ap) + 8, dispatch_txt); + lsdr.target = *(ap) + 8; + lsdr.cb = dispatch_txt; + lookup_record(T_TXT, &lsdr); continue; } if (strncasecmp("redirect=", *ap, 9) == 0) { - lookup_record(T_TXT, *(ap) + 9, dispatch_txt); + lsdr.target = *(ap) + 9; + lsdr.cb = dispatch_txt; + lookup_record(T_TXT, &lsdr); continue; } if (strcasecmp("mx", *ap) == 0) { print_dname(rr->rr_dname, buf2, sizeof(buf2)); buf2[strlen(buf2) - 1] = '\0'; - lookup_record(T_MX, buf2, dispatch_mx); + lsdr.target = buf2; + lsdr.cb = dispatch_mx; + lookup_record(T_MX, &lsdr); continue; } if (strncasecmp("mx:", *ap, 3) == 0) { - lookup_record(T_MX, *(ap) + 3, dispatch_mx); + lsdr.target = *(ap) + 3; + if (parse_sender(&lsdr) < 0) + continue; + lsdr.cb = dispatch_mx; + lookup_record(T_MX, &lsdr); continue; } } @@ -226,9 +269,10 @@ } void -dispatch_mx(struct dns_rr *rr) +dispatch_mx(struct dns_rr *rr, struct sender *sdr) { char buf[512]; + struct sender lsdr; if (rr->rr_type != T_MX) return; @@ -237,12 +281,16 @@ buf[strlen(buf) - 1] = '\0'; if (buf[strlen(buf) - 1] == '.') buf[strlen(buf) - 1] = '\0'; - lookup_record(T_A, buf, dispatch_a); - lookup_record(T_AAAA, buf, dispatch_aaaa); + lsdr.target = buf; + lsdr.cidr4 = lsdr.cidr6 = -1; + lsdr.cb = dispatch_a; + lookup_record(T_A, &lsdr); + lsdr.cb = dispatch_aaaa; + lookup_record(T_AAAA, &lsdr); } void -dispatch_a(struct dns_rr *rr) +dispatch_a(struct dns_rr *rr, struct sender *sdr) { char buffer[512]; const char *ptr; @@ -251,12 +299,16 @@ return; if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr, - buffer, sizeof buffer))) - printf("%s\n", ptr); + buffer, sizeof buffer))) { + if (sdr->cidr4 >= 0) + printf("%s/%d\n", ptr, sdr->cidr4); + else + printf("%s\n", ptr); + } } void -dispatch_aaaa(struct dns_rr *rr) +dispatch_aaaa(struct dns_rr *rr, struct sender *sdr) { char buffer[512]; const char *ptr; @@ -265,11 +317,15 @@ return; if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6, - buffer, sizeof buffer))) - printf("%s\n", ptr); + buffer, sizeof buffer))) { + if (sdr->cidr6 >= 0) + printf("%s/%d\n", ptr, sdr->cidr6); + else + printf("%s\n", ptr); + } } -static ssize_t +ssize_t parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz) { size_t len; @@ -302,4 +358,31 @@ } return r; +} + +int +parse_sender(struct sender *sdr) +{ + const char *err; + char *m4, *m6; + + m4 = sdr->target; + strsep(&m4, "/"); + if (m4 == NULL) + return 0; + m6 = m4; + strsep(&m6, "/"); + + sdr->cidr4 = strtonum(m4, 0, 32, &err); + if (err) + return sdr->cidr4 = -1; + + if (m6 == NULL) + return 0; + + sdr->cidr6 = strtonum(m6, 0, 128, &err); + if (err) + return sdr->cidr6 = -1; + + return 1; }