summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd
diff options
context:
space:
mode:
authorJacek Masiulaniec <jacekm@cvs.openbsd.org>2009-05-09 17:04:56 +0000
committerJacek Masiulaniec <jacekm@cvs.openbsd.org>2009-05-09 17:04:56 +0000
commit6a0f971e887cf324c3d90a90f5401030ddde6b66 (patch)
treedca1622b451da6546ae20e01de668dcff536a4af /usr.sbin/smtpd
parentbad8e4fca758e7b8298e71bfee6628ee4342178b (diff)
- New API to handle all DNS query types (A, MX, PTR) asynchronously.
- Improve RFC compliance: CNAMEs are resolved, equal preference MXs are randomized, relaying via MX that has equal/lower preference than local server is prevented, decision on when to treat domain name as implicit MX is better. ok gilles@
Diffstat (limited to 'usr.sbin/smtpd')
-rw-r--r--usr.sbin/smtpd/dns.c565
-rw-r--r--usr.sbin/smtpd/lka.c226
-rw-r--r--usr.sbin/smtpd/mta.c119
-rw-r--r--usr.sbin/smtpd/smtp.c20
-rw-r--r--usr.sbin/smtpd/smtpd.h45
5 files changed, 591 insertions, 384 deletions
diff --git a/usr.sbin/smtpd/dns.c b/usr.sbin/smtpd/dns.c
index 433f9396917..db9b8c3526e 100644
--- a/usr.sbin/smtpd/dns.c
+++ b/usr.sbin/smtpd/dns.c
@@ -1,7 +1,8 @@
-/* $OpenBSD: dns.c,v 1.10 2009/02/22 11:44:29 form Exp $ */
+/* $OpenBSD: dns.c,v 1.11 2009/05/09 17:04:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
+ * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -28,169 +29,487 @@
#include <event.h>
#include <netdb.h>
#include <resolv.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <unistd.h>
#include "smtpd.h"
-struct mxrecord {
- char hostname[MAXHOSTNAMELEN];
- u_int16_t priority;
+struct resdata {
+ struct imsgbuf ibuf;
+ struct imsgbuf *asker;
};
-static void mxsort(struct mxrecord *, size_t);
+struct mx {
+ char host[MAXHOSTNAMELEN];
+ double prio;
+};
+
+void parent_dispatch_dns(int, short, void *);
+
+int dns(void);
+void dns_dispatch_parent(int, short, void *);
+void lookup_a(struct imsgbuf *, struct dns *, int);
+void lookup_mx(struct imsgbuf *, struct dns *);
+int get_mxlist(char *, char *, struct dns **);
+void free_mxlist(struct dns *);
+int mxcmp(const void *, const void *);
+void lookup_ptr(struct imsgbuf *, struct dns *);
+
+/*
+ * User interface.
+ */
+
+void
+dns_query_a(struct smtpd *env, char *host, int port, u_int64_t id)
+{
+ struct dns query;
+
+ bzero(&query, sizeof(query));
+ strlcpy(query.host, host, sizeof(query.host));
+ query.port = port;
+ query.id = id;
+
+ imsg_compose(env->sc_ibufs[PROC_LKA], IMSG_DNS_A, 0, 0, -1, &query,
+ sizeof(query));
+}
+
+void
+dns_query_mx(struct smtpd *env, char *host, int port, u_int64_t id)
+{
+ struct dns query;
+
+ bzero(&query, sizeof(query));
+ strlcpy(query.host, host, sizeof(query.host));
+ query.port = port;
+ query.id = id;
+
+ imsg_compose(env->sc_ibufs[PROC_LKA], IMSG_DNS_MX, 0, 0, -1, &query,
+ sizeof(query));
+}
+
+void
+dns_query_ptr(struct smtpd *env, struct sockaddr_storage *ss, u_int64_t id)
+{
+ struct dns query;
+
+ bzero(&query, sizeof(query));
+ query.ss = *ss;
+ query.id = id;
+
+ imsg_compose(env->sc_ibufs[PROC_LKA], IMSG_DNS_PTR, 0, 0, -1, &query,
+ sizeof(query));
+}
+
+/*
+ * Parent resolver process interface.
+ */
+
+void
+dns_async(struct smtpd *env, struct imsgbuf *asker, int type, struct dns *query)
+{
+ struct resdata *rd;
+ int fd;
+
+ if ((rd = calloc(1, sizeof(*rd))) == NULL)
+ fatal(NULL);
+
+ rd->asker = asker;
+ query->env = env;
+
+ fd = dns();
+ imsg_init(&rd->ibuf, fd, parent_dispatch_dns);
+ rd->ibuf.events = EV_READ;
+ rd->ibuf.data = rd;
+ event_set(&rd->ibuf.ev, rd->ibuf.fd, rd->ibuf.events, rd->ibuf.handler,
+ rd->ibuf.data);
+ event_add(&rd->ibuf.ev, NULL);
+
+ imsg_compose(&rd->ibuf, type, 0, 0, -1, query, sizeof(*query));
+}
-static void
-mxsort(struct mxrecord *array, size_t len)
+void
+parent_dispatch_dns(int sig, short event, void *p)
{
- u_int32_t i;
- int32_t j;
- struct mxrecord store;
-
- for (i = 1; i < len; i++) {
- store = array[i];
- for (j = i - 1; j >= 0 && array[j].priority > store.priority;
- j--) {
- array[j + 1] = array[j];
+ struct resdata *rd = p;
+ struct imsgbuf *ibuf;
+ struct imsg imsg;
+ ssize_t n;
+
+ ibuf = &rd->ibuf;
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read_error");
+ if (n == 0)
+ fatal("parent_dispatch_dns: pipe closed");
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("parent_dispatch_dns: msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatalx("parent_dispatch_dns: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_DNS_A:
+ imsg_compose(rd->asker, IMSG_DNS_A, 0, 0, -1, imsg.data,
+ sizeof(struct dns));
+ break;
+
+ case IMSG_DNS_A_END:
+ case IMSG_DNS_PTR:
+ imsg_compose(rd->asker, imsg.hdr.type, 0, 0, -1,
+ imsg.data, sizeof(struct dns));
+ close(rd->ibuf.fd);
+ event_del(&ibuf->ev);
+ free(rd);
+ imsg_free(&imsg);
+ return;
+
+ default:
+ log_warnx("parent_dispatch_dns: got imsg %d",
+ imsg.hdr.type);
+ fatalx("parent_dispatch_dns: unexpected imsg");
}
- array[j + 1] = store;
+ imsg_free(&imsg);
}
+ imsg_event_add(ibuf);
}
+/*
+ * Helper resolver process.
+ */
+
int
-getmxbyname(char *name, char ***result)
+dns(void)
{
- union {
- u_int8_t bytes[PACKETSZ];
- HEADER header;
- } answer;
- u_int32_t i, j;
- int ret;
- u_int8_t *sp;
- u_int8_t *endp;
- u_int8_t *ptr;
- u_int16_t qdcount;
- u_int8_t expbuf[PACKETSZ];
- u_int16_t type;
- u_int16_t n;
- u_int16_t priority;
- size_t mxnb;
- struct mxrecord mxarray[MXARRAYSIZE];
- size_t chunklen;
-
- ret = res_query(name, C_IN, T_MX, (u_int8_t *)&answer.bytes,
- sizeof answer);
- if (ret == -1) {
+ int fd[2];
+ pid_t pid;
+ struct imsgbuf *ibuf;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, AF_UNSPEC, fd) == -1)
+ fatal("socketpair");
+
+ session_socket_blockmode(fd[0], BM_NONBLOCK);
+ session_socket_blockmode(fd[1], BM_NONBLOCK);
+
+ if ((pid = fork()) == -1)
+ fatal("dns: fork");
+ if (pid > 0) {
+ close(fd[1]);
+ return (fd[0]);
+ }
+ close(fd[0]);
+
+ event_base_free(NULL);
+ event_init();
+
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+
+ if ((ibuf = calloc(1, sizeof(*ibuf))) == NULL)
+ fatal(NULL);
+ imsg_init(ibuf, fd[1], dns_dispatch_parent);
+ ibuf->events = EV_READ;
+ ibuf->data = ibuf;
+ event_set(&ibuf->ev, ibuf->fd, ibuf->events, ibuf->handler, ibuf->data);
+ event_add(&ibuf->ev, NULL);
+
+ event_dispatch();
+ _exit(0);
+}
+
+void
+dns_dispatch_parent(int sig, short event, void *p)
+{
+ struct imsgbuf *ibuf = p;
+ struct imsg imsg;
+ ssize_t n;
+
+ switch (event) {
+ case EV_READ:
+ if ((n = imsg_read(ibuf)) == -1)
+ fatal("imsg_read_error");
+ if (n == 0) {
+ /* this pipe is dead, so remove the event handler */
+ event_del(&ibuf->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ break;
+ case EV_WRITE:
+ if (msgbuf_write(&ibuf->w) == -1)
+ fatal("dns_dispatch_parent: msgbuf_write");
+ imsg_event_add(ibuf);
+ return;
+ default:
+ fatalx("unknown event");
+ }
+
+ for (;;) {
+ if ((n = imsg_get(ibuf, &imsg)) == -1)
+ fatalx("dns_dispatch_parent: imsg_get error");
+ if (n == 0)
+ break;
+
+ switch (imsg.hdr.type) {
+ case IMSG_DNS_A:
+ lookup_a(ibuf, imsg.data, 1);
+ break;
+
+ case IMSG_DNS_MX:
+ lookup_mx(ibuf, imsg.data);
+ break;
+
+ case IMSG_DNS_PTR:
+ lookup_ptr(ibuf, imsg.data);
+ break;
+
+ default:
+ log_warnx("dns_dispatch_parent: got imsg %d",
+ imsg.hdr.type);
+ fatalx("dns_dispatch_parent: unexpected imsg");
+ }
+ imsg_free(&imsg);
+ }
+ imsg_event_add(ibuf);
+}
+
+void
+lookup_a(struct imsgbuf *ibuf, struct dns *query, int finalize)
+{
+ struct addrinfo *res0, *res, hints;
+ char *port = NULL;
+
+ log_debug("lookup_a %s:%d", query->host, query->port);
+
+ if (query->port && asprintf(&port, "%u", query->port) == -1)
+ fatal(NULL);
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ query->error = getaddrinfo(query->host, port, &hints, &res0);
+ if (query->error)
+ goto end;
+
+ for (res = res0; res; res = res->ai_next) {
+ memcpy(&query->ss, res->ai_addr, res->ai_addr->sa_len);
+ imsg_compose(ibuf, IMSG_DNS_A, 0, 0, -1, query, sizeof(*query));
+ }
+ freeaddrinfo(res0);
+end:
+ free(port);
+ if (finalize) {
+ log_debug("lookup_a %s", query->error ? "failed" : "success");
+ imsg_compose(ibuf, IMSG_DNS_A_END, 0, 0, -1, query,
+ sizeof(*query));
+ }
+}
+
+void
+lookup_mx(struct imsgbuf *ibuf, struct dns *query)
+{
+ struct dns *mx0, *mx;
+ int success = 0;
+
+ log_debug("lookup_mx %s", query->host);
+
+ query->error = get_mxlist(query->host, query->env->sc_hostname, &mx0);
+ if (query->error)
+ goto end;
+
+ if (mx0 == NULL) {
+ log_debug("implicit mx");
+ if ((mx0 = calloc(1, sizeof(*mx0))) == NULL)
+ fatal(NULL);
+ strlcpy(mx0->host, query->host, sizeof(mx0->host));
+ }
+
+ for (mx = mx0; mx; mx = mx->next) {
+ mx->port = query->port;
+ mx->id = query->id;
+ lookup_a(ibuf, mx, 0);
+ if (!mx->error)
+ success++;
+ }
+ free_mxlist(mx0);
+
+ if (success == 0)
+ query->error = EAI_NODATA;
+
+end:
+ log_debug("lookup_mx %s", query->error ? "failed" : "success");
+ imsg_compose(ibuf, IMSG_DNS_A_END, 0, 0, -1, query, sizeof(*query));
+}
+
+int
+get_mxlist(char *host, char *self, struct dns **res)
+{
+ struct mx tab[MAX_MX_COUNT];
+ char buf[PACKETSZ], *p, *endp;
+ int ntab, i, ret, type, n, maxprio, cname_ok = 3;
+ int qdcount, ancount;
+
+again:
+ ntab = 0;
+ maxprio = 16384;
+ ret = res_query(host, C_IN, T_MX, buf, sizeof(buf));
+ if (ret < 0) {
switch (h_errno) {
case TRY_AGAIN:
return (EAI_AGAIN);
case NO_RECOVERY:
return (EAI_FAIL);
case HOST_NOT_FOUND:
- return (EAI_NONAME);
case NO_DATA:
+ *res = NULL;
return (0);
}
- fatal("getmxbyname: res_query");
+ fatal("get_mxlist: res_query");
}
- /* sp stores start of dns packet,
- * endp stores end of dns packet,
- */
- sp = (u_int8_t *)&answer.bytes;
- endp = sp + ret;
-
- /* skip header */
- ptr = sp + HFIXEDSZ;
-
- for (qdcount = ntohs(answer.header.qdcount);
- qdcount--;
- ptr += ret + QFIXEDSZ) {
- ret = dn_skipname(ptr, endp);
- if (ret == -1)
- return 0;
+ p = buf + HFIXEDSZ;
+ endp = buf + ret;
+ qdcount = ntohs(((HEADER *)buf)->qdcount);
+ ancount = ntohs(((HEADER *)buf)->ancount);
+
+ if (qdcount < 1)
+ return (EAI_FAIL);
+ for (i = 0; i < qdcount; i++) {
+ ret = dn_skipname(p, endp);
+ if (ret < 0)
+ return (EAI_FAIL);
+ p += ret + QFIXEDSZ;
}
- mxnb = 0;
- for (; ptr < endp;) {
- memset(expbuf, 0, sizeof expbuf);
- ret = dn_expand(sp, endp, ptr, expbuf, sizeof expbuf);
- if (ret == -1)
- break;
- ptr += ret;
+ while (p < endp && ntab < ancount && ntab < MAX_MX_COUNT) {
+ ret = dn_skipname(p, endp);
+ if (ret < 0)
+ return (EAI_FAIL);
+ p += ret;
- GETSHORT(type, ptr);
- ptr += sizeof(u_int16_t) + sizeof(u_int32_t);
- GETSHORT(n, ptr);
+ GETSHORT(type, p);
+ p += sizeof(u_int16_t) + sizeof(u_int32_t);
+ GETSHORT(n, p);
+
+ if (type == T_CNAME) {
+ if (cname_ok-- == 0)
+ return (EAI_FAIL);
+ ret = dn_expand(buf, endp, p, tab[0].host,
+ sizeof(tab[0].host));
+ if (ret < 0)
+ return (EAI_FAIL);
+ host = tab[0].host;
+ goto again;
+ }
if (type != T_MX) {
- ptr += n;
+ log_warnx("get_mxlist: %s: bad rr type %d", host, type);
+ p += n;
continue;
}
- GETSHORT(priority, ptr);
- ret = dn_expand(sp, endp, ptr, expbuf, sizeof expbuf);
- if (ret == -1)
- return 0;
- ptr += ret;
-
- if (mxnb < sizeof(mxarray) / sizeof(struct mxrecord)) {
- if (strlcpy(mxarray[mxnb].hostname, expbuf,
- sizeof(mxarray[mxnb].hostname)) >=
- sizeof(mxarray[mxnb].hostname))
- return 0;
- mxarray[mxnb].priority = priority;
- }
- else {
- int tprio = 0;
-
- for (i = j = 0;
- i < sizeof(mxarray) / sizeof(struct mxrecord);
- ++i) {
- if (tprio < mxarray[i].priority) {
- tprio = mxarray[i].priority;
- j = i;
- }
- }
-
- if (mxarray[j].priority > priority) {
- if (strlcpy(mxarray[j].hostname, expbuf,
- sizeof(mxarray[j].hostname)) >=
- sizeof(mxarray[j].hostname))
- return 0;
- mxarray[j].priority = priority;
- }
- }
- ++mxnb;
+ GETSHORT(tab[ntab].prio, p);
+
+ ret = dn_expand(buf, endp, p, tab[ntab].host,
+ sizeof(tab[ntab].host));
+ if (ret < 0)
+ return (EAI_FAIL);
+ p += ret;
+
+ /*
+ * In case our name is listed as MX, prevent loops by excluding
+ * all hosts of our or greater preference number.
+ */
+ if (strcmp(self, tab[ntab].host) == 0)
+ maxprio = tab[ntab].prio;
+
+ ntab++;
}
- if (mxnb == 0)
- return 0;
+ /*
+ * Randomize equal preference hosts using the fractional part.
+ */
+ for (i = 0; i < ntab; i++)
+ tab[i].prio += (double)arc4random_uniform(ntab) / ntab;
+
+ qsort(tab, ntab, sizeof(struct mx), mxcmp);
- if (mxnb > sizeof(mxarray) / sizeof(struct mxrecord))
- mxnb = sizeof(mxarray) / sizeof(struct mxrecord);
+ for (i = 0; i < ntab; i++) {
+ log_debug("mx %s prio %f", tab[i].host, tab[i].prio);
+ if (tab[i].prio >= maxprio)
+ break;
+ if ((*res = calloc(1, sizeof(struct dns))) == NULL)
+ fatal(NULL);
+ strlcpy((*res)->host, tab[i].host, sizeof((*res)->host));
+ res = &(*res)->next;
+ }
- /* Rearrange MX records by priority */
- mxsort(mxarray, mxnb);
+ if (i == 0)
+ return (EAI_FAIL);
- chunklen = 0;
- for (i = 0; i < mxnb; ++i)
- chunklen += strlen(mxarray[i].hostname) + 1;
- chunklen += ((mxnb + 1) * sizeof(char *));
+ return (0);
+}
- *result = calloc(1, chunklen);
- if (*result == NULL)
- fatal("getmxbyname: calloc");
+void
+free_mxlist(struct dns *first)
+{
+ struct dns *mx, *next;
- ptr = (u_int8_t *)*result + (mxnb + 1) * sizeof(char *);
- for (i = 0; i < mxnb; ++i) {
- strlcpy(ptr, mxarray[i].hostname,
- strlen(mxarray[i].hostname) + 1);
- (*result)[i] = ptr;
- ptr += strlen(mxarray[i].hostname) + 1;
+ for (mx = first; mx; mx = next) {
+ next = mx->next;
+ free(mx);
}
- (*result)[i] = NULL;
+}
- return mxnb;
+int
+mxcmp(const void *va, const void *vb)
+{
+ const struct mx *a = va;
+ const struct mx *b = vb;
+
+ if (a->prio > b->prio)
+ return (1);
+ else if (a->prio < b->prio)
+ return (-1);
+ else
+ return (0);
+}
+
+void
+lookup_ptr(struct imsgbuf *ibuf, struct dns *query)
+{
+ struct addrinfo *res, hints;
+
+ log_debug("lookup_ptr %s", ss_to_text(&query->ss));
+
+ query->error = getnameinfo((struct sockaddr *)&query->ss,
+ query->ss.ss_len, query->host, sizeof(query->host), NULL, 0,
+ NI_NAMEREQD);
+ if (query->error)
+ goto end;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+
+ if (getaddrinfo(query->host, NULL, &hints, &res) == 0) {
+ query->error = EAI_NODATA;
+ freeaddrinfo(res);
+ }
+end:
+ log_debug("lookup_ptr %s", query->error ? "failed" : "success");
+ imsg_compose(ibuf, IMSG_DNS_PTR, 0, 0, -1, query, sizeof(*query));
}
diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c
index b9bfec2d841..2f0bc905446 100644
--- a/usr.sbin/smtpd/lka.c
+++ b/usr.sbin/smtpd/lka.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka.c,v 1.44 2009/05/01 21:44:19 gilles Exp $ */
+/* $OpenBSD: lka.c,v 1.45 2009/05/09 17:04:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -22,11 +22,13 @@
#include <sys/tree.h>
#include <sys/param.h>
#include <sys/socket.h>
+#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
+#include <errno.h>
#include <event.h>
#include <netdb.h>
#include <pwd.h>
@@ -69,16 +71,23 @@ int lka_expand_rcpt_iteration(struct smtpd *, struct aliaseslist *, struct lkas
void lka_rcpt_action(struct smtpd *, struct path *);
void lka_clear_aliaseslist(struct aliaseslist *);
int lka_encode_credentials(char *, char *);
-void lka_dns_reverse(struct session *s);
void
lka_sig_handler(int sig, short event, void *p)
{
+ int status;
+ pid_t pid;
+
switch (sig) {
case SIGINT:
case SIGTERM:
lka_shutdown();
break;
+ case SIGCHLD:
+ do {
+ pid = waitpid(-1, &status, WNOHANG);
+ } while (pid > 0 || (pid == -1 && errno == EINTR));
+ break;
default:
fatalx("lka_sig_handler: unexpected signal");
}
@@ -358,173 +367,31 @@ lka_dispatch_mta(int sig, short event, void *p)
break;
switch (imsg.hdr.type) {
- case IMSG_LKA_MX: {
- struct mxreq *mxreq;
- struct mxrep mxrep;
- struct addrinfo hints, *res, *resp;
- char **mx = NULL;
- char *lmx[1];
- int len, i;
- int mxcnt;
- int error;
- struct mxhost mxhost;
- char *secret = NULL;
-
- mxreq = imsg.data;
- mxrep.id = mxreq->id;
- mxrep.getaddrinfo_error = 0;
-
- if (mxreq->rule.r_action == A_RELAY) {
- log_debug("attempting to resolve %s", mxreq->hostname);
- len = getmxbyname(mxreq->hostname, &mx);
- if (len < 0) {
- mxrep.getaddrinfo_error = len;
- imsg_compose(ibuf, IMSG_LKA_MX_END, 0, 0, -1,
- &mxrep, sizeof(struct mxrep));
- break;
- }
- if (len == 0) {
- lmx[0] = mxreq->hostname;
- mx = lmx;
- len = 1;
- }
- }
- else if (mxreq->rule.r_action == A_RELAYVIA) {
- lmx[0] = mxreq->rule.r_value.relayhost.hostname;
- log_debug("attempting to resolve %s:%d (forced)", lmx[0],
- ntohs(mxreq->rule.r_value.relayhost.port));
- mx = lmx;
- len = 1;
-
- }
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_protocol = IPPROTO_TCP;
-
- for (i = 0; i < len; ++i) {
-
- error = getaddrinfo(mx[i], NULL, &hints, &res);
- if (error) {
- mxrep.getaddrinfo_error = error;
- imsg_compose(ibuf, IMSG_LKA_MX_END, 0, 0, -1,
- &mxrep, sizeof(struct mxrep));
- }
-
- if (mxreq->rule.r_action == A_RELAYVIA)
- mxhost.flags = mxreq->rule.r_value.relayhost.flags;
-
- if (mxhost.flags & F_AUTH) {
- secret = map_dblookup(env, "secrets", mx[i]);
- if (secret == NULL) {
- log_warnx("no credentials for relay through \"%s\"", mx[i]);
- freeaddrinfo(res);
- continue;
- }
- }
- if (secret)
- lka_encode_credentials(mxhost.credentials, secret);
-
- for (resp = res; resp != NULL && mxcnt < MAX_MX_COUNT; resp = resp->ai_next) {
-
- if (resp->ai_family == PF_INET) {
- struct sockaddr_in *ssin;
-
- mxhost.ss = *(struct sockaddr_storage *)resp->ai_addr;
-
- ssin = (struct sockaddr_in *)&mxhost.ss;
- if (mxreq->rule.r_value.relayhost.port != 0) {
- ssin->sin_port = mxreq->rule.r_value.relayhost.port;
- mxrep.mxhost = mxhost;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- continue;
- }
-
- switch (mxhost.flags & F_SSL) {
- case F_SMTPS:
- ssin->sin_port = htons(465);
- mxrep.mxhost = mxhost;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- break;
- case F_SSL:
- ssin->sin_port = htons(465);
- mxrep.mxhost = mxhost;
- mxrep.mxhost.flags &= ~F_STARTTLS;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- case F_STARTTLS:
- ssin->sin_port = htons(25);
- mxrep.mxhost = mxhost;
- mxrep.mxhost.flags &= ~F_SMTPS;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- break;
- default:
- ssin->sin_port = htons(25);
- mxrep.mxhost = mxhost;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- }
- }
-
- if (resp->ai_family == PF_INET6) {
- struct sockaddr_in6 *ssin6;
-
- mxhost.ss = *(struct sockaddr_storage *)resp->ai_addr;
- ssin6 = (struct sockaddr_in6 *)&mxhost.ss;
- if (mxreq->rule.r_value.relayhost.port != 0) {
- ssin6->sin6_port = mxreq->rule.r_value.relayhost.port;
- mxrep.mxhost = mxhost;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- continue;
- }
-
- switch (mxhost.flags & F_SSL) {
- case F_SMTPS:
- ssin6->sin6_port = htons(465);
- mxrep.mxhost = mxhost;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- break;
- case F_SSL:
- ssin6->sin6_port = htons(465);
- mxrep.mxhost = mxhost;
- mxrep.mxhost.flags &= ~F_STARTTLS;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- case F_STARTTLS:
- ssin6->sin6_port = htons(25);
- mxrep.mxhost = mxhost;
- mxrep.mxhost.flags &= ~F_SMTPS;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- break;
- default:
- ssin6->sin6_port = htons(25);
- mxrep.mxhost = mxhost;
- imsg_compose(ibuf, IMSG_LKA_MX, 0, 0, -1, &mxrep,
- sizeof(struct mxrep));
- }
- }
- }
- freeaddrinfo(res);
- free(secret);
- bzero(&mxhost.credentials, MAX_LINE_SIZE);
- }
+ case IMSG_LKA_SECRET: {
+ struct secret *query = imsg.data;
+ char *secret = NULL;
- mxrep.getaddrinfo_error = error;
+ secret = map_dblookup(env, "secrets", query->host);
- imsg_compose(ibuf, IMSG_LKA_MX_END, 0, 0, -1,
- &mxrep, sizeof(struct mxrep));
+ log_debug("secret for %s %s", query->host,
+ secret ? "found" : "not found");
- if (mx != lmx)
- free(mx);
+ if (secret)
+ lka_encode_credentials(query->secret, secret);
+ else
+ query->secret[0] = '\0';
+ imsg_compose(ibuf, IMSG_LKA_SECRET, 0, 0, -1, query,
+ sizeof(*query));
+ free(secret);
break;
}
+ case IMSG_DNS_A:
+ case IMSG_DNS_MX:
+ dns_async(env, ibuf, imsg.hdr.type, imsg.data);
+ break;
+
default:
log_warnx("lka_dispatch_mta: got imsg %d",
imsg.hdr.type);
@@ -571,10 +438,8 @@ lka_dispatch_smtp(int sig, short event, void *p)
break;
switch (imsg.hdr.type) {
- case IMSG_LKA_HOST:
- lka_dns_reverse(imsg.data);
- imsg_compose(ibuf, IMSG_LKA_HOST, 0, 0, -1, imsg.data,
- sizeof(struct session));
+ case IMSG_DNS_PTR:
+ dns_async(env, ibuf, IMSG_DNS_PTR, imsg.data);
break;
default:
log_warnx("lka_dispatch_smtp: got imsg %d",
@@ -703,6 +568,7 @@ lka(struct smtpd *env)
struct event ev_sigint;
struct event ev_sigterm;
+ struct event ev_sigchld;
struct peer peers[] = {
{ PROC_PARENT, lka_dispatch_parent },
@@ -741,8 +607,10 @@ lka(struct smtpd *env)
signal_set(&ev_sigint, SIGINT, lka_sig_handler, env);
signal_set(&ev_sigterm, SIGTERM, lka_sig_handler, env);
+ signal_set(&ev_sigchld, SIGCHLD, lka_sig_handler, env);
signal_add(&ev_sigint, NULL);
signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
@@ -1238,30 +1106,4 @@ lka_encode_credentials(char *dest, char *src)
return 1;
}
-void
-lka_dns_reverse(struct session *s)
-{
- char addr[NI_MAXHOST];
- struct addrinfo hints, *res;
- struct sockaddr *sa;
-
- strlcpy(s->s_hostname, "<unknown>", sizeof(s->s_hostname));
-
- sa = (struct sockaddr *)&s->s_ss;
- if (getnameinfo(sa, sa->sa_len, addr, sizeof(addr),
- NULL, 0, NI_NAMEREQD))
- return;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_flags = AI_NUMERICHOST;
-
- if (getaddrinfo(addr, NULL, &hints, &res))
- return; /* malicious PTR record. */
-
- freeaddrinfo(res);
-
- strlcpy(s->s_hostname, addr, sizeof(s->s_hostname));
-}
-
SPLAY_GENERATE(lkatree, lkasession, nodes, lkasession_cmp);
diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c
index 5ed262d8911..580ecde45aa 100644
--- a/usr.sbin/smtpd/mta.c
+++ b/usr.sbin/smtpd/mta.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta.c,v 1.44 2009/04/28 21:56:36 gilles Exp $ */
+/* $OpenBSD: mta.c,v 1.45 2009/05/09 17:04:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -152,14 +152,13 @@ mta_dispatch_lka(int sig, short event, void *p)
break;
switch (imsg.hdr.type) {
- case IMSG_LKA_MX: {
+ case IMSG_DNS_A: {
struct session key;
- struct mxrep *mxrep;
+ struct dns *reply = imsg.data;
struct session *s;
struct mxhost *mxhost;
- mxrep = imsg.data;
- key.s_id = mxrep->id;
+ key.s_id = reply->id;
s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key);
if (s == NULL)
@@ -169,19 +168,20 @@ mta_dispatch_lka(int sig, short event, void *p)
if (mxhost == NULL)
fatal("mta_dispatch_lka: calloc");
- *mxhost = mxrep->mxhost;
+ mxhost->ss = reply->ss;
+
TAILQ_INSERT_TAIL(&s->mxhosts, mxhost, entry);
break;
}
- case IMSG_LKA_MX_END: {
+
+ case IMSG_DNS_A_END: {
struct session key;
- struct mxrep *mxrep;
+ struct dns *reply = imsg.data;
struct session *s;
int ret;
- mxrep = imsg.data;
- key.s_id = mxrep->id;
+ key.s_id = reply->id;
s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key);
if (s == NULL)
@@ -192,7 +192,7 @@ mta_dispatch_lka(int sig, short event, void *p)
do {
ret = mta_connect(s);
} while (ret == 0);
-
+
if (ret < 0) {
mta_batch_update_queue(s->batch);
session_destroy(s);
@@ -200,6 +200,25 @@ mta_dispatch_lka(int sig, short event, void *p)
break;
}
+
+ case IMSG_LKA_SECRET: {
+ struct secret *reply = imsg.data;
+ struct session key, *s;
+
+ key.s_id = reply->id;
+
+ s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key);
+ if (s == NULL)
+ fatal("smtp_dispatch_parent: session is gone");
+
+ strlcpy(s->credentials, reply->secret,
+ sizeof(s->credentials));
+
+ mta_mxlookup(env, s, s->batch->hostname,
+ &s->batch->rule);
+ break;
+ }
+
default:
log_warnx("mta_dispatch_parent: got imsg %d",
imsg.hdr.type);
@@ -386,8 +405,21 @@ mta_dispatch_runner(int sig, short event, void *p)
s = batchp->sessionp;
- mta_mxlookup(env, s, batchp->hostname, &batchp->rule);
-
+ if (batchp->rule.r_value.relayhost.flags & F_AUTH) {
+ struct secret query;
+
+ bzero(&query, sizeof(query));
+ query.id = s->s_id;
+ strlcpy(query.host,
+ batchp->rule.r_value.relayhost.hostname,
+ sizeof(query.host));
+
+ imsg_compose(env->sc_ibufs[PROC_LKA],
+ IMSG_LKA_SECRET, 0, 0, -1, &query,
+ sizeof(query));
+ } else
+ mta_mxlookup(env, s, batchp->hostname,
+ &batchp->rule);
break;
}
default:
@@ -488,13 +520,28 @@ mta(struct smtpd *env)
void
mta_mxlookup(struct smtpd *env, struct session *sessionp, char *hostname, struct rule *rule)
{
- struct mxreq mxreq;
+ int port;
- mxreq.id = sessionp->s_id;
- mxreq.rule = *rule;
- (void)strlcpy(mxreq.hostname, hostname, MAXHOSTNAMELEN);
- imsg_compose(env->sc_ibufs[PROC_LKA], IMSG_LKA_MX, 0, 0, -1,
- &mxreq, sizeof(struct mxreq));
+ switch (rule->r_value.relayhost.flags & F_SSL) {
+ case F_SMTPS:
+ port = 465;
+ break;
+ case F_SSL:
+ port = 465;
+ rule->r_value.relayhost.flags &= ~F_STARTTLS;
+ break;
+ default:
+ port = 25;
+ }
+
+ if (rule->r_value.relayhost.port)
+ port = ntohs(rule->r_value.relayhost.port);
+
+ if (rule->r_action == A_RELAYVIA)
+ dns_query_a(env, rule->r_value.relayhost.hostname, port,
+ sessionp->s_id);
+ else
+ dns_query_mx(env, hostname, port, sessionp->s_id);
}
/* shamelessly ripped usr.sbin/relayd/check_tcp.c ;) */
@@ -504,8 +551,6 @@ mta_connect(struct session *sessionp)
int s;
int type;
struct linger lng;
- struct sockaddr_in ssin;
- struct sockaddr_in6 ssin6;
struct mxhost *mxhost;
mxhost = TAILQ_FIRST(&sessionp->mxhosts);
@@ -525,19 +570,9 @@ mta_connect(struct session *sessionp)
session_socket_blockmode(s, BM_NONBLOCK);
- if (mxhost->ss.ss_family == PF_INET) {
- ssin = *(struct sockaddr_in *)&mxhost->ss;
- if (connect(s, (struct sockaddr *)&ssin, sizeof(struct sockaddr_in)) == -1)
- if (errno != EINPROGRESS)
- goto bad;
- }
-
- if (mxhost->ss.ss_family == PF_INET6) {
- ssin6 = *(struct sockaddr_in6 *)&mxhost->ss;
- if (connect(s, (struct sockaddr *)&ssin6, sizeof(struct sockaddr_in6)) == -1)
- if (errno != EINPROGRESS)
- goto bad;
- }
+ if (connect(s, (struct sockaddr *)&mxhost->ss, mxhost->ss.ss_len) == -1)
+ if (errno != EINPROGRESS)
+ goto bad;
sessionp->s_tv.tv_sec = SMTPD_CONNECT_TIMEOUT;
sessionp->s_tv.tv_usec = 0;
@@ -602,7 +637,8 @@ mta_write(int s, short event, void *arg)
return;
}
- if (mxhost && mxhost->flags & F_SMTPS) {
+ if (sessionp->batch->rule.r_value.relayhost.flags & F_SMTPS) {
+ log_debug("mta_write: initializing ssl");
ssl_client_init(sessionp);
return;
}
@@ -631,7 +667,6 @@ mta_reply_handler(struct bufferevent *bev, void *arg)
char codebuf[4];
const char *errstr;
int flags = 0;
- struct mxhost *mxhost = TAILQ_FIRST(&sessionp->mxhosts);
line = evbuffer_readline(bev->input);
if (line == NULL)
@@ -679,18 +714,18 @@ mta_reply_handler(struct bufferevent *bev, void *arg)
if (sessionp->s_state == S_GREETED &&
(sessionp->s_flags & F_PEERHASAUTH) &&
(sessionp->s_flags & F_SECURE) &&
- (mxhost->flags & F_AUTH) &&
- (mxhost->credentials[0] != '\0')) {
- log_debug("AUTH PLAIN %s", mxhost->credentials);
+ (sessionp->batch->rule.r_value.relayhost.flags & F_AUTH) &&
+ (sessionp->credentials[0] != '\0')) {
+ log_debug("AUTH PLAIN %s", sessionp->credentials);
session_respond(sessionp, "AUTH PLAIN %s",
- mxhost->credentials);
+ sessionp->credentials);
sessionp->s_state = S_AUTH_INIT;
return 0;
}
if (sessionp->s_state == S_GREETED &&
!(sessionp->s_flags & F_PEERHASTLS) &&
- mxhost->flags & F_STARTTLS) {
+ (sessionp->batch->rule.r_value.relayhost.flags&F_STARTTLS)){
/* PERM - we want TLS but it is not advertised */
batchp->status = S_BATCH_PERMFAILURE;
mta_batch_update_queue(batchp);
@@ -700,7 +735,7 @@ mta_reply_handler(struct bufferevent *bev, void *arg)
if (sessionp->s_state == S_GREETED &&
!(sessionp->s_flags & F_PEERHASAUTH) &&
- mxhost->flags & F_AUTH) {
+ (sessionp->batch->rule.r_value.relayhost.flags & F_AUTH)) {
/* PERM - we want AUTH but it is not advertised */
batchp->status = S_BATCH_PERMFAILURE;
mta_batch_update_queue(batchp);
diff --git a/usr.sbin/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c
index 5b8e2fa52d2..dfebcc69e76 100644
--- a/usr.sbin/smtpd/smtp.c
+++ b/usr.sbin/smtpd/smtp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp.c,v 1.41 2009/04/28 22:38:22 jacekm Exp $ */
+/* $OpenBSD: smtp.c,v 1.42 2009/05/09 17:04:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -283,21 +283,22 @@ smtp_dispatch_lka(int sig, short event, void *p)
break;
switch (imsg.hdr.type) {
- case IMSG_LKA_HOST: {
- struct session key;
+ case IMSG_DNS_PTR: {
+ struct dns *reply = imsg.data;
struct session *s;
- struct session *ss;
+ struct session key;
- ss = imsg.data;
- key.s_id = ss->s_id;
+ key.s_id = reply->id;
s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key);
if (s == NULL)
fatal("smtp_dispatch_lka: session is gone");
- strlcpy(s->s_hostname, ss->s_hostname,
+ strlcpy(s->s_hostname,
+ reply->error ? "<unknown>" : reply->host,
sizeof(s->s_hostname));
- strlcpy(s->s_msg.session_hostname, ss->s_hostname,
+
+ strlcpy(s->s_msg.session_hostname, s->s_hostname,
sizeof(s->s_msg.session_hostname));
session_init(s->s_l, s);
@@ -716,8 +717,7 @@ smtp_accept(int fd, short event, void *p)
if (s_smtp.sessions_active == s->s_env->sc_maxconn)
event_del(&l->ev);
- imsg_compose(s->s_env->sc_ibufs[PROC_LKA], IMSG_LKA_HOST, 0, 0, -1, s,
- sizeof(struct session));
+ dns_query_ptr(l->env, &s->s_ss, s->s_id);
SPLAY_INSERT(sessiontree, &s->s_env->sc_sessions, s);
}
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index cd49ff05d72..be43006cf89 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.105 2009/04/28 23:11:25 gilles Exp $ */
+/* $OpenBSD: smtpd.h,v 1.106 2009/05/09 17:04:55 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -66,7 +66,6 @@
#define PATH_OFFLINE "/offline"
/* number of MX records to lookup */
-#define MXARRAYSIZE 5
#define MAX_MX_COUNT 10
/* rfc5321 limits */
@@ -93,9 +92,7 @@ struct relayhost {
struct mxhost {
TAILQ_ENTRY(mxhost) entry;
- u_int8_t flags;
struct sockaddr_storage ss;
- char credentials[MAX_LINE_SIZE];
};
/* buffer specific headers */
@@ -171,9 +168,7 @@ enum imsg_type {
IMSG_CONF_RELOAD,
IMSG_LKA_MAIL,
IMSG_LKA_RCPT,
- IMSG_LKA_MX,
- IMSG_LKA_MX_END,
- IMSG_LKA_HOST,
+ IMSG_LKA_SECRET,
IMSG_MDA_MAILBOX_FILE,
IMSG_MDA_MESSAGE_FILE,
IMSG_MFA_RCPT,
@@ -221,7 +216,12 @@ enum imsg_type {
IMSG_STATS,
- IMSG_SMTP_ENQUEUE
+ IMSG_SMTP_ENQUEUE,
+
+ IMSG_DNS_A,
+ IMSG_DNS_A_END,
+ IMSG_DNS_MX,
+ IMSG_DNS_PTR
};
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
@@ -614,6 +614,8 @@ struct session {
struct session_auth_req s_auth;
+ char credentials[MAX_LINE_SIZE];
+
struct batch *batch;
TAILQ_HEAD(mxhostlist, mxhost) mxhosts;
@@ -717,16 +719,20 @@ struct forward_req {
char pw_name[MAXLOGNAME];
};
-struct mxreq {
- u_int64_t id;
- char hostname[MAXHOSTNAMELEN];
- struct rule rule;
+struct dns {
+ u_int64_t id;
+ char host[MAXHOSTNAMELEN];
+ int port;
+ int error;
+ struct sockaddr_storage ss;
+ struct smtpd *env;
+ struct dns *next;
};
-struct mxrep {
- u_int64_t id;
- int getaddrinfo_error;
- struct mxhost mxhost;
+struct secret {
+ u_int64_t id;
+ char host[MAXHOSTNAMELEN];
+ char secret[MAX_LINE_SIZE];
};
enum lkasession_flags {
@@ -781,7 +787,12 @@ int msgbuf_write(struct msgbuf *);
/* dns.c */
-int getmxbyname(char *, char ***);
+void dns_query_a(struct smtpd *, char *, int, u_int64_t);
+void dns_query_mx(struct smtpd *, char *, int, u_int64_t);
+void dns_query_ptr(struct smtpd *, struct sockaddr_storage *,
+ u_int64_t);
+void dns_async(struct smtpd *, struct imsgbuf *, int,
+ struct dns *);
/* forward.c */