示例#1
0
int
envelope_ascii_dump(enum envelope_field field, struct envelope *ep,
    char *buf, size_t len)
{
	switch (field) {
	case EVP_VERSION:
		return ascii_dump_uint32(SMTPD_ENVELOPE_VERSION, buf, len);
	case EVP_MSGID:
		return 1;
	case EVP_TYPE:
		return ascii_dump_type(ep->type, buf, len);
	case EVP_HELO:
		return ascii_dump_string(ep->helo, buf, len);
	case EVP_HOSTNAME:
		return ascii_dump_string(ep->hostname, buf, len);
	case EVP_ERRORLINE:
		return ascii_dump_string(ep->errorline, buf, len);
	case EVP_SOCKADDR:
		return ascii_dump_string(ss_to_text(&ep->ss), buf, len);
	case EVP_SENDER:
		return ascii_dump_mailaddr(&ep->sender, buf, len);
	case EVP_RCPT:
		return ascii_dump_mailaddr(&ep->rcpt, buf, len);
	case EVP_DEST:
		return ascii_dump_mailaddr(&ep->dest, buf, len);
	case EVP_MDA_METHOD:
		return ascii_dump_mda_method(ep->agent.mda.method, buf, len);
	case EVP_MDA_BUFFER:
		return ascii_dump_string(ep->agent.mda.buffer, buf, len);
	case EVP_MDA_USER:
		return ascii_dump_string(ep->agent.mda.user, buf, len);
	case EVP_MTA_RELAY_HOST:
		return ascii_dump_string(ep->agent.mta.relay.hostname,
		    buf, len);
	case EVP_MTA_RELAY_PORT:
		return ascii_dump_mta_relay_port(ep->agent.mta.relay.port,
		    buf, len);
	case EVP_MTA_RELAY_CERT:
		return ascii_dump_string(ep->agent.mta.relay.cert,
		    buf, len);
	case EVP_MTA_RELAY_FLAGS:
		return ascii_dump_mta_relay_flags(ep->agent.mta.relay.flags,
		    buf, len);
	case EVP_MTA_RELAY_AUTHMAP:
		return ascii_dump_string(ep->agent.mta.relay.authmap,
		    buf, len);
	case EVP_CTIME:
		return ascii_dump_time(ep->creation, buf, len);
	case EVP_EXPIRE:
		return ascii_dump_time(ep->expire, buf, len);
	case EVP_RETRY:
		return ascii_dump_uint16(ep->retry, buf, len);
	case EVP_LASTTRY:
		return ascii_dump_time(ep->lasttry, buf, len);
	case EVP_FLAGS:
		return ascii_dump_flags(ep->flags, buf, len);
	}
	return 0;
}
示例#2
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);
}
示例#3
0
void
session_pickup(struct session *s, struct submit_status *ss)
{
	void	*ssl;

	s->s_flags &= ~F_WAITIMSG;

	if ((ss != NULL && ss->code == 421) ||
	    (s->s_dstatus & DS_TEMPFAILURE)) {
		stat_increment("smtp.tempfail", 1);
		session_respond(s, "421 Service temporarily unavailable");
		session_enter_state(s, S_QUIT);
		io_reload(&s->s_io);
		return;
	}

	switch (s->s_state) {

	case S_CONNECTED:
		session_enter_state(s, S_INIT);
		s->s_msg.session_id = s->s_id;
		s->s_msg.ss = s->s_ss;
		session_imsg(s, PROC_MFA, IMSG_MFA_CONNECT, 0, 0, -1,
			     &s->s_msg, sizeof(s->s_msg));
		break;

	case S_INIT:
		if (ss->code != 250) {
			session_destroy(s, "rejected by filter");
			return;
		}

		if (s->s_l->flags & F_SMTPS) {
			ssl = ssl_smtp_init(s->s_l->ssl_ctx);
			io_set_read(&s->s_io);
			io_start_tls(&s->s_io, ssl);
			return;
		}

		session_respond(s, SMTPD_BANNER, env->sc_hostname);
		session_enter_state(s, S_GREETED);
		break;

	case S_AUTH_FINALIZE:
		if (s->s_flags & F_AUTHENTICATED)
			session_respond(s, "235 Authentication succeeded");
		else
			session_respond(s, "535 Authentication failed");
		session_enter_state(s, S_HELO);
		break;

	case S_RSET:
		session_respond(s, "250 2.0.0 Reset state");
		session_enter_state(s, S_HELO);
		break;

	case S_HELO:
		if (ss->code != 250) {
			session_enter_state(s, S_GREETED);
			session_respond(s, "%d Helo rejected", ss->code);
			break;
		}

		session_respond(s, "250%c%s Hello %s [%s], pleased to meet you",
		    (s->s_flags & F_EHLO) ? '-' : ' ',
		    env->sc_hostname, s->s_msg.helo, ss_to_text(&s->s_ss));

		if (s->s_flags & F_EHLO) {
			/* unconditionnal extensions go first */
			session_respond(s, "250-8BITMIME");
			session_respond(s, "250-ENHANCEDSTATUSCODES");

			/* XXX - we also want to support reading SIZE from MAIL parameters */
			session_respond(s, "250-SIZE %zu", env->sc_maxsize);

			if (ADVERTISE_TLS(s))
				session_respond(s, "250-STARTTLS");

			if (ADVERTISE_AUTH(s))
				session_respond(s, "250-AUTH PLAIN LOGIN");
			session_respond(s, "250 HELP");
		}
		break;

	case S_MAIL_MFA:
		if (ss->code != 250) {
			session_enter_state(s, S_HELO);
			session_respond(s, "%d Sender rejected", ss->code);
			break;
		}

		session_enter_state(s, S_MAIL_QUEUE);
		s->s_msg.sender = ss->u.maddr;

		session_imsg(s, PROC_QUEUE, IMSG_QUEUE_CREATE_MESSAGE, 0, 0, -1,
		    &s->s_msg, sizeof(s->s_msg));
		break;

	case S_MAIL_QUEUE:
		session_enter_state(s, S_MAIL);
		session_respond(s, "%d 2.1.0 Sender ok", ss->code);
		break;

	case S_RCPT_MFA:
		/* recipient was not accepted */
		if (ss->code != 250) {
			/* We do not have a valid recipient, downgrade state */
			if (s->rcptcount == 0)
				session_enter_state(s, S_MAIL);
			else
				session_enter_state(s, S_RCPT);
			session_respond(s, "%d 5.0.0 Recipient rejected: %s@%s", ss->code,
			    s->s_msg.rcpt.user,
			    s->s_msg.rcpt.domain);
			break;
		}

		session_enter_state(s, S_RCPT);
		s->rcptcount++;
		s->s_msg.dest = ss->u.maddr;
		session_respond(s, "%d 2.0.0 Recipient ok", ss->code);
		break;

	case S_DATA_QUEUE:
		session_enter_state(s, S_DATACONTENT);
		session_respond(s, "354 Enter mail, end with \".\" on a line by"
		    " itself");

		fprintf(s->datafp, "Received: from %s (%s [%s])\n",
		    s->s_msg.helo, s->s_hostname, ss_to_text(&s->s_ss));
		fprintf(s->datafp, "\tby %s (OpenSMTPD) with %sSMTP id %08x",
		    env->sc_hostname, s->s_flags & F_EHLO ? "E" : "",
		    evpid_to_msgid(s->s_msg.id));

		if (s->s_flags & F_SECURE) {
			fprintf(s->datafp, "\n\t(version=%s cipher=%s bits=%d)",
			    SSL_get_cipher_version(s->s_io.ssl),
			    SSL_get_cipher_name(s->s_io.ssl),
			    SSL_get_cipher_bits(s->s_io.ssl, NULL));
		}
		if (s->rcptcount == 1)
			fprintf(s->datafp, "\n\tfor <%s@%s>; ",
			    s->s_msg.rcpt.user,
			    s->s_msg.rcpt.domain);
		else
			fprintf(s->datafp, ";\n\t");

		fprintf(s->datafp, "%s\n", time_to_text(time(NULL)));
		break;

	case S_DATACONTENT:
		if (ss->code != 250)
			s->s_dstatus |= DS_PERMFAILURE;
		session_read_data(s, ss->u.dataline);
		break;

	case S_DONE:
		session_respond(s, "250 2.0.0 %08x Message accepted for delivery",
		    evpid_to_msgid(s->s_msg.id));
		log_info("%08x: from=<%s%s%s>, size=%ld, nrcpts=%zu, proto=%s, "
		    "relay=%s [%s]",
		    evpid_to_msgid(s->s_msg.id),
		    s->s_msg.sender.user,
		    s->s_msg.sender.user[0] == '\0' ? "" : "@",
		    s->s_msg.sender.domain,
		    s->s_datalen,
		    s->rcptcount,
		    s->s_flags & F_EHLO ? "ESMTP" : "SMTP",
		    s->s_hostname,
		    ss_to_text(&s->s_ss));

		session_enter_state(s, S_HELO);
		s->s_msg.id = 0;
		bzero(&s->s_nresp, sizeof(s->s_nresp));
		break;

	default:
		fatal("session_pickup: unknown state");
	}

	io_reload(&s->s_io);
}
示例#4
0
void
session_respond(struct session *s, char *fmt, ...)
{
	va_list	 ap;
	int	 n, delay;
	char	 buf[SMTP_LINE_MAX];

	va_start(ap, fmt);
	n = vsnprintf(buf, sizeof buf, fmt, ap);
	va_end(ap);
	if (n == -1 || n >= SMTP_LINE_MAX)
		fatal("session_respond: line too long");
	if (n < 4)
		fatal("session_respond: response too short");

	log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf);

	iobuf_xfqueue(&s->s_iobuf, "session_respond", "%s\r\n", buf);

	/*
	 * Log failures.  Might be annoying in the long term, but it is a good
	 * development aid for now.
	 */
	switch (buf[0]) {
	case '5':
	case '4':
		log_info("%08x: from=<%s@%s>, relay=%s [%s], stat=LocalError (%.*s)",
		    evpid_to_msgid(s->s_msg.id),
		    s->s_msg.sender.user, s->s_msg.sender.domain,
		    s->s_hostname, ss_to_text(&s->s_ss),
		    n, buf);
		break;
	}

	/* Detect multi-line response. */
	switch (buf[3]) {
	case '-':
		return;
	case ' ':
		break;
	default:
		fatalx("session_respond: invalid response");
	}

	/*
	 * Deal with request flooding; avoid letting response rate keep up
	 * with incoming request rate.
	 */
	s->s_nresp[s->s_state]++;

	if (s->s_state == S_RCPT)
		delay = 0;
	else if ((n = s->s_nresp[s->s_state] - FAST_RESPONSES) > 0)
		delay = MIN(1 << (n - 1), MAX_RESPONSE_DELAY);
	else
		delay = 0;

	if (delay > 0) {
		struct timeval tv = { delay, 0 };

		io_pause(&s->s_io, IO_PAUSE_OUT);
		stat_increment("smtp.delays", 1);

		/* in case session_respond is called multiple times */
		evtimer_del(&s->s_ev);
		evtimer_set(&s->s_ev, session_respond_delayed, s);
		evtimer_add(&s->s_ev, &tv);
	}
}
示例#5
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
}