Esempio n. 1
0
static void
mta_route_drain(struct mta_route *route)
{
	struct mta_task		*task;
	struct envelope		*e;

	log_debug("mta: draining %s (tasks=%i, refs=%i, sessions=%i)",
	    mta_route_to_text(route),
	    route->ntask, route->refcount, route->nsession);

	if (route->ntask == 0 && route->refcount == 0 && route->nsession == 0) {
		mta_route_free(route);
		stat_decrement("mta.route", 1);
		return;
	}

	if (route->ntask == 0) {
		log_debug("mta: no task for %s", mta_route_to_text(route));
		return;
	}

	if (route->nfail > 3) {
		/* Three connection errors in a row: consider that the route
		 * has a problem.
		 */
		log_debug("mta: too many failures on %s",
		    mta_route_to_text(route));

		while ((task = TAILQ_FIRST(&route->tasks))) {
			TAILQ_REMOVE(&route->tasks, task, entry);
			route->ntask -= 1;
			while((e = TAILQ_FIRST(&task->envelopes)))
				mta_envelope_done(task, e, route->errorline);
			free(task);
			stat_decrement("mta.task", 1);
		}
		route->nfail = 0;
		/* XXX maybe close the route for while */
		return;
	}

	/* make sure there are one session for each task */
	while (route->nsession < route->ntask) {
		/* if we have reached the max number of session, wait */
		if (route->nsession >= route->maxconn) {
			log_debug("mta: max conn reached for %s",
			    mta_route_to_text(route));
			return;
		}
		route->nsession += 1;
		mta_session(route);
	}
}
Esempio n. 2
0
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);
}
Esempio n. 3
0
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);
}
Esempio n. 4
0
static int
queue_ram_envelope_update(uint64_t evpid, const char *buf, size_t len)
{
	struct qr_envelope	*evp;
	struct qr_message	*msg;
	void			*tmp;

	if ((msg = get_message(evpid_to_msgid(evpid))) == NULL)
		return (0);

	if ((evp = tree_get(&msg->envelopes, evpid)) == NULL) {
		log_warn("warn: queue-ram: not found");
		return (0);
	}
	tmp = malloc(len);
	if (tmp == NULL) {
		log_warn("warn: queue-ram: malloc");
		return (0);
	}
	memmove(tmp, buf, len);
	free(evp->buf);
	evp->len = len;
	evp->buf = tmp;
	stat_decrement("queue.ram.envelope.size", evp->len);
	stat_increment("queue.ram.envelope.size", len);
	return (1);
}
Esempio n. 5
0
static void
mta_status(struct mta_session *s, int connerr, const char *fmt, ...)
{
	struct envelope		*e;
	char			*status;
	va_list			 ap;

	va_start(ap, fmt);
	if (vasprintf(&status, fmt, ap) == -1)
		fatal("vasprintf");
	va_end(ap);

	if (s->task) {
		while((e = TAILQ_FIRST(&s->task->envelopes)))
			mta_envelope_done(s->task, e, status);
		free(s->task);
		s->task = NULL;
		stat_decrement("mta.task.running", 1);
	}

	if (connerr)
		mta_route_error(s->route, status);

	free(status);
}
Esempio n. 6
0
static int
queue_ram_message_delete(uint32_t msgid)
{
	struct qr_message	*msg;
	struct qr_envelope	*evp;
	uint64_t		 evpid;

	if ((msg = tree_pop(&messages, msgid)) == NULL) {
		log_warnx("warn: queue-ram: not found");
		return (0);
	}
	while (tree_poproot(&messages, &evpid, (void**)&evp)) {
		stat_decrement("queue.ram.envelope.size", evp->len);
		free(evp->buf);
		free(evp);
	}
	stat_decrement("queue.ram.message.size", msg->len);
	free(msg->buf);
	free(msg);
	return (0);
}
Esempio n. 7
0
static void
queue_envelope_cache_del(uint64_t evpid)
{
	struct envelope *cached;

	if ((cached = tree_pop(&evpcache_tree, evpid)) == NULL)
		return;

	TAILQ_REMOVE(&evpcache_list, cached, entry);
	free(cached);
	stat_decrement("queue.evpcache.size", 1);
}
Esempio n. 8
0
static int
queue_ram_envelope_delete(uint64_t evpid)
{
	struct qr_envelope	*evp;
	struct qr_message	*msg;

	if ((msg = get_message(evpid_to_msgid(evpid))) == NULL)
		return (0);

	if ((evp = tree_pop(&msg->envelopes, evpid)) == NULL) {
		log_warnx("warn: queue-ram: not found");
		return (0);
	}
	stat_decrement("queue.ram.envelope.size", evp->len);
	free(evp->buf);
	free(evp);
	if (tree_empty(&msg->envelopes)) {
		tree_xpop(&messages, evpid_to_msgid(evpid));
		stat_decrement("queue.ram.message.size", msg->len);
		free(msg->buf);
		free(msg);
	}
	return (1);
}
Esempio n. 9
0
static void
mta_envelope_done(struct mta_task *task, struct envelope *e, const char *status)
{
	char	relay[MAX_LINE_SIZE];

	envelope_set_errormsg(e, "%s", status);

	snprintf(relay, sizeof relay, "relay=%s, ", task->route->hostname);
	log_envelope(e, relay, e->errorline);

	imsg_compose_event(env->sc_ievs[PROC_QUEUE],
	    mta_response_delivery(e->errorline), 0, 0, -1, e, sizeof(*e));
	TAILQ_REMOVE(&task->envelopes, e, entry);
	free(e);
	stat_decrement("mta.envelope", 1);
}
Esempio n. 10
0
void
control_close(int fd)
{
	struct ctl_conn	*c;

	if ((c = control_connbyfd(fd)) == NULL) {
		log_warn("control_close: fd %d: not found", fd);
		return;
	}
	TAILQ_REMOVE(&ctl_conns, c, entry);
	event_del(&c->iev.ev);
	imsg_clear(&c->iev.ibuf);
	close(fd);
	free(c);

	if (stat_decrement(STATS_CONTROL_SESSION) < env->sc_maxconn &&
	    !event_pending(&control_state.ev, EV_READ, NULL)) {
		log_warnx("re-enabling ctl connections");
		event_add(&control_state.ev, NULL);
	}
}
Esempio n. 11
0
static void
mta_envelope_done(struct mta_task *task, struct envelope *e, const char *status)
{
	struct	mta_host *host = TAILQ_FIRST(&task->session->hosts);
	char		  relay[MAX_LINE_SIZE], stat[MAX_LINE_SIZE];

	envelope_set_errormsg(e, "%s", status);

	snprintf(relay, sizeof relay, "relay=%s [%s], ",
	    host->fqdn, ss_to_text(&host->sa));
	snprintf(stat, sizeof stat, "%s (%s)",
	    mta_response_status(e->errorline),
	    mta_response_text(e->errorline));
	log_envelope(e, relay, stat);

	imsg_compose_event(env->sc_ievs[PROC_QUEUE],
	    mta_response_delivery(e->errorline), 0, 0, -1, e, sizeof(*e));

	TAILQ_REMOVE(&task->envelopes, e, entry);
	free(e);
	stat_decrement("mta.envelope", 1);
}
Esempio n. 12
0
/*
 * 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");
	}
}
Esempio n. 13
0
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);
}
Esempio n. 14
0
void
runner_imsg(struct imsgev *iev, struct imsg *imsg)
{
	struct envelope	*e, bounce;
	struct scheduler_info	si;

	log_imsg(PROC_RUNNER, iev->proc, imsg);

	switch (imsg->hdr.type) {
	case IMSG_QUEUE_COMMIT_MESSAGE:
		e = imsg->data;
		runner_message_to_scheduler(evpid_to_msgid(e->id));
		runner_reset_events();
		return;

	case IMSG_QUEUE_DELIVERY_OK:
		stat_decrement(STATS_RUNNER);
		e = imsg->data;
		log_debug("queue_delivery_ok: %016"PRIx64, e->id);
		scheduler->remove(e->id);
		queue_envelope_delete(e);
		return;

	case IMSG_QUEUE_DELIVERY_TEMPFAIL:
		stat_decrement(STATS_RUNNER);
		e = imsg->data;
		e->retry++;
		queue_envelope_update(e);
		log_debug("queue_delivery_tempfail: %016"PRIx64, e->id);
		scheduler_info(&si, e);
		scheduler->insert(&si);
		runner_reset_events();
		return;

	case IMSG_QUEUE_DELIVERY_PERMFAIL:
		stat_decrement(STATS_RUNNER);
		e = imsg->data;
		if (e->type != D_BOUNCE && e->sender.user[0] != '\0') {
			bounce_record_message(e, &bounce);
			log_debug("queue_delivery_permfail: %016"PRIx64,
			    bounce.id);
			scheduler_info(&si, &bounce);
			scheduler->insert(&si);
			runner_reset_events();
		}
		scheduler->remove(e->id);
		queue_envelope_delete(e);
		return;

	case IMSG_MDA_SESS_NEW:
		stat_decrement(STATS_MDA_SESSION);
		if (env->sc_maxconn - stat_get(STATS_MDA_SESSION, STAT_ACTIVE))
			env->sc_flags &= ~SMTPD_MDA_BUSY;
		runner_reset_events();
		return;

	case IMSG_BATCH_DONE:
		stat_decrement(STATS_MTA_SESSION);
		if (env->sc_maxconn - stat_get(STATS_MTA_SESSION, STAT_ACTIVE))
			env->sc_flags &= ~SMTPD_MTA_BUSY;
		runner_reset_events();
		return;

	case IMSG_SMTP_ENQUEUE:
		e = imsg->data;
		if (imsg->fd < 0 || !bounce_session(imsg->fd, e)) {
			queue_envelope_update(e);
			log_debug("smtp_enqueue: %016"PRIx64, e->id);
			scheduler_info(&si, e);
			scheduler->insert(&si);
			runner_reset_events();
			return;
		}
		return;

	case IMSG_QUEUE_PAUSE_MDA:
		env->sc_flags |= SMTPD_MDA_PAUSED;
		return;

	case IMSG_QUEUE_RESUME_MDA:
		env->sc_flags &= ~SMTPD_MDA_PAUSED;
		runner_reset_events();
		return;

	case IMSG_QUEUE_PAUSE_MTA:
		env->sc_flags |= SMTPD_MTA_PAUSED;
		return;

	case IMSG_QUEUE_RESUME_MTA:
		env->sc_flags &= ~SMTPD_MTA_PAUSED;
		runner_reset_events();
		return;

	case IMSG_CTL_VERBOSE:
		log_verbose(*(int *)imsg->data);
		return;

	case IMSG_RUNNER_SCHEDULE:
		scheduler->force(*(u_int64_t *)imsg->data);
		runner_reset_events();		
		return;

	case IMSG_RUNNER_REMOVE: {
		runner_remove(*(u_int64_t *)imsg->data);
		runner_reset_events();
		return;
	}
	}

	errx(1, "runner_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
}
Esempio n. 15
0
/*
 * 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");
	}
}
Esempio n. 16
0
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
}