Import gilles@'s standalone spfwalk utility into smtpctl(8) as
authorsunil <sunil@openbsd.org>
Sat, 6 Jan 2018 07:59:27 +0000 (07:59 +0000)
committersunil <sunil@openbsd.org>
Sat, 6 Jan 2018 07:59:27 +0000 (07:59 +0000)
'spf walk' command.

Ok gilles@

usr.sbin/smtpd/smtpctl.8
usr.sbin/smtpd/smtpctl.c
usr.sbin/smtpd/smtpctl/Makefile
usr.sbin/smtpd/spfwalk.c [new file with mode: 0644]

index e231fe6..4db465c 100644 (file)
@@ -1,4 +1,4 @@
-.\" $OpenBSD: smtpctl.8,v 1.59 2017/05/23 06:55:05 jmc Exp $
+.\" $OpenBSD: smtpctl.8,v 1.60 2018/01/06 07:59:27 sunil Exp $
 .\"
 .\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org>
 .\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: May 23 2017 $
+.Dd $Mdocdate: January 6 2018 $
 .Dt SMTPCTL 8
 .Os
 .Sh NAME
@@ -245,6 +245,12 @@ Displays runtime statistics concerning
 .Xr smtpd 8 .
 .It Cm show status
 Shows if MTA, MDA and SMTP systems are currently running or paused.
+.It Cm spf walk
+Recursively lookup SPF records for the domains read from stdin.
+For example:
+.Bd -literal -offset indent
+# smtpctl spf walk < domains.txt
+.Ed
 .It Cm trace Ar subsystem
 Enables real-time tracing of
 .Ar subsystem .
