/* * Handle a response to an SMTP command */ static void mta_response(struct mta_session *s, char *line) { void *ssl; struct envelope *evp; switch (s->state) { case MTA_SMTP_BANNER: mta_enter_state(s, MTA_SMTP_EHLO); break; case MTA_SMTP_EHLO: if (line[0] != '2') { if ((s->flags & MTA_USE_AUTH) || !(s->flags & MTA_ALLOW_PLAIN)) { mta_route_error(s->route, line); mta_enter_state(s, MTA_DONE); return; } mta_enter_state(s, MTA_SMTP_HELO); return; } mta_enter_state(s, MTA_SMTP_STARTTLS); break; case MTA_SMTP_HELO: if (line[0] != '2') { mta_route_error(s->route, line); mta_enter_state(s, MTA_DONE); return; } mta_enter_state(s, MTA_SMTP_READY); break; case MTA_SMTP_STARTTLS: if (line[0] != '2') { if (s->flags & MTA_ALLOW_PLAIN) { mta_enter_state(s, MTA_SMTP_AUTH); return; } /* stop here if ssl can't be used */ mta_route_error(s->route, line); mta_enter_state(s, MTA_DONE); return; } ssl = ssl_mta_init(s->ssl); if (ssl == NULL) fatal("mta: ssl_mta_init"); s->is_reading = 0; io_set_write(&s->io); io_start_tls(&s->io, ssl); break; case MTA_SMTP_AUTH: if (line[0] != '2') { mta_route_error(s->route, line); mta_enter_state(s, MTA_DONE); return; } mta_enter_state(s, MTA_SMTP_READY); break; case MTA_SMTP_MAIL: if (line[0] != '2') { mta_status(s, 0, line); mta_enter_state(s, MTA_SMTP_RSET); return; } mta_enter_state(s, MTA_SMTP_RCPT); break; case MTA_SMTP_RCPT: evp = s->currevp; s->currevp = TAILQ_NEXT(s->currevp, entry); if (line[0] != '2') { mta_envelope_done(s->task, evp, line); if (TAILQ_EMPTY(&s->task->envelopes)) { free(s->task); s->task = NULL; stat_decrement("mta.task.running", 1); mta_enter_state(s, MTA_SMTP_RSET); break; } } if (s->currevp == NULL) mta_enter_state(s, MTA_SMTP_DATA); else mta_enter_state(s, MTA_SMTP_RCPT); break; case MTA_SMTP_DATA: if (line[0] != '2' && line[0] != '3') { mta_status(s, 0, line); mta_enter_state(s, MTA_SMTP_RSET); return; } mta_enter_state(s, MTA_SMTP_BODY); break; case MTA_SMTP_DONE: mta_status(s, 0, line); if (line[0] == '2') s->msgcount++; mta_enter_state(s, MTA_SMTP_READY); break; case MTA_SMTP_RSET: mta_enter_state(s, MTA_SMTP_READY); break; default: fatalx("mta_response() bad state"); } }
void mta_session_imsg(struct mproc *p, struct imsg *imsg) { struct ca_vrfy_resp_msg *resp_ca_vrfy; struct ca_cert_resp_msg *resp_ca_cert; struct mta_session *s; struct mta_host *h; struct msg m; uint64_t reqid; const char *name; void *ssl; int dnserror, status; switch (imsg->hdr.type) { case IMSG_QUEUE_MESSAGE_FD: m_msg(&m, imsg); m_get_id(&m, &reqid); m_end(&m); s = mta_tree_pop(&wait_fd, reqid); if (s == NULL) { if (imsg->fd != -1) close(imsg->fd); return; } if (imsg->fd == -1) { log_debug("debug: mta: failed to obtain msg fd"); mta_flush_task(s, IMSG_DELIVERY_TEMPFAIL, "Could not get message fd", 0, 0); mta_enter_state(s, MTA_READY); io_reload(&s->io); return; } s->datafp = fdopen(imsg->fd, "r"); if (s->datafp == NULL) fatal("mta: fdopen"); if (mta_check_loop(s->datafp)) { log_debug("debug: mta: loop detected"); fclose(s->datafp); s->datafp = NULL; mta_flush_task(s, IMSG_DELIVERY_LOOP, "Loop detected", 0, 0); mta_enter_state(s, MTA_READY); } else { mta_enter_state(s, MTA_MAIL); } io_reload(&s->io); return; case IMSG_DNS_PTR: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_int(&m, &dnserror); if (dnserror) name = NULL; else m_get_string(&m, &name); m_end(&m); s = mta_tree_pop(&wait_ptr, reqid); if (s == NULL) return; h = s->route->dst; h->lastptrquery = time(NULL); if (name) h->ptrname = xstrdup(name, "mta: ptr"); waitq_run(&h->ptrname, h->ptrname); return; case IMSG_LKA_SSL_INIT: resp_ca_cert = imsg->data; s = mta_tree_pop(&wait_ssl_init, resp_ca_cert->reqid); if (s == NULL) return; if (resp_ca_cert->status == CA_FAIL) { if (s->relay->pki_name) { log_info("smtp-out: Disconnecting session %016"PRIx64 ": CA failure", s->id); mta_free(s); return; } else { ssl = ssl_mta_init(NULL, 0, NULL, 0); if (ssl == NULL) fatal("mta: ssl_mta_init"); io_start_tls(&s->io, ssl); return; } } resp_ca_cert = xmemdup(imsg->data, sizeof *resp_ca_cert, "mta:ca_cert"); resp_ca_cert->cert = xstrdup((char *)imsg->data + sizeof *resp_ca_cert, "mta:ca_cert"); resp_ca_cert->key = xstrdup((char *)imsg->data + sizeof *resp_ca_cert + resp_ca_cert->cert_len, "mta:ca_key"); ssl = ssl_mta_init(resp_ca_cert->cert, resp_ca_cert->cert_len, resp_ca_cert->key, resp_ca_cert->key_len); if (ssl == NULL) fatal("mta: ssl_mta_init"); io_start_tls(&s->io, ssl); memset(resp_ca_cert->cert, 0, resp_ca_cert->cert_len); memset(resp_ca_cert->key, 0, resp_ca_cert->key_len); free(resp_ca_cert->cert); free(resp_ca_cert->key); free(resp_ca_cert); return; case IMSG_LKA_SSL_VERIFY: resp_ca_vrfy = imsg->data; s = mta_tree_pop(&wait_ssl_verify, resp_ca_vrfy->reqid); if (s == NULL) return; if (resp_ca_vrfy->status == CA_OK) s->flags |= MTA_VERIFIED; else if (s->relay->flags & F_TLS_VERIFY) { errno = 0; mta_error(s, "SSL certificate check failed"); mta_free(s); return; } mta_io(&s->io, IO_TLSVERIFIED); io_resume(&s->io, IO_PAUSE_IN); io_reload(&s->io); return; case IMSG_LKA_HELO: m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_int(&m, &status); if (status == LKA_OK) m_get_string(&m, &name); m_end(&m); s = mta_tree_pop(&wait_helo, reqid); if (s == NULL) return; if (status == LKA_OK) { s->helo = xstrdup(name, "mta_session_imsg"); mta_connect(s); } else { mta_source_error(s->relay, s->route, "Failed to retrieve helo string"); mta_free(s); } return; default: errx(1, "mta_session_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); } }
void mta_session_imsg(struct imsgev *iev, struct imsg *imsg) { uint64_t id; struct mta_session *s; struct mta_host *host; struct secret *secret; struct dns *dns; const char *error; void *ptr; switch(imsg->hdr.type) { case IMSG_QUEUE_MESSAGE_FD: id = *(uint64_t*)(imsg->data); if (imsg->fd == -1) fatalx("mta: cannot obtain msgfd"); s = tree_xget(&sessions, id); s->datafp = fdopen(imsg->fd, "r"); if (s->datafp == NULL) fatal("mta: fdopen"); if (mta_check_loop(s->datafp)) { log_debug("mta: loop detected"); fclose(s->datafp); s->datafp = NULL; mta_status(s, 0, "646 Loop detected"); mta_enter_state(s, MTA_SMTP_READY); } else { mta_enter_state(s, MTA_SMTP_MAIL); } return; case IMSG_LKA_SECRET: /* LKA responded to AUTH lookup. */ secret = imsg->data; s = tree_xget(&sessions, secret->id); s->secret = xstrdup(secret->secret, "mta: secret"); if (s->secret[0] == '\0') { mta_route_error(s->route, "secrets lookup failed"); mta_enter_state(s, MTA_DONE); } else mta_enter_state(s, MTA_MX); return; case IMSG_DNS_HOST: dns = imsg->data; s = tree_xget(&sessions, dns->id); host = xcalloc(1, sizeof *host, "mta: host"); host->sa = dns->ss; TAILQ_INSERT_TAIL(&s->hosts, host, entry); return; case IMSG_DNS_HOST_END: /* LKA responded to DNS lookup. */ dns = imsg->data; s = tree_xget(&sessions, dns->id); if (!dns->error) { mta_enter_state(s, MTA_CONNECT); return; } if (dns->error == DNS_RETRY) error = "100 MX lookup failed temporarily"; else if (dns->error == DNS_EINVAL) error = "600 Invalid domain name"; else if (dns->error == DNS_ENONAME) error = "600 Domain does not exist"; else if (dns->error == DNS_ENOTFOUND) error = "600 No MX address found for domain"; else error = "100 Weird error"; mta_route_error(s->route, error); mta_enter_state(s, MTA_CONNECT); return; case IMSG_DNS_PTR: dns = imsg->data; s = tree_xget(&sessions, dns->id); host = TAILQ_FIRST(&s->hosts); if (dns->error) strlcpy(host->fqdn, "<unknown>", sizeof host->fqdn); else strlcpy(host->fqdn, dns->host, sizeof host->fqdn); log_debug("mta: %p: connected to %s", s, host->fqdn); /* check if we need to start tls now... */ if (((s->flags & MTA_FORCE_ANYSSL) && host->used == 1) || (s->flags & MTA_FORCE_SMTPS)) { log_debug("mta: %p: trying smtps (ssl=%p)...", s, s->ssl); if ((ptr = ssl_mta_init(s->ssl)) == NULL) fatalx("mta: ssl_mta_init"); io_start_tls(&s->io, ptr); } else { mta_enter_state(s, MTA_SMTP_BANNER); } break; default: errx(1, "mta_session_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); } }