commit a248a3c15a4660190d07b7767de9998809d5b144 from: Martijn van Duren date: Wed Mar 27 10:27:53 2019 UTC initial import. Basics appear functional 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 blob - /dev/null blob + e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (mode 644) blob - /dev/null blob + 4f77a04cf6f65653b40d1dcc2354aab472a27900 (mode 644) --- /dev/null +++ main.c @@ -0,0 +1,139 @@ +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#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||100.64.7.2 blob - /dev/null blob + e190c1c0fa454eb54f08eb9c220e6d24132495f3 (mode 644) --- /dev/null +++ smtp_proc.h @@ -0,0 +1,21 @@ +#include + +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);