diff options
Diffstat (limited to 'smtpd/spfwalk.c')
-rw-r--r-- | smtpd/spfwalk.c | 391 |
1 files changed, 0 insertions, 391 deletions
diff --git a/smtpd/spfwalk.c b/smtpd/spfwalk.c deleted file mode 100644 index 0832d1bc..00000000 --- a/smtpd/spfwalk.c +++ /dev/null @@ -1,391 +0,0 @@ -/* - * Copyright (c) 2017 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include "includes.h" - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/tree.h> - -#ifdef HAVE_ARPA_NAMESER_COMPAT_H -#include <arpa/nameser_compat.h> -#endif -#include <arpa/inet.h> -#include <arpa/nameser.h> -#include <netinet/in.h> -#include <netdb.h> - -#include <asr.h> -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <strings.h> -#include <unistd.h> - -#include "smtpd-defines.h" -#include "smtpd-api.h" -#include "unpack_dns.h" -#include "parser.h" - -struct target { - void (*dispatch)(struct dns_rr *, struct target *); - int cidr4; - int cidr6; -}; - -int spfwalk(int, struct parameter *); - -static void dispatch_txt(struct dns_rr *, struct target *); -static void dispatch_mx(struct dns_rr *, struct target *); -static void dispatch_a(struct dns_rr *, struct target *); -static void dispatch_aaaa(struct dns_rr *, struct target *); -static void lookup_record(int, const char *, struct target *); -static void dispatch_record(struct asr_result *, void *); -static ssize_t parse_txt(const char *, size_t, char *, size_t); -static int parse_target(char *, struct target *); -void *xmalloc(size_t size); - -int ip_v4 = 0; -int ip_v6 = 0; -int ip_both = 1; - -struct dict seen; - -int -spfwalk(int argc, struct parameter *argv) -{ - struct target tgt; - const char *ip_family = NULL; - char *line = NULL; - size_t linesize = 0; - ssize_t linelen; - - if (argv) - ip_family = argv[0].u.u_str; - - if (ip_family) { - if (strcmp(ip_family, "-4") == 0) { - ip_both = 0; - ip_v4 = 1; - } else if (strcmp(ip_family, "-6") == 0) { - ip_both = 0; - ip_v6 = 1; - } else - errx(1, "invalid ip_family"); - } - - dict_init(&seen); - event_init(); - - tgt.cidr4 = tgt.cidr6 = -1; - tgt.dispatch = dispatch_txt; - - while ((linelen = getline(&line, &linesize, stdin)) != -1) { - while (linelen-- > 0 && isspace((unsigned char)line[linelen])) - line[linelen] = '\0'; - - if (linelen > 0) - lookup_record(T_TXT, line, &tgt); - } - - free(line); - -#if HAVE_PLEDGE - if (pledge("dns stdio", NULL) == -1) - err(1, "pledge"); -#endif - - event_dispatch(); - - return 0; -} - -void -lookup_record(int type, const char *record, struct target *tgt) -{ - struct asr_query *as; - struct target *ntgt; - - as = res_query_async(record, C_IN, type, NULL); - if (as == NULL) - err(1, "res_query_async"); - ntgt = xmalloc(sizeof(*ntgt)); - *ntgt = *tgt; - event_asr_run(as, dispatch_record, (void *)ntgt); -} - -void -dispatch_record(struct asr_result *ar, void *arg) -{ - struct target *tgt = arg; - struct unpack pack; - struct dns_header h; - struct dns_query q; - struct dns_rr rr; - - /* best effort */ - if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA) - goto end; - - unpack_init(&pack, ar->ar_data, ar->ar_datalen); - unpack_header(&pack, &h); - unpack_query(&pack, &q); - - for (; h.ancount; h.ancount--) { - unpack_rr(&pack, &rr); - /**/ - tgt->dispatch(&rr, tgt); - } -end: - free(tgt); -} - -void -dispatch_txt(struct dns_rr *rr, struct target *tgt) -{ - char buf[4096]; - char *argv[512]; - char buf2[512]; - struct target ltgt; - struct in6_addr ina; - char **ap = argv; - char *in = buf; - char *record, *end; - ssize_t n; - - if (rr->rr_type != T_TXT) - return; - n = parse_txt(rr->rr.other.rdata, rr->rr.other.rdlen, buf, sizeof(buf)); - if (n == -1 || n == sizeof(buf)) - return; - buf[n] = '\0'; - - if (strncasecmp("v=spf1 ", buf, 7)) - return; - - while ((*ap = strsep(&in, " ")) != NULL) { - if (strcasecmp(*ap, "v=spf1") == 0) - continue; - - end = *ap + strlen(*ap)-1; - if (*end == '.') - *end = '\0'; - - if (dict_set(&seen, *ap, &seen)) - continue; - - if (**ap == '-' || **ap == '~') - continue; - - if (**ap == '+' || **ap == '?') - (*ap)++; - - ltgt.cidr4 = ltgt.cidr6 = -1; - - if (strncasecmp("ip4:", *ap, 4) == 0) { - if ((ip_v4 == 1 || ip_both == 1) && - inet_net_pton(AF_INET, *(ap) + 4, - &ina, sizeof(ina)) != -1) - printf("%s\n", *(ap) + 4); - continue; - } - if (strncasecmp("ip6:", *ap, 4) == 0) { - if ((ip_v6 == 1 || ip_both == 1) && - inet_net_pton(AF_INET6, *(ap) + 4, - &ina, sizeof(ina)) != -1) - printf("%s\n", *(ap) + 4); - continue; - } - if (strcasecmp("a", *ap) == 0) { - print_dname(rr->rr_dname, buf2, sizeof(buf2)); - buf2[strlen(buf2) - 1] = '\0'; - ltgt.dispatch = dispatch_a; - lookup_record(T_A, buf2, <gt); - ltgt.dispatch = dispatch_aaaa; - lookup_record(T_AAAA, buf2, <gt); - continue; - } - if (strncasecmp("a:", *ap, 2) == 0) { - record = *(ap) + 2; - if (parse_target(record, <gt) < 0) - continue; - ltgt.dispatch = dispatch_a; - lookup_record(T_A, record, <gt); - ltgt.dispatch = dispatch_aaaa; - lookup_record(T_AAAA, record, <gt); - continue; - } - if (strncasecmp("exists:", *ap, 7) == 0) { - ltgt.dispatch = dispatch_a; - lookup_record(T_A, *(ap) + 7, <gt); - continue; - } - if (strncasecmp("include:", *ap, 8) == 0) { - ltgt.dispatch = dispatch_txt; - lookup_record(T_TXT, *(ap) + 8, <gt); - continue; - } - if (strncasecmp("redirect=", *ap, 9) == 0) { - ltgt.dispatch = dispatch_txt; - lookup_record(T_TXT, *(ap) + 9, <gt); - continue; - } - if (strcasecmp("mx", *ap) == 0) { - print_dname(rr->rr_dname, buf2, sizeof(buf2)); - buf2[strlen(buf2) - 1] = '\0'; - ltgt.dispatch = dispatch_mx; - lookup_record(T_MX, buf2, <gt); - continue; - } - if (strncasecmp("mx:", *ap, 3) == 0) { - record = *(ap) + 3; - if (parse_target(record, <gt) < 0) - continue; - ltgt.dispatch = dispatch_mx; - lookup_record(T_MX, record, <gt); - continue; - } - } - *ap = NULL; -} - -void -dispatch_mx(struct dns_rr *rr, struct target *tgt) -{ - char buf[512]; - struct target ltgt; - - if (rr->rr_type != T_MX) - return; - - print_dname(rr->rr.mx.exchange, buf, sizeof(buf)); - buf[strlen(buf) - 1] = '\0'; - if (buf[strlen(buf) - 1] == '.') - buf[strlen(buf) - 1] = '\0'; - - ltgt = *tgt; - ltgt.dispatch = dispatch_a; - lookup_record(T_A, buf, <gt); - ltgt.dispatch = dispatch_aaaa; - lookup_record(T_AAAA, buf, <gt); -} - -void -dispatch_a(struct dns_rr *rr, struct target *tgt) -{ - char buffer[512]; - const char *ptr; - - if (rr->rr_type != T_A) - return; - - if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr, - buffer, sizeof buffer))) { - if (tgt->cidr4 >= 0) - printf("%s/%d\n", ptr, tgt->cidr4); - else - printf("%s\n", ptr); - } -} - -void -dispatch_aaaa(struct dns_rr *rr, struct target *tgt) -{ - char buffer[512]; - const char *ptr; - - if (rr->rr_type != T_AAAA) - return; - - if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6, - buffer, sizeof buffer))) { - if (tgt->cidr6 >= 0) - printf("%s/%d\n", ptr, tgt->cidr6); - else - printf("%s\n", ptr); - } -} - -ssize_t -parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz) -{ - size_t len; - ssize_t r = 0; - - while (rdatalen) { - len = *(const unsigned char *)rdata; - if (len >= rdatalen) { - errno = EINVAL; - return -1; - } - - rdata++; - rdatalen--; - - if (len == 0) - continue; - - if (len >= dstsz) { - errno = EOVERFLOW; - return -1; - } - memmove(dst, rdata, len); - dst += len; - dstsz -= len; - - rdata += len; - rdatalen -= len; - r += len; - } - - return r; -} - -int -parse_target(char *record, struct target *tgt) -{ - const char *err; - char *m4, *m6; - - m4 = record; - strsep(&m4, "/"); - if (m4 == NULL) - return 0; - - m6 = m4; - strsep(&m6, "/"); - - if (*m4) { - tgt->cidr4 = strtonum(m4, 0, 32, &err); - if (err) - return tgt->cidr4 = -1; - } - - if (m6 == NULL) - return 0; - - tgt->cidr6 = strtonum(m6, 0, 128, &err); - if (err) - return tgt->cidr6 = -1; - - return 0; -} |