index bd85863..ec2320b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: smtpctl.c,v 1.154 2017/07/27 18:48:30 sunil Exp $     */
+/*     $OpenBSD: smtpctl.c,v 1.155 2018/01/06 07:59:27 sunil Exp $     */
 
 /*
  * Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -69,6 +69,8 @@ static int is_gzip_buffer(const char *);
 static FILE *offline_file(void);
 static void sendmail_compat(int, char **);
 
+extern int     do_spfwalk(int, struct parameter *);
+
 extern char    *__progname;
 int             sendmail;
 struct smtpd   *env;
@@ -1067,6 +1069,7 @@ main(int argc, char **argv)
        cmd_install("show routes",              do_show_routes);
        cmd_install("show stats",               do_show_stats);
        cmd_install("show status",              do_show_status);
+       cmd_install("spf walk",                 do_spfwalk);
        cmd_install("trace <str>",              do_trace);
        cmd_install("uncorrupt <msgid>",        do_uncorrupt);
        cmd_install("unprofile <str>",          do_unprofile);
index 0355bb9..73d990f 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.45 2017/07/03 22:21:47 espie Exp $
+#      $OpenBSD: Makefile,v 1.46 2018/01/06 07:59:27 sunil Exp $
 
 .PATH:         ${.CURDIR}/..
 
@@ -46,7 +46,9 @@ SRCS+=        table_static.c
 SRCS+= table_db.c
 SRCS+= table_getpwnam.c
 SRCS+= table_proc.c
+SRCS+= unpack_dns.c
+SRCS+= spfwalk.c
 
-LDADD+=        -lutil -lz -lcrypto
-DPADD+=        ${LIBUTIL} ${LIBZ} ${LIBCRYPTO}
+LDADD+=        -levent -lutil -lz -lcrypto
+DPADD+=        ${LIBEVENT} ${LIBUTIL} ${LIBZ} ${LIBCRYPTO}
 .include <bsd.prog.mk>
diff --git a/usr.sbin/smtpd/spfwalk.c b/usr.sbin/smtpd/spfwalk.c
new file mode 100644 (file)
index 0000000..833841a
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * 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 <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <netdb.h>
+
+#include <asr.h>
+#include <ctype.h>
+#include <err.h>
+#include <event.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include "smtpd-defines.h"
+#include "unpack_dns.h"
+#include "parser.h"
+
+int do_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_record(struct asr_result *, void *);
+
+int     ip_v4 = 0;
+int     ip_v6 = 0;
+int     ip_both = 1;
+
+int
+do_spfwalk(int argc, struct parameter *argv)
+{
+       struct passwd   *pw;
+       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");
+       }
+
+       argv += optind;
+       argc -= optind;
+
+       if ((pw = getpwnam(SMTPD_USER)) == NULL)
+               errx(1, "unknown user " SMTPD_USER);
+
+       if (setgroups(1, &pw->pw_gid) ||
+           setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
+           setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
+               err(1, "do_spfwalk: cannot drop privileges");
+
+       event_init();
+
+       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);
+       }
+
+       free(line);
+
+       if (pledge("dns stdio", NULL) == -1)
+               err(1, "pledge");
+
+       event_dispatch();
+
+       return 0;
+}
+
+void
+lookup_record(int type, const char *record, void (*cb)(struct dns_rr *))
+{
+       struct asr_query *as;
+
+       as = res_query_async(record, C_IN, type, NULL);
+       if (as == NULL)
+               err(1, "res_query_async");
+       event_asr_run(as, dispatch_record, cb);
+}
+
+void
+dispatch_record(struct asr_result *ar, void *arg)
+{
+       void (*cb)(struct dns_rr *) = 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)
+               return;
+
+       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);
+               /**/
+               cb(&rr);
+       }
+}
+
+void
+dispatch_txt(struct dns_rr *rr)
+{
+        char buf[512];
+        char buf2[512];
+        char *in = buf;
+        char *argv[512];
+        char **ap = argv;
+
+       print_dname(rr->rr.other.rdata, buf, sizeof(buf));
+       buf[strlen(buf) - 1] = '\0';
+       if (buf[strlen(buf) - 1] == '.')
+               buf[strlen(buf) - 1] = '\0';
+       if (strncasecmp("v=spf1 ", buf, 7))
+               return;
+
+       while ((*ap = strsep(&in, " ")) != NULL) {
+               if (strcasecmp(*ap, "v=spf1") == 0)
+                       continue;
+
+               if (strncasecmp("ip4:", *ap, 4) == 0) {
+                       if (ip_v4 == 1 || ip_both == 1)
+                               printf("%s\n", *(ap) + 4);
+                       continue;
+               }
+               if (strncasecmp("ip6:", *ap, 4) == 0) {
+                       if (ip_v6 == 1 || ip_both == 1)
+                               printf("%s\n", *(ap) + 4);
+                       continue;
+               }
+               if (strncasecmp("+ip4:", *ap, 5) == 0) {
+                       if (ip_v4 == 1 || ip_both == 1)
+                               printf("%s\n", *(ap) + 5);
+                       continue;
+               }
+               if (strncasecmp("+ip6:", *ap, 5) == 0) {
+                       if (ip_v6 == 1 || ip_both == 1)
+                               printf("%s\n", *(ap) + 5);
+                       continue;
+               }
+               if (strncasecmp("include:", *ap, 8) == 0) {
+                       lookup_record(T_TXT, *(ap) + 8, dispatch_txt);
+                       continue;
+               }
+               if (strncasecmp("redirect=", *ap, 9) == 0) {
+                       lookup_record(T_TXT, *(ap) + 9, dispatch_txt);
+                       continue;
+               }
+               if (strcasecmp(*ap, "mx") == 0 || strcasecmp(*ap, "+mx") == 0) {
+                       print_dname(rr->rr_dname, buf2, sizeof(buf2));
+                       buf2[strlen(buf2) - 1] = '\0';
+                       lookup_record(T_MX, buf2, dispatch_mx);
+                       continue;
+               }
+               if (strcasecmp(*ap, "a") == 0 || strcasecmp(*ap, "+a") == 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);
+                       continue;
+               }
+       }
+       *ap = NULL;
+}
+
+void
+dispatch_mx(struct dns_rr *rr)
+{
+       char buf[512];
+
+       print_dname(rr->rr.mx.exchange, buf, sizeof(buf));
+       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);
+}
+
+void
+dispatch_a(struct dns_rr *rr)
+{
+       char buffer[512];
+       const char *ptr;
+
+       if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr,
+           buffer, sizeof buffer)))
+               printf("%s\n", ptr);
+}
+
+void
+dispatch_aaaa(struct dns_rr *rr)
+{
+       char buffer[512];
+       const char *ptr;
+
+       if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6,
+           buffer, sizeof buffer)))
+               printf("%s\n", ptr);
+}