static void filter_io_in(struct io *io, int evt) { struct filter_session *s = io->arg; char *line; size_t len; log_trace(TRACE_FILTERS, "filter-api:%s filter_io_in(%p, %s)", filter_name, s, io_strevent(evt)); switch (evt) { case IO_DATAIN: nextline: line = iobuf_getline(&s->pipe.ibuf, &len); if ((line == NULL && iobuf_len(&s->pipe.ibuf) >= LINE_MAX) || (line && len >= LINE_MAX)) { s->pipe.error = 1; break; } /* No complete line received */ if (line == NULL) { iobuf_normalize(&s->pipe.ibuf); /* flow control */ if (iobuf_queued(&s->pipe.obuf) >= FILTER_HIWAT) io_pause(&s->pipe.iev, IO_PAUSE_IN); return; } s->pipe.idatalen += len + 1; /* XXX warning: do not clear io from this call! */ filter_dispatch_dataline(s->id, line); goto nextline; case IO_DISCONNECTED: if (iobuf_len(&s->pipe.ibuf)) { log_warn("warn: filter-api:%s %016"PRIx64" incomplete input", filter_name, s->id); } log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" input done (%zu bytes)", filter_name, s->id, s->pipe.idatalen); break; default: log_warn("warn: filter-api:%s %016"PRIx64": unexpected io event %d on data pipe", filter_name, s->id, evt); s->pipe.error = 1; } if (s->pipe.error) { io_clear(&s->pipe.oev); iobuf_clear(&s->pipe.obuf); } io_clear(&s->pipe.iev); iobuf_clear(&s->pipe.ibuf); filter_trigger_eom(s); }
static void mta_free(struct mta_session *s) { struct mta_relay *relay; struct mta_route *route; log_debug("debug: mta: %p: session done", s); if (s->ready) s->relay->nconn_ready -= 1; if (s->flags & MTA_HANGON) { log_debug("debug: mta: %p: cancelling hangon timer", s); runq_cancel(hangon, NULL, s); } io_clear(&s->io); iobuf_clear(&s->iobuf); if (s->task) fatalx("current task should have been deleted already"); if (s->datafp) fclose(s->datafp); if (s->helo) free(s->helo); relay = s->relay; route = s->route; free(s); stat_decrement("mta.session", 1); mta_route_collect(relay, route); }
static void clamav_clear(struct clamav *cl) { if (cl == NULL) return; iobuf_clear(&cl->iobuf); clamav_close(cl); free(cl); }
static void filter_io_out(struct io *io, int evt) { struct filter_session *s = io->arg; log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" filter_io_out(%s)", filter_name, s->id, io_strevent(evt)); switch (evt) { case IO_TIMEOUT: case IO_DISCONNECTED: case IO_ERROR: log_trace(TRACE_FILTERS, "filter-api:%s %016"PRIx64" io error on output pipe", filter_name, s->id); s->pipe.error = 1; break; case IO_LOWAT: /* flow control */ if (s->pipe.iev.sock != -1 && s->pipe.iev.flags & IO_PAUSE_IN) { io_resume(&s->pipe.iev, IO_PAUSE_IN); return; } /* if the input is done and there is a response we are done */ if (s->pipe.iev.sock == -1 && s->response.ready) break; /* just wait for more data to send */ return; default: fatalx("filter_io_out()"); } io_clear(&s->pipe.oev); iobuf_clear(&s->pipe.obuf); if (s->pipe.error) { io_clear(&s->pipe.iev); iobuf_clear(&s->pipe.ibuf); } filter_trigger_eom(s); }
void session_destroy(struct session *s, const char * reason) { uint32_t msgid; log_debug("smtp: %p: deleting session: %s", s, reason); if (s->s_flags & F_ZOMBIE) goto finalize; log_debug("session_destroy: s->datafp = %p", s->datafp); if (s->datafp != NULL) fclose(s->datafp); if (s->s_msg.id != 0 && s->s_state != S_DONE) { msgid = evpid_to_msgid(s->s_msg.id); imsg_compose_event(env->sc_ievs[PROC_QUEUE], IMSG_QUEUE_REMOVE_MESSAGE, 0, 0, -1, &msgid, sizeof(msgid)); } if (s->s_io.ssl) { if (s->s_l->flags & F_SMTPS) if (s->s_flags & F_SECURE) stat_decrement("smtp.smtps", 1); if (s->s_l->flags & F_STARTTLS) if (s->s_flags & F_SECURE) stat_decrement("smtp.tls", 1); } event_del(&s->s_ev); /* in case something was scheduled */ io_clear(&s->s_io); iobuf_clear(&s->s_iobuf); /* resume when session count decreases to 95% */ stat_decrement("smtp.session", 1); /* If the session is waiting for an imsg, do not kill it now, since * the id must still be valid. */ if (s->s_flags & F_WAITIMSG) { s->s_flags = F_ZOMBIE; return; } finalize: smtp_destroy(s); SPLAY_REMOVE(sessiontree, &env->sc_sessions, s); bzero(s, sizeof(*s)); free(s); }
void io_free(struct io *io) { io_debug("io_clear(%p)\n", io); /* the current io is virtually dead */ if (io == current) current = NULL; #ifdef IO_SSL SSL_free(io->ssl); io->ssl = NULL; #endif if (event_initialized(&io->ev)) event_del(&io->ev); if (io->sock != -1) { close(io->sock); io->sock = -1; } iobuf_clear(&io->iobuf); free(io); }
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_enter_state(struct mta_session *s, int newstate) { int oldstate; struct secret secret; struct mta_route *route; struct mta_host *host; struct sockaddr *sa; int max_reuse; ssize_t q; #ifdef VALGRIND bzero(&batch, sizeof(batch)); #endif again: oldstate = s->state; log_trace(TRACE_MTA, "mta: %p: %s -> %s", s, mta_strstate(oldstate), mta_strstate(newstate)); s->state = newstate; /* don't try this at home! */ #define mta_enter_state(_s, _st) do { newstate = _st; goto again; } while(0) switch (s->state) { case MTA_INIT: if (s->route->auth) mta_enter_state(s, MTA_SECRET); else mta_enter_state(s, MTA_MX); break; case MTA_DATA: /* * Obtain message body fd. */ imsg_compose_event(env->sc_ievs[PROC_QUEUE], IMSG_QUEUE_MESSAGE_FD, s->task->msgid, 0, -1, &s->id, sizeof(s->id)); break; case MTA_SECRET: /* * Lookup AUTH secret. */ bzero(&secret, sizeof(secret)); secret.id = s->id; strlcpy(secret.mapname, s->route->auth, sizeof(secret.mapname)); strlcpy(secret.host, s->route->hostname, sizeof(secret.host)); imsg_compose_event(env->sc_ievs[PROC_LKA], IMSG_LKA_SECRET, 0, 0, -1, &secret, sizeof(secret)); break; case MTA_MX: /* * Lookup MX record. */ if (s->flags & MTA_FORCE_MX) /* XXX */ dns_query_host(s->route->hostname, s->route->port, s->id); else dns_query_mx(s->route->hostname, s->route->backupname, 0, s->id); break; case MTA_CONNECT: /* * Connect to the MX. */ /* cleanup previous connection if any */ iobuf_clear(&s->iobuf); io_clear(&s->io); if (s->flags & MTA_FORCE_ANYSSL) max_reuse = 2; else max_reuse = 1; /* pick next mx */ while ((host = TAILQ_FIRST(&s->hosts))) { if (host->used == max_reuse) { TAILQ_REMOVE(&s->hosts, host, entry); free(host); continue; } host->used++; log_debug("mta: %p: connecting to %s...", s, ss_to_text(&host->sa)); sa = (struct sockaddr *)&host->sa; if (s->route->port) sa_set_port(sa, s->route->port); else if ((s->flags & MTA_FORCE_ANYSSL) && host->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); iobuf_xinit(&s->iobuf, 0, 0, "mta_enter_state"); io_init(&s->io, -1, s, mta_io, &s->iobuf); io_set_timeout(&s->io, 10000); if (io_connect(&s->io, sa, NULL) == -1) { log_debug("mta: %p: connection failed: %s", s, strerror(errno)); iobuf_clear(&s->iobuf); /* * This error is most likely a "no route", * so there is no need to try the same * relay again. */ TAILQ_REMOVE(&s->hosts, host, entry); free(host); continue; } return; } /* tried them all? */ mta_route_error(s->route, "150 Can not connect to MX"); mta_enter_state(s, MTA_DONE); break; case MTA_DONE: /* * Kill the mta session. */ log_debug("mta: %p: session done", s); io_clear(&s->io); iobuf_clear(&s->iobuf); if (s->task) fatalx("current task should have been deleted already"); if (s->datafp) fclose(s->datafp); s->datafp = NULL; while ((host = TAILQ_FIRST(&s->hosts))) { TAILQ_REMOVE(&s->hosts, host, entry); free(host); } route = s->route; tree_xpop(&sessions, s->id); free(s); stat_decrement("mta.session", 1); mta_route_collect(route); break; case MTA_SMTP_BANNER: /* just wait for banner */ s->is_reading = 1; io_set_read(&s->io); break; case MTA_SMTP_EHLO: s->ext = 0; mta_send(s, "EHLO %s", env->sc_hostname); break; case MTA_SMTP_HELO: s->ext = 0; mta_send(s, "HELO %s", env->sc_hostname); break; case MTA_SMTP_STARTTLS: if (s->flags & MTA_TLS) /* already started */ mta_enter_state(s, MTA_SMTP_AUTH); else if ((s->ext & MTA_EXT_STARTTLS) == 0) /* server doesn't support starttls, do not use it */ mta_enter_state(s, MTA_SMTP_AUTH); else mta_send(s, "STARTTLS"); break; case MTA_SMTP_AUTH: if (s->secret && s->flags & MTA_TLS) mta_send(s, "AUTH PLAIN %s", s->secret); else if (s->secret) { log_debug("mta: %p: not using AUTH on non-TLS session", s); mta_enter_state(s, MTA_CONNECT); } else { mta_enter_state(s, MTA_SMTP_READY); } break; case MTA_SMTP_READY: /* ready to send a new mail */ if (s->ready == 0) { s->ready = 1; mta_route_ok(s->route); } if (s->msgcount >= s->route->maxmail) { log_debug("mta: %p: cannot send more message to %s", s, mta_route_to_text(s->route)); mta_enter_state(s, MTA_SMTP_QUIT); } else if ((s->task = TAILQ_FIRST(&s->route->tasks))) { log_debug("mta: %p: handling next task for %s", s, mta_route_to_text(s->route)); TAILQ_REMOVE(&s->route->tasks, s->task, entry); s->route->ntask -= 1; s->task->session = s; stat_decrement("mta.task", 1); stat_increment("mta.task.running", 1); mta_enter_state(s, MTA_DATA); } else { log_debug("mta: %p: no pending task for %s", s, mta_route_to_text(s->route)); /* XXX stay open for a while? */ mta_enter_state(s, MTA_SMTP_QUIT); } break; case MTA_SMTP_MAIL: if (s->task->sender.user[0] && s->task->sender.domain[0]) mta_send(s, "MAIL FROM: <%s@%s>", s->task->sender.user, s->task->sender.domain); else mta_send(s, "MAIL FROM: <>"); break; case MTA_SMTP_RCPT: if (s->currevp == NULL) s->currevp = TAILQ_FIRST(&s->task->envelopes); mta_send(s, "RCPT TO: <%s@%s>", s->currevp->dest.user, s->currevp->dest.domain); break; case MTA_SMTP_DATA: fseek(s->datafp, 0, SEEK_SET); mta_send(s, "DATA"); break; case MTA_SMTP_BODY: if (s->datafp == NULL) { log_trace(TRACE_MTA, "mta: %p: end-of-file", s); mta_enter_state(s, MTA_SMTP_DONE); break; } if ((q = mta_queue_data(s)) == -1) { mta_enter_state(s, MTA_DONE); break; } log_trace(TRACE_MTA, "mta: %p: >>> [...%zi bytes...]", s, q); break; case MTA_SMTP_DONE: mta_send(s, "."); break; case MTA_SMTP_QUIT: mta_send(s, "QUIT"); break; case MTA_SMTP_RSET: mta_send(s, "RSET"); break; default: fatalx("mta_enter_state: unknown state"); } #undef mta_enter_state }