static int lka_addrname(const char *tablename, const struct sockaddr *sa, struct addrname *res) { struct table *table; union lookup lk; const char *source; source = sa_to_text(sa); log_debug("debug: lka: helo %s:%s", tablename, source); table = table_find(tablename, NULL); if (table == NULL) { log_warnx("warn: cannot find helo table %s", tablename); return (LKA_TEMPFAIL); } switch (table_lookup(table, source, K_ADDRNAME, &lk)) { case -1: log_warnx("warn: failure during helo lookup %s:%s", tablename, source); return (LKA_TEMPFAIL); case 0: return (LKA_PERMFAIL); default: *res = lk.addrname; return (LKA_OK); } }
/* * Handle a response to an SMTP command */ static void mta_response(struct mta_session *s, char *line) { struct mta_envelope *e; struct sockaddr_storage ss; struct sockaddr *sa; const char *domain; socklen_t sa_len; char buf[SMTPD_MAXLINESIZE]; int delivery; switch (s->state) { case MTA_BANNER: if (s->flags & MTA_LMTP) mta_enter_state(s, MTA_LHLO); else mta_enter_state(s, MTA_EHLO); break; case MTA_EHLO: if (line[0] != '2') { /* rejected at ehlo state */ if ((s->flags & MTA_USE_AUTH) || (s->flags & MTA_WANT_SECURE)) { mta_error(s, "EHLO rejected: %s", line); s->flags |= MTA_FREE; return; } mta_enter_state(s, MTA_HELO); return; } if (!(s->flags & MTA_FORCE_PLAIN)) mta_enter_state(s, MTA_STARTTLS); else mta_enter_state(s, MTA_READY); break; case MTA_HELO: if (line[0] != '2') { mta_error(s, "HELO rejected: %s", line); s->flags |= MTA_FREE; return; } mta_enter_state(s, MTA_READY); break; case MTA_LHLO: if (line[0] != '2') { mta_error(s, "LHLO rejected: %s", line); s->flags |= MTA_FREE; return; } mta_enter_state(s, MTA_READY); break; case MTA_STARTTLS: if (line[0] != '2') { if (!(s->flags & MTA_WANT_SECURE)) { mta_enter_state(s, MTA_AUTH); return; } /* XXX mark that the MX doesn't support STARTTLS */ mta_error(s, "STARTTLS rejected: %s", line); s->flags |= MTA_FREE; return; } mta_start_tls(s); break; case MTA_AUTH_PLAIN: if (line[0] != '2') { mta_error(s, "AUTH rejected: %s", line); s->flags |= MTA_FREE; return; } mta_enter_state(s, MTA_READY); break; case MTA_AUTH_LOGIN: if (strncmp(line, "334 ", 4) != 0) { mta_error(s, "AUTH rejected: %s", line); s->flags |= MTA_FREE; return; } mta_enter_state(s, MTA_AUTH_LOGIN_USER); break; case MTA_AUTH_LOGIN_USER: if (strncmp(line, "334 ", 4) != 0) { mta_error(s, "AUTH rejected: %s", line); s->flags |= MTA_FREE; return; } mta_enter_state(s, MTA_AUTH_LOGIN_PASS); break; case MTA_AUTH_LOGIN_PASS: if (line[0] != '2') { mta_error(s, "AUTH rejected: %s", line); s->flags |= MTA_FREE; return; } mta_enter_state(s, MTA_READY); break; case MTA_MAIL: if (line[0] != '2') { if (line[0] == '5') delivery = IMSG_DELIVERY_PERMFAIL; else delivery = IMSG_DELIVERY_TEMPFAIL; mta_flush_task(s, delivery, line, 0, 0); mta_enter_state(s, MTA_RSET); return; } mta_enter_state(s, MTA_RCPT); break; case MTA_RCPT: e = s->currevp; /* remove envelope from hosttat cache if there */ if ((domain = strchr(e->dest, '@')) != NULL) { domain++; mta_hoststat_uncache(domain, e->id); } s->currevp = TAILQ_NEXT(s->currevp, entry); if (line[0] == '2') { s->failures = 0; /* * this host is up, reschedule envelopes that * were cached for reschedule. */ if (domain) mta_hoststat_reschedule(domain); } else { if (line[0] == '5') delivery = IMSG_DELIVERY_PERMFAIL; else delivery = IMSG_DELIVERY_TEMPFAIL; s->failures++; /* remove failed envelope from task list */ TAILQ_REMOVE(&s->task->envelopes, e, entry); stat_decrement("mta.envelope", 1); /* log right away */ snprintf(buf, sizeof(buf), "%s", mta_host_to_text(s->route->dst)); e->session = s->id; /* XXX */ /* * getsockname() can only fail with ENOBUFS here * best effort, don't log source ... */ sa_len = sizeof(ss); sa = (struct sockaddr *)&ss; if (getsockname(s->io.sock, sa, &sa_len) < 0) mta_delivery_log(e, NULL, buf, delivery, line); else mta_delivery_log(e, sa_to_text(sa), buf, delivery, line); if (domain) mta_hoststat_update(domain + 1, e->status); mta_delivery_notify(e); if (s->relay->limits->max_failures_per_session && s->failures == s->relay->limits->max_failures_per_session) { mta_flush_task(s, IMSG_DELIVERY_TEMPFAIL, "Too many consecutive errors, closing connection", 0, 1); mta_enter_state(s, MTA_QUIT); break; } /* * if no more envelopes, flush failed queue */ if (TAILQ_EMPTY(&s->task->envelopes)) { mta_flush_task(s, IMSG_DELIVERY_OK, "No envelope", 0, 0); mta_enter_state(s, MTA_RSET); break; } } if (s->currevp == NULL) mta_enter_state(s, MTA_DATA); else mta_enter_state(s, MTA_RCPT); break; case MTA_DATA: if (line[0] == '2' || line[0] == '3') { mta_enter_state(s, MTA_BODY); break; } if (line[0] == '5') delivery = IMSG_DELIVERY_PERMFAIL; else delivery = IMSG_DELIVERY_TEMPFAIL; mta_flush_task(s, delivery, line, 0, 0); mta_enter_state(s, MTA_RSET); break; case MTA_LMTP_EOM: case MTA_EOM: if (line[0] == '2') { delivery = IMSG_DELIVERY_OK; s->msgtried = 0; s->msgcount++; } else if (line[0] == '5') delivery = IMSG_DELIVERY_PERMFAIL; else delivery = IMSG_DELIVERY_TEMPFAIL; mta_flush_task(s, delivery, line, (s->flags & MTA_LMTP) ? 1 : 0, 0); if (s->task) { s->rcptcount--; mta_enter_state(s, MTA_LMTP_EOM); } else { s->rcptcount = 0; if (s->relay->limits->sessdelay_transaction) { log_debug("debug: mta: waiting for %llds before next transaction", (long long int)s->relay->limits->sessdelay_transaction); s->hangon = s->relay->limits->sessdelay_transaction -1; s->flags |= MTA_HANGON; runq_schedule(hangon, time(NULL) + s->relay->limits->sessdelay_transaction, NULL, s); } else mta_enter_state(s, MTA_READY); } break; case MTA_RSET: s->rcptcount = 0; if (s->relay->limits->sessdelay_transaction) { log_debug("debug: mta: waiting for %llds after reset", (long long int)s->relay->limits->sessdelay_transaction); s->hangon = s->relay->limits->sessdelay_transaction -1; s->flags |= MTA_HANGON; runq_schedule(hangon, time(NULL) + s->relay->limits->sessdelay_transaction, NULL, s); } else mta_enter_state(s, MTA_READY); break; default: fatalx("mta_response() bad state"); } }
static void mta_connect(struct mta_session *s) { struct sockaddr_storage ss; struct sockaddr *sa; int portno; const char *schema = "smtp+tls://"; if (s->helo == NULL) { if (s->relay->helotable && s->route->src->sa) { m_create(p_lka, IMSG_LKA_HELO, 0, 0, -1); m_add_id(p_lka, s->id); m_add_string(p_lka, s->relay->helotable); m_add_sockaddr(p_lka, s->route->src->sa); m_close(p_lka); tree_xset(&wait_helo, s->id, s); s->flags |= MTA_WAIT; return; } else if (s->relay->heloname) s->helo = xstrdup(s->relay->heloname, "mta_connect"); else s->helo = xstrdup(env->sc_hostname, "mta_connect"); } io_clear(&s->io); iobuf_clear(&s->iobuf); s->use_smtps = s->use_starttls = s->use_smtp_tls = 0; switch (s->attempt) { case 0: if (s->flags & MTA_FORCE_SMTPS) s->use_smtps = 1; /* smtps */ else if (s->flags & (MTA_FORCE_TLS|MTA_FORCE_ANYSSL)) s->use_starttls = 1; /* tls, tls+smtps */ else if (!(s->flags & MTA_FORCE_PLAIN)) s->use_smtp_tls = 1; break; case 1: if (s->flags & MTA_FORCE_ANYSSL) { s->use_smtps = 1; /* tls+smtps */ break; } default: mta_free(s); return; } portno = s->use_smtps ? 465 : 25; /* Override with relay-specified port */ if (s->relay->port) portno = s->relay->port; memmove(&ss, s->route->dst->sa, s->route->dst->sa->sa_len); sa = (struct sockaddr *)&ss; if (sa->sa_family == AF_INET) ((struct sockaddr_in *)sa)->sin_port = htons(portno); else if (sa->sa_family == AF_INET6) ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); s->attempt += 1; if (s->use_smtp_tls) schema = "smtp+tls://"; else if (s->use_starttls) schema = "tls://"; else if (s->use_smtps) schema = "smtps://"; else if (s->flags & MTA_LMTP) schema = "lmtp://"; else schema = "smtp://"; log_info("smtp-out: Connecting to %s%s:%d (%s) on session" " %016"PRIx64"...", schema, sa_to_text(s->route->dst->sa), portno, s->route->dst->ptrname, s->id); mta_enter_state(s, MTA_INIT); iobuf_xinit(&s->iobuf, 0, 0, "mta_connect"); io_init(&s->io, -1, s, mta_io, &s->iobuf); io_set_timeout(&s->io, 300000); if (io_connect(&s->io, sa, s->route->src->sa) == -1) { /* * This error is most likely a "no route", * so there is no need to try again. */ log_debug("debug: mta: io_connect failed: %s", s->io.error); if (errno == EADDRNOTAVAIL) mta_source_error(s->relay, s->route, s->io.error); else mta_error(s, "Connection failed: %s", s->io.error); mta_free(s); } }
static void mta_flush_task(struct mta_session *s, int delivery, const char *error, size_t count, int cache) { struct mta_envelope *e; char relay[SMTPD_MAXLINESIZE]; size_t n; struct sockaddr_storage ss; struct sockaddr *sa; socklen_t sa_len; const char *domain; snprintf(relay, sizeof relay, "%s", mta_host_to_text(s->route->dst)); n = 0; while ((e = TAILQ_FIRST(&s->task->envelopes))) { if (count && n == count) { stat_decrement("mta.envelope", n); return; } TAILQ_REMOVE(&s->task->envelopes, e, entry); /* we're about to log, associate session to envelope */ e->session = s->id; e->ext = s->ext; /* XXX */ /* * getsockname() can only fail with ENOBUFS here * best effort, don't log source ... */ sa = (struct sockaddr *)&ss; sa_len = sizeof(ss); if (getsockname(s->io.sock, sa, &sa_len) < 0) mta_delivery_log(e, NULL, relay, delivery, error); else mta_delivery_log(e, sa_to_text(sa), relay, delivery, error); mta_delivery_notify(e); domain = strchr(e->dest, '@'); if (domain) { mta_hoststat_update(domain + 1, error); if (cache) mta_hoststat_cache(domain + 1, e->id); } n++; } free(s->task->sender); free(s->task); s->task = NULL; if (s->datafp) { fclose(s->datafp); s->datafp = NULL; } stat_decrement("mta.envelope", n); stat_decrement("mta.task.running", 1); stat_decrement("mta.task", 1); }