diff options
Diffstat (limited to 'usr.sbin/smtpd/mta.c')
-rw-r--r-- | usr.sbin/smtpd/mta.c | 324 |
1 files changed, 188 insertions, 136 deletions
diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c index 51cf24721da..164e293c2b7 100644 --- a/usr.sbin/smtpd/mta.c +++ b/usr.sbin/smtpd/mta.c @@ -1,9 +1,9 @@ -/* $OpenBSD: mta.c,v 1.89 2010/06/01 11:05:12 jacekm Exp $ */ +/* $OpenBSD: mta.c,v 1.90 2010/06/01 19:47:09 jacekm Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> - * Copyright (c) 2009-2010 Jacek Masiulaniec <jacekm@dobremiasto.net> + * 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 @@ -40,83 +40,114 @@ #include "smtpd.h" #include "client.h" -#include "queue_backend.h" void mta_imsg(struct smtpd *, struct imsgev *, struct imsg *); __dead void mta_shutdown(void); void mta_sig_handler(int, short, void *); -struct mta_session *mta_lookup(struct smtpd *, u_int32_t); +struct mta_session *mta_lookup(struct smtpd *, u_int64_t); void mta_enter_state(struct mta_session *, int, void *); void mta_pickup(struct mta_session *, void *); void mta_event(int, short, void *); void mta_status(struct mta_session *, const char *, ...); -void mta_rcpt_status(struct recipient *, char *); -void mta_rcpt_log(struct mta_session *, struct recipient *); -void mta_rcpt_done(struct mta_session *, struct recipient *); +void mta_message_status(struct message *, char *); +void mta_message_log(struct mta_session *, struct message *); +void mta_message_done(struct mta_session *, struct message *); void mta_connect_done(int, short, void *); +void mta_request_datafd(struct mta_session *); void mta_imsg(struct smtpd *env, struct imsgev *iev, struct imsg *imsg) { - struct aux aux; struct mta_session *s; - struct recipient *rcpt; struct mta_relay *relay; - struct action *action; + struct message *m; + struct secret *secret; + struct batch *b; struct dns *dns; struct ssl *ssl; - size_t rcpt_sz; if (iev->proc == PROC_QUEUE) { switch (imsg->hdr.type) { case IMSG_BATCH_CREATE: - if (imsg->fd < 0) - fatalx("mta: fd pass fail"); + b = imsg->data; s = calloc(1, sizeof *s); if (s == NULL) fatal(NULL); - TAILQ_INIT(&s->recipients); - TAILQ_INIT(&s->relays); - s->id = imsg->hdr.peerid; + s->id = b->id; s->state = MTA_INIT; s->env = env; - s->datafd = imsg->fd; - memcpy(&s->content_id, imsg->data, - sizeof s->content_id); + s->datafd = -1; + + /* establish host name */ + if (b->rule.r_action == A_RELAYVIA) + s->host = strdup(b->rule.r_value.relayhost.hostname); + else + s->host = strdup(b->hostname); + if (s->host == NULL) + fatal(NULL); + + /* establish port */ + s->port = ntohs(b->rule.r_value.relayhost.port); /* XXX */ + + /* have cert? */ + s->cert = strdup(b->rule.r_value.relayhost.cert); + if (s->cert == NULL) + fatal(NULL); + else if (s->cert[0] == '\0') { + free(s->cert); + s->cert = NULL; + } + + /* use auth? */ + if ((b->rule.r_value.relayhost.flags & F_SSL) && + (b->rule.r_value.relayhost.flags & F_AUTH)) + s->flags |= MTA_USE_AUTH; + + /* force a particular SSL mode? */ + switch (b->rule.r_value.relayhost.flags & F_SSL) { + case F_SSL: + s->flags |= MTA_FORCE_ANYSSL; + break; + + case F_SMTPS: + s->flags |= MTA_FORCE_SMTPS; + + case F_STARTTLS: + /* client_* API by default requires STARTTLS */ + break; + + default: + s->flags |= MTA_ALLOW_PLAIN; + } + + TAILQ_INIT(&s->recipients); + TAILQ_INIT(&s->relays); SPLAY_INSERT(mtatree, &env->mta_sessions, s); return; case IMSG_BATCH_APPEND: - action = imsg->data; - s = mta_lookup(env, imsg->hdr.peerid); - if (s->auxraw == NULL) { - /* - * XXX: queue can batch together actions with - * different relay params. - */ - s->auxraw = strdup(action->arg); - if (s->auxraw == NULL) - fatal(NULL); - auxsplit(&s->aux, s->auxraw); - } - auxsplit(&aux, action->arg); - rcpt_sz = sizeof *rcpt + strlen(aux.rcpt) + 1; - rcpt = malloc(rcpt_sz); - if (rcpt == NULL) + m = imsg->data; + s = mta_lookup(env, m->batch_id); + m = malloc(sizeof *m); + if (m == NULL) fatal(NULL); - rcpt->action_id = action->id; - strlcpy(rcpt->address, aux.rcpt, rcpt_sz - sizeof *rcpt); - strlcpy(rcpt->status, "000 init", sizeof rcpt->status); - TAILQ_INSERT_TAIL(&s->recipients, rcpt, entry); + *m = *(struct message *)imsg->data; + strlcpy(m->session_errorline, "000 init", + sizeof(m->session_errorline)); + TAILQ_INSERT_TAIL(&s->recipients, m, entry); return; case IMSG_BATCH_CLOSE: - s = mta_lookup(env, imsg->hdr.peerid); - memcpy(&s->birth, imsg->data, sizeof s->birth); - mta_pickup(s, NULL); + b = imsg->data; + mta_pickup(mta_lookup(env, b->id), NULL); + return; + + case IMSG_QUEUE_MESSAGE_FD: + b = imsg->data; + mta_pickup(mta_lookup(env, b->id), &imsg->fd); return; } } @@ -124,7 +155,8 @@ mta_imsg(struct smtpd *env, struct imsgev *iev, struct imsg *imsg) if (iev->proc == PROC_LKA) { switch (imsg->hdr.type) { case IMSG_LKA_SECRET: - mta_pickup(mta_lookup(env, imsg->hdr.peerid), imsg->data); + secret = imsg->data; + mta_pickup(mta_lookup(env, secret->id), secret->secret); return; case IMSG_DNS_A: @@ -290,25 +322,24 @@ mta_session_cmp(struct mta_session *a, struct mta_session *b) } struct mta_session * -mta_lookup(struct smtpd *env, u_int32_t id) +mta_lookup(struct smtpd *env, u_int64_t id) { struct mta_session key, *res; key.id = id; - res = SPLAY_FIND(mtatree, &env->mta_sessions, &key); - if (res == NULL) + if ((res = SPLAY_FIND(mtatree, &env->mta_sessions, &key)) == NULL) fatalx("mta_lookup: session not found"); - return res; + return (res); } void mta_enter_state(struct mta_session *s, int newstate, void *p) { + struct secret secret; struct mta_relay *relay; struct sockaddr *sa; - struct recipient *rcpt; + struct message *m; struct smtp_client *pcb; - char *host; int max_reuse; s->state = newstate; @@ -318,34 +349,33 @@ mta_enter_state(struct mta_session *s, int newstate, void *p) /* * Lookup AUTH secret. */ - if (s->aux.relay_via[0]) - host = s->aux.relay_via; - else { - rcpt = TAILQ_FIRST(&s->recipients); - host = strchr(rcpt->address, '@') + 1; - } + bzero(&secret, sizeof(secret)); + secret.id = s->id; + strlcpy(secret.host, s->host, sizeof(secret.host)); imsg_compose_event(s->env->sc_ievs[PROC_LKA], IMSG_LKA_SECRET, - s->id, 0, -1, host, strlen(host) + 1); + 0, 0, -1, &secret, sizeof(secret)); break; case MTA_MX: /* * Lookup MX record. */ - if (s->aux.relay_via[0]) - host = s->aux.relay_via; - else { - rcpt = TAILQ_FIRST(&s->recipients); - host = strchr(rcpt->address, '@') + 1; - } - dns_query_mx(s->env, host, 0, s->id); + dns_query_mx(s->env, s->host, 0, s->id); + break; + + case MTA_DATA: + /* + * Obtain message body fd. + */ + log_debug("mta: getting datafd"); + mta_request_datafd(s); break; case MTA_CONNECT: /* * Connect to the MX. */ - if (strcmp(s->aux.ssl, "ssl") == 0) + if (s->flags & MTA_FORCE_ANYSSL) max_reuse = 2; else max_reuse = 1; @@ -362,25 +392,24 @@ mta_enter_state(struct mta_session *s, int newstate, void *p) log_debug("mta: connect %s", ss_to_text(&relay->sa)); sa = (struct sockaddr *)&relay->sa; - if (s->aux.port[0]) - sa_set_port(sa, s->aux.port); - else if (strcmp(s->aux.ssl, "ssl") == 0 && - relay->used == 1) - sa_set_port(sa, "465"); - else if (strcmp(s->aux.ssl, "smtps") == 0) - sa_set_port(sa, "465"); + if (s->port) + sa_set_port(sa, s->port); + else if ((s->flags & MTA_FORCE_ANYSSL) && relay->used == 1) + sa_set_port(sa, 465); + else if (s->flags & MTA_FORCE_SMTPS) + sa_set_port(sa, 465); else - sa_set_port(sa, "25"); + sa_set_port(sa, 25); s->fd = socket(sa->sa_family, SOCK_STREAM, 0); if (s->fd == -1) - fatal("socket"); + fatal("mta cannot create socket"); session_socket_blockmode(s->fd, BM_NONBLOCK); session_socket_no_linger(s->fd); if (connect(s->fd, sa, sa->sa_len) == -1) { if (errno != EINPROGRESS) { - mta_status(s, "110 connect: %s", + mta_status(s, "110 connect error: %s", strerror(errno)); close(s->fd); continue; @@ -412,10 +441,10 @@ mta_enter_state(struct mta_session *s, int newstate, void *p) pcb = client_init(s->fd, s->datafd, s->env->sc_hostname, 1); /* lookup SSL certificate */ - if (s->aux.cert[0]) { - struct ssl key, *res; + if (s->cert) { + struct ssl key, *res; - strlcpy(key.ssl_name, s->aux.cert, sizeof key.ssl_name); + strlcpy(key.ssl_name, s->cert, sizeof(key.ssl_name)); res = SPLAY_FIND(ssltree, s->env->sc_ssl, &key); if (res == NULL) { client_close(pcb); @@ -430,11 +459,11 @@ mta_enter_state(struct mta_session *s, int newstate, void *p) /* choose SMTPS vs. STARTTLS */ relay = TAILQ_FIRST(&s->relays); - if (strcmp(s->aux.ssl, "ssl") == 0 && relay->used == 1) + if ((s->flags & MTA_FORCE_ANYSSL) && relay->used == 1) client_ssl_smtps(pcb); - else if (strcmp(s->aux.ssl, "smtps") == 0) + else if (s->flags & MTA_FORCE_SMTPS) client_ssl_smtps(pcb); - else if (s->aux.ssl[0] == '\0') + else if (s->flags & MTA_ALLOW_PLAIN) client_ssl_optional(pcb); /* enable AUTH */ @@ -442,14 +471,17 @@ mta_enter_state(struct mta_session *s, int newstate, void *p) client_auth(pcb, s->secret); /* set envelope sender */ - if (s->aux.mail_from[0]) - client_sender(pcb, "%s", s->aux.mail_from); + m = TAILQ_FIRST(&s->recipients); + if (m->sender.user[0] && m->sender.domain[0]) + client_sender(pcb, "%s@%s", m->sender.user, + m->sender.domain); else client_sender(pcb, ""); /* set envelope recipients */ - TAILQ_FOREACH(rcpt, &s->recipients, entry) - client_rcpt(pcb, rcpt, "%s", rcpt->address); + TAILQ_FOREACH(m, &s->recipients, entry) + client_rcpt(pcb, m, "%s@%s", m->recipient.user, + m->recipient.domain); s->pcb = pcb; event_set(&s->ev, s->fd, EV_READ|EV_WRITE, mta_event, s); @@ -462,11 +494,11 @@ mta_enter_state(struct mta_session *s, int newstate, void *p) */ /* update queue status */ - while ((rcpt = TAILQ_FIRST(&s->recipients))) - mta_rcpt_done(s, rcpt); + while ((m = TAILQ_FIRST(&s->recipients))) + mta_message_done(s, m); imsg_compose_event(s->env->sc_ievs[PROC_QUEUE], - IMSG_BATCH_DONE, s->id, 0, -1, NULL, 0); + IMSG_BATCH_DONE, 0, 0, -1, NULL, 0); /* deallocate resources */ SPLAY_REMOVE(mtatree, &s->env->mta_sessions, s); @@ -475,8 +507,9 @@ mta_enter_state(struct mta_session *s, int newstate, void *p) free(relay); } close(s->datafd); - free(s->auxraw); free(s->secret); + free(s->host); + free(s->cert); free(s); break; @@ -492,7 +525,7 @@ mta_pickup(struct mta_session *s, void *p) switch (s->state) { case MTA_INIT: - if (s->aux.auth[0]) + if (s->flags & MTA_USE_AUTH) mta_enter_state(s, MTA_SECRET, NULL); else mta_enter_state(s, MTA_MX, NULL); @@ -523,6 +556,15 @@ mta_pickup(struct mta_session *s, void *p) mta_status(s, "600 Unable to resolve DNS for domain"); mta_enter_state(s, MTA_DONE, NULL); } else + mta_enter_state(s, MTA_DATA, NULL); + break; + + case MTA_DATA: + /* QUEUE replied to body fd request. */ + s->datafd = *(int *)p; + if (s->datafd == -1) + fatalx("mta cannot obtain msgfd"); + else mta_enter_state(s, MTA_CONNECT, NULL); break; @@ -530,7 +572,7 @@ mta_pickup(struct mta_session *s, void *p) /* Remote accepted/rejected connection. */ error = session_socket_error(s->fd); if (error) { - mta_status(s, "110 connect: %s", strerror(error)); + mta_status(s, "110 connect error: %s", strerror(error)); close(s->fd); mta_enter_state(s, MTA_CONNECT, NULL); } else @@ -551,7 +593,6 @@ mta_event(int fd, short event, void *p) { struct mta_session *s = p; struct smtp_client *pcb = s->pcb; - struct recipient *rcpt; if (event & EV_TIMEOUT) { mta_status(s, "150 timeout"); @@ -564,10 +605,9 @@ mta_event(int fd, short event, void *p) case CLIENT_STOP_WRITE: goto ro; case CLIENT_RCPT_FAIL: - rcpt = pcb->rcptfail; - mta_rcpt_status(pcb->rcptfail, pcb->reply); - mta_rcpt_log(s, pcb->rcptfail); - mta_rcpt_done(s, pcb->rcptfail); + mta_message_status(pcb->rcptfail, pcb->reply); + mta_message_log(s, pcb->rcptfail); + mta_message_done(s, pcb->rcptfail); goto rw; case CLIENT_DONE: mta_status(s, "%s", pcb->status); @@ -580,13 +620,10 @@ out: client_close(pcb); s->pcb = NULL; - if (TAILQ_EMPTY(&s->recipients)) { - log_debug("%s: leaving", __func__); + if (TAILQ_EMPTY(&s->recipients)) mta_enter_state(s, MTA_DONE, NULL); - } else { - log_debug("%s: connecting to next", __func__); + else mta_enter_state(s, MTA_CONNECT, NULL); - } return; rw: @@ -603,7 +640,7 @@ void mta_status(struct mta_session *s, const char *fmt, ...) { char *status; - struct recipient *rcpt, *next; + struct message *m, *next; va_list ap; va_start(ap, fmt); @@ -611,16 +648,16 @@ mta_status(struct mta_session *s, const char *fmt, ...) fatal("vasprintf"); va_end(ap); - for (rcpt = TAILQ_FIRST(&s->recipients); rcpt; rcpt = next) { - next = TAILQ_NEXT(rcpt, entry); + for (m = TAILQ_FIRST(&s->recipients); m; m = next) { + next = TAILQ_NEXT(m, entry); /* save new status */ - mta_rcpt_status(rcpt, status); + mta_message_status(m, status); /* remove queue entry */ if (*status == '2' || *status == '5' || *status == '6') { - mta_rcpt_log(s, rcpt); - mta_rcpt_done(s, rcpt); + mta_message_log(s, m); + mta_message_done(s, m); } } @@ -628,56 +665,58 @@ mta_status(struct mta_session *s, const char *fmt, ...) } void -mta_rcpt_status(struct recipient *rcpt, char *status) +mta_message_status(struct message *m, char *status) { /* * Previous delivery attempts might have assigned an errorline of * higher status (eg. 5yz is of higher status than 4yz), so check * this before deciding to overwrite existing status with a new one. */ - if (*status != '2' && strncmp(rcpt->status, status, 3) > 0) + if (*status != '2' && strncmp(m->session_errorline, status, 3) > 0) return; /* change status */ - log_debug("mta: new status for %s: %s", rcpt->address, status); - strlcpy(rcpt->status, status, sizeof rcpt->status); + log_debug("mta: new status for %s@%s: %s", m->recipient.user, + m->recipient.domain, status); + strlcpy(m->session_errorline, status, sizeof(m->session_errorline)); } void -mta_rcpt_log(struct mta_session *s, struct recipient *rcpt) +mta_message_log(struct mta_session *s, struct message *m) { - struct mta_relay *relay; - - relay = TAILQ_FIRST(&s->relays); + struct mta_relay *relay = TAILQ_FIRST(&s->relays); + char *status = m->session_errorline; - log_info("%s: to=%s, delay=%d, relay=%s [%s], stat=%s (%s)", - queue_be_decode(s->content_id), rcpt->address, - time(NULL) - s->birth, + log_info("%s: to=<%s@%s>, delay=%d, relay=%s [%s], stat=%s (%s)", + m->message_id, m->recipient.user, + m->recipient.domain, time(NULL) - m->creation, relay ? relay->fqdn : "(none)", relay ? ss_to_text(&relay->sa) : "", - rcpt->status[0] == '2' ? "Sent" : - rcpt->status[0] == '5' ? "RemoteError" : - rcpt->status[0] == '4' ? "RemoteError" : "LocalError", - rcpt->status + 4); + *status == '2' ? "Sent" : + *status == '5' ? "RemoteError" : + *status == '4' ? "RemoteError" : "LocalError", + status + 4); } void -mta_rcpt_done(struct mta_session *s, struct recipient *rcpt) +mta_message_done(struct mta_session *s, struct message *m) { - struct action *action; - int action_sz; - - action_sz = sizeof *action + strlen(rcpt->status) + 1; - action = malloc(action_sz); - if (action == NULL) - fatal(NULL); - action->id = rcpt->action_id; - strlcpy(action->arg, rcpt->status, action_sz - sizeof *action); - imsg_compose_event(s->env->sc_ievs[PROC_QUEUE], IMSG_BATCH_UPDATE, - s->id, 0, -1, action, action_sz); - TAILQ_REMOVE(&s->recipients, rcpt, entry); - free(action); - free(rcpt); + switch (m->session_errorline[0]) { + case '6': + case '5': + m->status = S_MESSAGE_PERMFAILURE; + break; + case '2': + m->status = S_MESSAGE_ACCEPTED; + break; + default: + m->status = S_MESSAGE_TEMPFAILURE; + break; + } + imsg_compose_event(s->env->sc_ievs[PROC_QUEUE], + IMSG_QUEUE_MESSAGE_UPDATE, 0, 0, -1, m, sizeof(*m)); + TAILQ_REMOVE(&s->recipients, m, entry); + free(m); } void @@ -686,4 +725,17 @@ mta_connect_done(int fd, short event, void *p) mta_pickup(p, NULL); } +void +mta_request_datafd(struct mta_session *s) +{ + struct batch b; + struct message *m; + + b.id = s->id; + m = TAILQ_FIRST(&s->recipients); + strlcpy(b.message_id, m->message_id, sizeof(b.message_id)); + imsg_compose_event(s->env->sc_ievs[PROC_QUEUE], IMSG_QUEUE_MESSAGE_FD, + 0, 0, -1, &b, sizeof(b)); +} + SPLAY_GENERATE(mtatree, mta_session, entry, mta_session_cmp); |