summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2009-01-28 21:44:16 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2009-01-28 21:44:16 +0000
commit15057d46a366f67b17e58550f57fcf70a7b67e4b (patch)
tree111fc723cb2d8fa4ac4bd9be586d1bfa83c4719b
parent54659ec20398b406f4b95914d7e644680d1fdefd (diff)
first steps towards better mta code. currently mta uses struct batch to
store a lot of its session related code, but this is just not right and this commit starts making mta code aware of struct session. This will ease the implementation of ssl sessions in mta. while at it, make mta autodetect port to use if it isn't provided in a rule but can be derived from a parameter (i.e: "relay via ssmtp ...").
-rw-r--r--usr.sbin/smtpd/mta.c184
-rw-r--r--usr.sbin/smtpd/parse.y4
-rw-r--r--usr.sbin/smtpd/smtpd.h10
3 files changed, 176 insertions, 22 deletions
diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c
index 27ef4f33ac2..30112447c74 100644
--- a/usr.sbin/smtpd/mta.c
+++ b/usr.sbin/smtpd/mta.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta.c,v 1.15 2009/01/28 13:29:40 gilles Exp $ */
+/* $OpenBSD: mta.c,v 1.16 2009/01/28 21:44:15 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -45,12 +45,13 @@ void mta_setup_events(struct smtpd *);
void mta_disable_events(struct smtpd *);
void mta_timeout(int, short, void *);
void mta_write(int, short, void *);
-int mta_connect(struct batch *);
+int mta_connect(struct session *);
void mta_read_handler(struct bufferevent *, void *);
void mta_write_handler(struct bufferevent *, void *);
void mta_error_handler(struct bufferevent *, short, void *);
int mta_reply_handler(struct bufferevent *, void *);
void mta_batch_update_queue(struct batch *);
+void mta_expand_mxarray(struct session *);
void
mta_sig_handler(int sig, short event, void *p)
@@ -214,17 +215,30 @@ mta_dispatch_runner(int sig, short event, void *p)
switch (imsg.hdr.type) {
case IMSG_BATCH_CREATE: {
+ struct session *s;
struct batch *batchp;
+ /* create a client session */
+ if ((s = calloc(1, sizeof(*s))) == NULL)
+ fatal(NULL);
+ s->s_state = S_INIT;
+ s->s_env = env;
+ s->s_id = queue_generate_id();
+ SPLAY_INSERT(sessiontree, &s->s_env->sc_sessions, s);
+
+ /* create the batch for this session */
batchp = calloc(1, sizeof (struct batch));
if (batchp == NULL)
fatal("mta_dispatch_runner: calloc");
*batchp = *(struct batch *)imsg.data;
+ batchp->session_id = s->s_id;
batchp->mx_off = 0;
batchp->env = env;
batchp->flags = 0;
+ s->batch = batchp;
+
TAILQ_INIT(&batchp->messages);
SPLAY_INSERT(batchtree, &env->batch_queue, batchp);
@@ -249,7 +263,9 @@ mta_dispatch_runner(int sig, short event, void *p)
break;
}
case IMSG_BATCH_CLOSE: {
- struct batch *batchp;
+ struct batch *batchp;
+ struct session *s;
+ struct session key;
batchp = (struct batch *)imsg.data;
batchp = batch_by_id(env, batchp->id);
@@ -258,11 +274,18 @@ mta_dispatch_runner(int sig, short event, void *p)
batchp->flags |= F_BATCH_COMPLETE;
- while (! mta_connect(batchp)) {
- if (batchp->mx_off == batchp->mx_cnt) {
+ log_debug("batch ready, we can initiate a session");
+
+ key.s_id = batchp->session_id;
+
+ s = SPLAY_FIND(sessiontree, &env->sc_sessions, &key);
+ if (s == NULL)
+ fatalx("failed to retrieve mta session.");
+
+ mta_expand_mxarray(s);
+ while (! mta_connect(s))
+ if (s->mx_off == s->mx_cnt)
break;
- }
- }
break;
}
default:
@@ -377,15 +400,16 @@ mta(struct smtpd *env)
/* shamelessly ripped usr.sbin/relayd/check_tcp.c ;) */
int
-mta_connect(struct batch *batchp)
+mta_connect(struct session* sessionp)
{
int s;
int type;
struct linger lng;
struct sockaddr_in ssin;
struct sockaddr_in6 ssin6;
+ struct batch *batchp = sessionp->batch;
- if ((s = socket(batchp->mxarray[batchp->mx_off].ss.ss_family, SOCK_STREAM, 0)) == -1) {
+ if ((s = socket(sessionp->mxarray[sessionp->mx_off].ss.ss_family, SOCK_STREAM, 0)) == -1) {
goto bad;
}
@@ -401,8 +425,8 @@ mta_connect(struct batch *batchp)
session_socket_blockmode(s, BM_NONBLOCK);
- if (batchp->mxarray[batchp->mx_off].ss.ss_family == PF_INET) {
- ssin = *(struct sockaddr_in *)&batchp->mxarray[batchp->mx_off].ss;
+ if (sessionp->mxarray[sessionp->mx_off].ss.ss_family == PF_INET) {
+ ssin = *(struct sockaddr_in *)&sessionp->mxarray[sessionp->mx_off].ss;
if (connect(s, (struct sockaddr *)&ssin, sizeof(struct sockaddr_in)) == -1) {
if (errno != EINPROGRESS) {
goto bad;
@@ -410,8 +434,8 @@ mta_connect(struct batch *batchp)
}
}
- if (batchp->mxarray[batchp->mx_off].ss.ss_family == PF_INET6) {
- ssin6 = *(struct sockaddr_in6 *)&batchp->mxarray[batchp->mx_off].ss;
+ if (sessionp->mxarray[sessionp->mx_off].ss.ss_family == PF_INET6) {
+ ssin6 = *(struct sockaddr_in6 *)&sessionp->mxarray[sessionp->mx_off].ss;
if (connect(s, (struct sockaddr *)&ssin6, sizeof(struct sockaddr_in6)) == -1) {
if (errno != EINPROGRESS) {
goto bad;
@@ -422,13 +446,13 @@ mta_connect(struct batch *batchp)
batchp->tv.tv_sec = SMTPD_CONNECT_TIMEOUT;
batchp->tv.tv_usec = 0;
batchp->peerfd = s;
- event_set(&batchp->ev, s, EV_TIMEOUT|EV_WRITE, mta_write, batchp);
+ event_set(&batchp->ev, s, EV_TIMEOUT|EV_WRITE, mta_write, sessionp);
event_add(&batchp->ev, &batchp->tv);
return 1;
bad:
- batchp->mx_off++;
+ sessionp->mx_off++;
close(s);
return 0;
}
@@ -436,11 +460,12 @@ bad:
void
mta_write(int s, short event, void *arg)
{
- struct batch *batchp = arg;
+ struct session *sessionp = arg;
+ struct batch *batchp = sessionp->batch;
int ret;
if (event == EV_TIMEOUT) {
- batchp->mx_off++;
+ sessionp->mx_off++;
close(s);
if (batchp->bev) {
bufferevent_free(batchp->bev);
@@ -449,8 +474,8 @@ mta_write(int s, short event, void *arg)
strlcpy(batchp->errorline, "connection timed-out.", MAX_LINE_SIZE);
ret = 0;
- while (batchp->mx_off < batchp->mx_cnt &&
- (ret = mta_connect(batchp)) == 0) {
+ while (sessionp->mx_off < sessionp->mx_cnt &&
+ (ret = mta_connect(sessionp)) == 0) {
continue;
}
if (ret)
@@ -811,6 +836,8 @@ mta_batch_update_queue(struct batch *batchp)
{
struct smtpd *env = batchp->env;
struct message *messagep;
+ struct session *sessionp;
+ struct session key;
while ((messagep = TAILQ_FIRST(&batchp->messages)) != NULL) {
@@ -830,6 +857,11 @@ mta_batch_update_queue(struct batch *batchp)
free(messagep);
}
+ key.s_id = batchp->session_id;
+ sessionp = SPLAY_FIND(sessiontree, &env->sc_sessions, &key);
+ if (sessionp == NULL)
+ fatalx("failed to retrieve mta session.");
+
SPLAY_REMOVE(batchtree, &env->batch_queue, batchp);
if (batchp->messagefp)
@@ -842,4 +874,118 @@ mta_batch_update_queue(struct batch *batchp)
close(batchp->peerfd);
free(batchp);
+
+ SPLAY_REMOVE(sessiontree, &env->sc_sessions, &key);
+ free(sessionp->mxarray);
+ free(sessionp);
+}
+
+void
+mta_expand_mxarray(struct session *sessionp)
+{
+ int i;
+ int j;
+ u_int16_t port;
+ struct mxhost mxhost;
+ struct sockaddr_in *ssin;
+ struct sockaddr_in6 *ssin6;
+ struct batch *batchp = sessionp->batch;
+
+ /* First pass, we compute the length of the final mxarray */
+ for (i = 0; i < batchp->mx_cnt; ++i) {
+ mxhost = batchp->mxarray[i];
+
+ if (mxhost.ss.ss_family == AF_INET) {
+ ssin = (struct sockaddr_in *)&mxhost.ss;
+ port = ntohs(ssin->sin_port);
+ }
+ else if (mxhost.ss.ss_family == AF_INET6) {
+ ssin6 = (struct sockaddr_in6 *)&mxhost.ss;
+ port = ntohs(ssin6->sin6_port);
+ }
+
+ if (port) {
+ ++sessionp->mx_cnt;
+ continue;
+ }
+
+ switch (mxhost.flags & F_SSL) {
+ case F_SSL:
+ sessionp->mx_cnt += 2;
+ break;
+ case F_SSMTP:
+ case F_STARTTLS:
+ default:
+ ++sessionp->mx_cnt;
+ }
+ }
+
+ /* Second pass, we actually fill the array */
+ sessionp->mxarray = calloc(sessionp->mx_cnt, sizeof(struct mxhost));
+ if (sessionp->mxarray == NULL)
+ fatal("calloc");
+
+ for (i = j = 0; i < batchp->mx_cnt; ++i) {
+ mxhost = batchp->mxarray[i];
+
+ if (mxhost.ss.ss_family == AF_INET) {
+ ssin = (struct sockaddr_in *)&mxhost.ss;
+ port = ntohs(ssin->sin_port);
+ }
+ else if (mxhost.ss.ss_family == AF_INET6) {
+ ssin6 = (struct sockaddr_in6 *)&mxhost.ss;
+ port = ntohs(ssin6->sin6_port);
+ }
+
+ if (port) {
+ sessionp->mxarray[j++] = mxhost;
+ continue;
+ }
+
+ switch (mxhost.flags & F_SSL) {
+ case F_SSL:
+ if (mxhost.ss.ss_family == AF_INET) {
+ ssin->sin_port = htons(465);
+ mxhost.ss = *(struct sockaddr_storage *)ssin;
+ }
+ else if (mxhost.ss.ss_family == AF_INET6) {
+ ssin6->sin6_port = htons(465);
+ mxhost.ss = *(struct sockaddr_storage *)ssin6;
+ }
+ sessionp->mxarray[j++] = mxhost;
+
+ if (mxhost.ss.ss_family == AF_INET) {
+ ssin->sin_port = htons(25);
+ mxhost.ss = *(struct sockaddr_storage *)ssin;
+ }
+ else if (mxhost.ss.ss_family == AF_INET6) {
+ ssin6->sin6_port = htons(25);
+ mxhost.ss = *(struct sockaddr_storage *)ssin6;
+ }
+ sessionp->mxarray[j++] = mxhost;
+ break;
+ case F_SSMTP:
+ if (mxhost.ss.ss_family == AF_INET) {
+ ssin->sin_port = htons(465);
+ mxhost.ss = *(struct sockaddr_storage *)ssin;
+ }
+ else if (mxhost.ss.ss_family == AF_INET6) {
+ ssin6->sin6_port = htons(465);
+ mxhost.ss = *(struct sockaddr_storage *)ssin6;
+ }
+ sessionp->mxarray[j++] = mxhost;
+ break;
+ case F_STARTTLS:
+ default:
+ if (mxhost.ss.ss_family == AF_INET) {
+ ssin->sin_port = htons(25);
+ mxhost.ss = *(struct sockaddr_storage *)ssin;
+ }
+ else if (mxhost.ss.ss_family == AF_INET6) {
+ ssin6->sin6_port = htons(25);
+ mxhost.ss = *(struct sockaddr_storage *)ssin6;
+ }
+ sessionp->mxarray[j++] = mxhost;
+ }
+ }
}
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index 93addf568cb..785695d5925 100644
--- a/usr.sbin/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.22 2009/01/14 23:36:52 gilles Exp $ */
+/* $OpenBSD: parse.y,v 1.23 2009/01/28 21:44:15 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -688,7 +688,7 @@ action : DELIVER TO MAILDIR STRING {
fatal("hostname too long");
if ($5 == 0)
- rule->r_value.relayhost.port = 25;
+ rule->r_value.relayhost.port = 0;
else
rule->r_value.relayhost.port = $5;
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index fcada9b9aba..e1a68974b8d 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.53 2009/01/28 19:38:46 gilles Exp $ */
+/* $OpenBSD: smtpd.h,v 1.54 2009/01/28 21:44:15 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -478,6 +478,7 @@ struct batch {
SPLAY_ENTRY(batch) b_nodes;
u_int64_t id;
+ u_int64_t session_id;
enum batch_type type;
enum batch_flags flags;
@@ -594,6 +595,13 @@ struct session {
struct message s_msg;
struct session_auth_req s_auth;
+
+ struct mxhost *mxarray;
+ u_int8_t mx_cnt;
+ u_int8_t mx_off;
+
+ struct batch *batch;
+
};
struct smtpd {