Commit Diff


commit - /dev/null
commit + a248a3c15a4660190d07b7767de9998809d5b144
blob - /dev/null
blob + 0de0555c3d53d804c464490c227553854d447b28 (mode 644)
--- /dev/null
+++ Makefile
@@ -0,0 +1,7 @@
+#	$OpenBSD: Makefile,v 1.1 2018/04/26 13:57:13 eric Exp $
+
+PROG=	dnsbl
+BINDIR=	/usr/bin
+SRCS+=	main.c smtp_proc.c
+
+.include <bsd.prog.mk>
blob - /dev/null
blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644)
blob - /dev/null
blob + 4f77a04cf6f65653b40d1dcc2354aab472a27900 (mode 644)
--- /dev/null
+++ main.c
@@ -0,0 +1,139 @@
+#include <sys/socket.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "smtp_proc.h"
+
+static char **blacklists = NULL;
+static size_t nblacklists = 0;
+int retries = 0;
+enum filter_decision onfailure = FILTER_PROCEED;
+
+void usage(void);
+enum filter_decision dnsbl_connect(char *, int, time_t, char *, char *,
+    uint64_t, uint64_t, struct smtp_filter_connect *);
+
+int
+main(int argc, char *argv[])
+{
+	int ch;
+	size_t i;
+	char *line = NULL;
+	size_t linesize = 0;
+	ssize_t linelen;
+	char *msgid, *token, *ip;
+	const char *errstr;
+	int lookup;
+
+	if (pledge("stdio dns", NULL) == -1)
+		err(1, "pledge");
+
+	while ((ch = getopt(argc, argv, "b:f:r:")) != -1) {
+		switch (ch) {
+		case 'b':
+			blacklists = reallocarray(blacklists, nblacklists + 1,
+			    sizeof(*blacklists));
+			if (blacklists == NULL)
+				err(1, NULL);
+			if ((blacklists[nblacklists] = strdup(optarg)) == NULL)
+				err(1, NULL);
+			nblacklists++;
+			break;
+		case 'f':
+			if (strcasecmp(optarg, "proceed") == 0)
+				onfailure = FILTER_PROCEED;
+			else if (strcasecmp(optarg, "reject") == 0)
+				onfailure = FILTER_REJECT;
+			else if (strcasecmp(optarg, "disconnect") == 0)
+				onfailure = FILTER_DISCONNECT;
+			else if (strcasecmp(optarg, "rewrite") == 0)
+				onfailure = FILTER_REWRITE;
+			else
+				errx(1, "Invalid on failure case");
+		case 'r':
+			if ((retries = strtonum(optarg, 0, 10, &errstr)) == 0 &&
+			    errstr != NULL)
+				errx(1, "retries %s", errstr);
+			break;
+		default:
+			usage();
+		}
+	}
+
+	if (nblacklists == 0)
+		errx(1, "No blacklist specified");
+
+	smtp_register_filter_connect(dnsbl_connect);
+	smtp_run();
+
+	return 0;
+}
+
+enum filter_decision
+dnsbl_connect(char *type, int version, time_t tm, char *direction, char *phase,
+    uint64_t reqid, uint64_t token, struct smtp_filter_connect *params)
+{
+	char query[255];
+	char reply[1500];
+	struct hostent *hent;
+	u_char *addr;
+	int i;
+
+	addr = (u_char *)&(params->addr);
+	for (i = 0; i < nblacklists; i++) {
+		if (params->af == AF_INET) {
+			if (snprintf(query, sizeof(query), "%u.%u.%u.%u.%s",
+			    addr[3], addr[2], addr[1], addr[0],
+			    blacklists[i]) >= sizeof(query))
+				errx(1, "Can't create query, domain too long");
+		} else if (params->af == AF_INET6) {
+			if (snprintf(query, sizeof(query), "%hhx.%hhx.%hhx.%hhx"
+			    ".%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx"
+			    ".%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx"
+			    ".%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%s",
+			    (u_char) (addr[15] & 0xf), (u_char) (addr[15] >> 4),
+			    (u_char) (addr[14] & 0xf), (u_char) (addr[14] >> 4),
+			    (u_char) (addr[13] & 0xf), (u_char) (addr[13] >> 4),
+			    (u_char) (addr[12] & 0xf), (u_char) (addr[12] >> 4),
+			    (u_char) (addr[11] & 0xf), (u_char) (addr[11] >> 4),
+			    (u_char) (addr[10] & 0xf), (u_char) (addr[10] >> 4),
+			    (u_char) (addr[9] & 0xf), (u_char) (addr[9] >> 4),
+			    (u_char) (addr[8] & 0xf), (u_char) (addr[8] >> 4),
+			    (u_char) (addr[7] & 0xf), (u_char) (addr[8] >> 4),
+			    (u_char) (addr[6] & 0xf), (u_char) (addr[7] >> 4),
+			    (u_char) (addr[5] & 0xf), (u_char) (addr[5] >> 4),
+			    (u_char) (addr[4] & 0xf), (u_char) (addr[4] >> 4),
+			    (u_char) (addr[3] & 0xf), (u_char) (addr[3] >> 4),
+			    (u_char) (addr[2] & 0xf), (u_char) (addr[2] >> 4),
+			    (u_char) (addr[1] & 0xf), (u_char) (addr[1] >> 4),
+			    (u_char) (addr[0] & 0xf), (u_char) (addr[0] >> 4),
+			    blacklists[i]) >= sizeof(query))
+				errx(1, "Can't create query, domain too long");
+		} else
+			errx(1, "Invalid address family received");
+	}
+
+	for (i = -1; i < retries; i++) {
+		if ((hent = gethostbyname(query)) == NULL) {
+			if (h_errno == HOST_NOT_FOUND)
+				return FILTER_PROCEED;
+			if (h_errno != TRY_AGAIN)
+				return onfailure;
+		}
+	}
+	return FILTER_REJECT;
+}
+
+void
+usage(void)
+{
+	fprintf(stderr, "usage: %s [-b blacklist]\n",
+	    getprogname());
+	exit(1);
+}
blob - /dev/null
blob + 187b7cd5c48a092120a73d390b13c92987fb1d82 (mode 644)
--- /dev/null
+++ smtp_proc.c
@@ -0,0 +1,266 @@
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "smtp_proc.h"
+
+#define NITEMS(x) (sizeof(x) / sizeof(*x))
+
+typedef int (*smtp_cb)(char *, int, time_t, char *, char *, uint64_t, uint64_t,
+    void *);
+
+struct smtp_callback;
+
+static int smtp_register(char *, char *, char *, smtp_cb);
+static void smtp_connect(struct smtp_callback *, int, time_t, uint64_t,
+    uint64_t, char *);
+static void smtp_data(struct smtp_callback *, int, time_t, uint64_t, uint64_t,
+    char *);
+static void smtp_data_line(struct smtp_callback *, int, time_t, uint64_t,
+    uint64_t, char *);
+static void smtp_rset(struct smtp_callback *, int, time_t, uint64_t, uint64_t,
+    char *);
+static void smtp_quit(struct smtp_callback *, int, time_t, uint64_t, uint64_t,
+    char *);
+static void smtp_noop(struct smtp_callback *, int, time_t, uint64_t, uint64_t,
+    char *);
+static void smtp_help(struct smtp_callback *, int, time_t, uint64_t, uint64_t,
+    char *);
+static void smtp_wiz(struct smtp_callback *, int, time_t, uint64_t, uint64_t,
+    char *);
+static void smtp_commit(struct smtp_callback *, int, time_t, uint64_t, uint64_t,
+    char *);
+static void smtp_handle(struct smtp_callback *, int, time_t, uint64_t, uint64_t,
+    void *);
+
+struct smtp_callback {
+	char *type;
+	char *phase;
+	char *direction;
+	void (*smtp_parse)(struct smtp_callback *, int, time_t, uint64_t,
+	    uint64_t, char *);
+	smtp_cb cb;
+} smtp_callbacks[] = {
+	{"filter", "connect", "smtp-in", smtp_connect, NULL},
+	{"filter", "data", "smtp-in", smtp_data, NULL},
+	{"filter", "data-line", "smtp-in", smtp_data_line, NULL},
+	{"filter", "rset", "smtp-in", smtp_rset, NULL},
+	{"filter", "quit", "smtp-in", smtp_quit, NULL},
+	{"filter", "noop", "smtp-in", smtp_noop, NULL},
+	{"filter", "help", "smtp-in", smtp_help, NULL},
+	{"filter", "wiz", "smtp-in", smtp_wiz, NULL},
+	{"filter", "commit", "smtp-in", smtp_commit, NULL}
+};
+
+static int ready = 0;
+
+int
+smtp_register_filter_connect(enum filter_decision (*cb)(char *, int, time_t,
+    char *, char *, uint64_t, uint64_t, struct smtp_filter_connect *))
+{
+	return smtp_register("filter", "connect", "smtp-in", (smtp_cb) cb);
+}
+
+void
+smtp_run(void)
+{
+	char *line = NULL;
+	size_t linesize = 0;
+	ssize_t linelen;
+	char *start, *end, *type, *direction, *phase, *params;
+	int version;
+	time_t tm;
+	uint64_t reqid, token;
+	int i;
+
+	printf("register|ready\n");
+	ready = 1;
+
+	while ((linelen = getline(&line, &linesize, stdin)) != -1) {
+		if (line[linelen - 1] != '\n')
+			errx(1, "Invalid line received: missing newline");
+		line[linelen - 1] = '\0';
+		type = line;
+		if ((start = strchr(type, '|')) == NULL)
+			errx(1, "Invalid line received: missing version");
+		start++[0] = '\0';
+		if ((end = strchr(start, '|')) == NULL)
+			errx(1, "Invalid line received: missing time");
+		end++[0] = '\0';
+		if (strcmp(start, "1") != 0)
+			errx(1, "Unsupported protocol received: %s", start);
+		version = 1;
+		start = end;
+		if ((direction = strchr(start, '|')) == NULL)
+			errx(1, "Invalid line received: missing direction");
+		direction++[0] = '\0';
+		tm = (time_t) strtoull(start, &end, 10);
+		if (start[0] == '\0' || end[0] != '\0')
+			errx(1, "Invalid line received: invalid timestamp");
+		if ((phase = strchr(direction, '|')) == NULL)
+			errx(1, "Invalid line receieved: missing phase");
+		phase++[0] = '\0';
+		if ((start = strchr(phase, '|')) == NULL)
+			errx(1, "Invalid line received: missing reqid");
+		start++[0] = '\0';
+		reqid = strtoull(start, &end, 16);
+		if (start[0] == '|' || end[0] != '|')
+			errx(1, "Invalid line received: invalid reqid");
+		end++[0] = '\0';
+		start = end;
+		token = strtoull(start, &end, 16);
+		if (start[0] == '|' || end[0] != '|')
+			errx(1, "Invalid line received: invalid token");
+		params = end + 1;
+
+		for (i = 0; i < NITEMS(smtp_callbacks); i++) {
+			if (strcmp(type, smtp_callbacks[i].type) == 0 &&
+			    strcmp(phase, smtp_callbacks[i].phase) == 0 &&
+			    strcmp(direction, smtp_callbacks[i].direction) == 0)
+				break;
+		}
+		if (i == NITEMS(smtp_callbacks)) {
+			errx(1, "Invalid line received: received unregistered "
+			    "%s: %s", type, phase);
+		}
+		smtp_callbacks[i].smtp_parse(&(smtp_callbacks[i]), version, tm,
+		    reqid, token, params);
+	}
+}
+
+static void
+smtp_connect(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+	struct smtp_filter_connect sfconnect;
+	char *address;
+	int ret;
+
+	sfconnect.hostname = params;
+	if ((address = strchr(params, '|')) == NULL)
+		errx(1, "Invalid line received: missing address");
+	address++[0] = '\0';
+
+	sfconnect.af = AF_INET;
+	if (strncasecmp(address, "ipv6:", 5) == 0) {
+		sfconnect.af = AF_INET6;
+		address += 5;
+	}
+
+	ret = inet_pton(sfconnect.af, address, sfconnect.af == AF_INET ?
+	    (void *)&(sfconnect.addr) : (void *)&(sfconnect.addr6));
+	if (ret == 0)
+		errx(1, "Invalid line received: Couldn't parse address");
+	if (ret == -1)
+		err(1, "Couldn't convert address");
+
+	smtp_handle(cb, version, tm, reqid, token, (void *)(&sfconnect));
+}
+
+static void 
+smtp_data(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+}
+
+static void 
+smtp_data_line(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+}
+
+static void 
+smtp_rset(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+}
+
+static void 
+smtp_quit(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+}
+
+static void 
+smtp_noop(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+}
+
+static void 
+smtp_help(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+}
+
+static void 
+smtp_wiz(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+}
+
+static void 
+smtp_commit(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, char *params)
+{
+}
+
+static void
+smtp_handle(struct smtp_callback *cb, int version, time_t tm, uint64_t reqid,
+    uint64_t token, void *params)
+{
+	int ret;
+
+	switch (cb->cb(cb->type, version, tm, cb->direction, cb->phase, reqid,
+	    token, params)) {
+	case FILTER_PROCEED:
+		printf("filter-result|%016"PRIx64"|%016"PRIx64"|proceed\n",
+		    token, reqid);
+		break;
+	case FILTER_REJECT:
+		printf("filter-result|%016"PRIx64"|%016"PRIx64"|reject|%s\n",
+		    token, reqid, "451 Proper message later");
+		break;
+	case FILTER_DISCONNECT:
+		printf("filter-result|%016"PRIx64"|%016"PRIx64"|disconnect|"
+		    "%s\n", token, reqid, "421 Proper message later");
+		break;
+	case FILTER_REWRITE:
+		errx(1, "Not sure what is intended here yet");
+	}
+}
+
+static int
+smtp_register(char *type, char *phase, char *direction, smtp_cb cb)
+{
+	int i;
+
+	if (ready) {
+		errx(1, "Can't register when proc is running");
+	}
+	for (i = 0; i < NITEMS(smtp_callbacks); i++) {
+		if (strcmp(type, smtp_callbacks[i].type) == 0 &&
+		    strcmp(phase, smtp_callbacks[i].phase) == 0 &&
+		    strcmp(direction, smtp_callbacks[i].direction) == 0) {
+			if (smtp_callbacks[i].cb != NULL) {
+				errno = EALREADY;
+				return -1;
+			}
+			smtp_callbacks[i].cb = cb;
+			printf("register|%s|%s|%s\n", type, direction, phase);
+			return 0;
+		}
+	}
+	errno = EINVAL;
+	return -1;
+}
+
+
+//filter|1|1553668146|smtp-in|connect|478cc771bf86f378|5161a9ce4540b4d1|<unknown>|100.64.7.2
blob - /dev/null
blob + e190c1c0fa454eb54f08eb9c220e6d24132495f3 (mode 644)
--- /dev/null
+++ smtp_proc.h
@@ -0,0 +1,21 @@
+#include <netinet/in.h>
+
+struct smtp_filter_connect {
+	char *hostname;
+	int af;
+	union {
+		struct in_addr addr;
+		struct in6_addr addr6;
+	};
+};
+
+enum filter_decision {
+	FILTER_PROCEED = 0,
+	FILTER_REJECT,
+	FILTER_DISCONNECT,
+	FILTER_REWRITE
+};
+
+int smtp_register_filter_connect(enum filter_decision (*cb)(char *, int, time_t,
+    char *, char *, uint64_t, uint64_t, struct smtp_filter_connect *));
+void smtp_run(void);