summaryrefslogtreecommitdiff
path: root/usr.sbin
diff options
context:
space:
mode:
authorJacek Masiulaniec <jacekm@cvs.openbsd.org>2009-12-12 10:33:12 +0000
committerJacek Masiulaniec <jacekm@cvs.openbsd.org>2009-12-12 10:33:12 +0000
commit173db6f90670e33ca553f47f7b652265aee3a240 (patch)
treeb058a07cd10d9bc3337f633b76ea06e0db0be950 /usr.sbin
parent26ca1705c8c1bcff436db5b16871b448eef40ed9 (diff)
Simplify client_* api, mainly by making fatal conditions result in immediate
fatals instead of passing the error up (kills ~300 lines). Implement sending of the QUIT command which replaces crude close(2). tested by gilles@, todd@
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/smtpd/bounce.c81
-rw-r--r--usr.sbin/smtpd/client.c602
-rw-r--r--usr.sbin/smtpd/client.h46
-rw-r--r--usr.sbin/smtpd/enqueue.c80
-rw-r--r--usr.sbin/smtpd/mta.c168
-rw-r--r--usr.sbin/smtpd/smtpd.h4
6 files changed, 352 insertions, 629 deletions
diff --git a/usr.sbin/smtpd/bounce.c b/usr.sbin/smtpd/bounce.c
index 2293db048d6..52feb25ad5a 100644
--- a/usr.sbin/smtpd/bounce.c
+++ b/usr.sbin/smtpd/bounce.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bounce.c,v 1.12 2009/11/11 10:04:05 chl Exp $ */
+/* $OpenBSD: bounce.c,v 1.13 2009/12/12 10:33:11 jacekm Exp $ */
/*
* Copyright (c) 2009 Gilles Chehade <gilles@openbsd.org>
@@ -39,7 +39,7 @@
struct client_ctx {
struct event ev;
struct message m;
- struct smtp_client *sp;
+ struct smtp_client *pcb;
};
void bounce_event(int, short, void *);
@@ -61,17 +61,14 @@ bounce_session(struct smtpd *env, int fd, struct message *messagep)
/* init smtp session */
if ((cc = calloc(1, sizeof(*cc))) == NULL)
goto fail;
- if ((cc->sp = client_init(fd, env->sc_hostname)) == NULL)
- goto fail;
+ cc->pcb = client_init(fd, env->sc_hostname, 1);
cc->m = *messagep;
- if (client_ssl_optional(cc->sp) < 0)
- goto fail;
+ client_ssl_optional(cc->pcb);
/* assign recipient */
- if (client_rcpt(cc->sp, "%s@%s", messagep->sender.user,
- messagep->sender.domain) < 0)
- goto fail;
+ client_rcpt(cc->pcb, NULL, "%s@%s", messagep->sender.user,
+ messagep->sender.domain);
/* Construct an appropriate reason line. */
reason = messagep->session_errorline;
@@ -80,7 +77,7 @@ bounce_session(struct smtpd *env, int fd, struct message *messagep)
/* create message header */
/* XXX - The Date: header should be added during SMTP pickup. */
- if (client_data_printf(cc->sp,
+ client_data_printf(cc->pcb,
"Subject: Delivery status notification\n"
"From: Mailer Daemon <MAILER-DAEMON@%s>\n"
"To: %s@%s\n"
@@ -101,27 +98,25 @@ bounce_session(struct smtpd *env, int fd, struct message *messagep)
messagep->sender.user, messagep->sender.domain,
time_to_text(time(NULL)),
messagep->recipient.user, messagep->recipient.domain,
- reason) < 0)
- goto fail;
+ reason);
/* append original message */
if ((msgfd = queue_open_message_file(messagep->message_id)) == -1)
goto fail;
- if (client_data_fd(cc->sp, msgfd) < 0)
- goto fail;
+ client_data_fd(cc->pcb, msgfd);
close(msgfd);
msgfd = -1;
/* setup event */
session_socket_blockmode(fd, BM_NONBLOCK);
event_set(&cc->ev, fd, EV_WRITE, bounce_event, cc);
- event_add(&cc->ev, client_timeout(cc->sp));
+ event_add(&cc->ev, &cc->pcb->timeout);
return 1;
fail:
close(msgfd);
- if (cc && cc->sp)
- client_close(cc->sp);
+ if (cc && cc->pcb)
+ client_close(cc->pcb);
free(cc);
return 0;
}
@@ -130,45 +125,29 @@ void
bounce_event(int fd, short event, void *p)
{
struct client_ctx *cc = p;
- char *ep = NULL;
- int error = 0;
- int (*iofunc)(struct smtp_client *);
+ char *ep;
if (event & EV_TIMEOUT) {
- message_set_errormsg(&cc->m, "150 timeout");
- cc->m.status = S_MESSAGE_TEMPFAILURE;
- queue_message_update(&cc->m);
- client_close(cc->sp);
- free(cc);
- return;
+ ep = "150 timeout";
+ goto out;
}
- if (event & EV_READ)
- iofunc = client_read;
- else
- iofunc = client_write;
-
- switch (iofunc(cc->sp)) {
+ switch (client_talk(cc->pcb)) {
case CLIENT_WANT_READ:
- event_set(&cc->ev, fd, EV_READ, bounce_event, cc);
- event_add(&cc->ev, client_timeout(cc->sp));
- return;
+ goto read;
case CLIENT_WANT_WRITE:
- event_set(&cc->ev, fd, EV_WRITE, bounce_event, cc);
- event_add(&cc->ev, client_timeout(cc->sp));
- return;
- case CLIENT_ERROR:
- error = 1;
+ goto write;
case CLIENT_RCPT_FAIL:
+ ep = cc->pcb->reply;
+ break;
case CLIENT_DONE:
+ ep = cc->pcb->status;
break;
+ default:
+ fatalx("bounce_event: unexpected code");
}
- if (error)
- ep = client_strerror(cc->sp);
- else
- ep = client_reply(cc->sp);
-
+out:
if (*ep == '2')
queue_remove_envelope(&cc->m);
else {
@@ -180,6 +159,16 @@ bounce_event(int fd, short event, void *p)
queue_message_update(&cc->m);
}
- client_close(cc->sp);
+ client_close(cc->pcb);
free(cc);
+ return;
+
+read:
+ event_set(&cc->ev, fd, EV_READ, bounce_event, cc);
+ event_add(&cc->ev, &cc->pcb->timeout);
+ return;
+
+write:
+ event_set(&cc->ev, fd, EV_WRITE, bounce_event, cc);
+ event_add(&cc->ev, &cc->pcb->timeout);
}
diff --git a/usr.sbin/smtpd/client.c b/usr.sbin/smtpd/client.c
index 1c64a3dd519..e00bf48b7ab 100644
--- a/usr.sbin/smtpd/client.c
+++ b/usr.sbin/smtpd/client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: client.c,v 1.17 2009/11/17 09:22:19 jacekm Exp $ */
+/* $OpenBSD: client.c,v 1.18 2009/12/12 10:33:11 jacekm Exp $ */
/*
* Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
@@ -38,11 +38,14 @@
#include "imsg.h"
#include "client.h"
+int client_read(struct smtp_client *);
+int client_write(struct smtp_client *);
int client_next_state(struct smtp_client *);
+void client_status(struct smtp_client *, char *, ...);
int client_getln(struct smtp_client *);
-int client_putln(struct smtp_client *, char *, ...);
-int client_data_add(struct smtp_client *, char *, size_t);
+void client_putln(struct smtp_client *, char *, ...);
+void client_data_add(struct smtp_client *, char *, size_t);
#ifndef CLIENT_NO_SSL
int client_ssl_connect(struct smtp_client *);
@@ -54,19 +57,20 @@ int ssl_buf_write(SSL *, struct msgbuf *);
char *buf_getln(struct buf_read *);
int buf_read(int, struct buf_read *);
+void log_debug(const char *, ...); /* XXX */
+void fatal(const char *); /* XXX */
+void fatalx(const char *); /* XXX */
+
/*
- * client_init()
- *
* Initialize SMTP session.
*/
struct smtp_client *
-client_init(int fd, char *ehlo)
+client_init(int fd, char *ehlo, int verbose)
{
struct smtp_client *sp = NULL;
- int rv = -1;
if ((sp = calloc(1, sizeof(*sp))) == NULL)
- goto done;
+ fatal(NULL);
if (ehlo == NULL || *ehlo == '\0') {
char buf[NI_MAXHOST];
struct sockaddr_storage sa;
@@ -74,342 +78,201 @@ client_init(int fd, char *ehlo)
len = sizeof(sa);
if (getsockname(fd, (struct sockaddr *)&sa, &len))
- goto done;
+ fatal("client_init: getsockname");
if (getnameinfo((struct sockaddr *)&sa, len, buf, sizeof(buf),
NULL, 0, NI_NUMERICHOST))
- goto done;
+ fatalx("client_init: getnameinfo");
if (asprintf(&sp->ehlo, "[%s]", buf) == -1)
- goto done;
+ fatal("client_init: asprintf");
} else if ((sp->ehlo = strdup(ehlo)) == NULL)
- goto done;
+ fatal(NULL);
if ((sp->sender = strdup("")) == NULL)
- goto done;
+ fatal(NULL);
+ if (verbose)
+ sp->verbose = stdout;
+ else if ((sp->verbose = fopen("/dev/null", "a")) == NULL)
+ fatal("client_init: fopen");
if ((sp->data = buf_dynamic(0, SIZE_T_MAX)) == NULL)
- goto done;
+ fatal(NULL);
sp->state = CLIENT_INIT;
+ sp->handler = client_write;
+ sp->timeout.tv_sec = 300;
msgbuf_init(&sp->w);
sp->w.fd = fd;
TAILQ_INIT(&sp->recipients);
-#ifndef CLIENT_NO_SSL
+#ifdef CLIENT_NO_SSL
+ sp->exts[CLIENT_EXT_STARTTLS].want = 0;
+ sp->exts[CLIENT_EXT_STARTTLS].must = 0;
+#else
sp->exts[CLIENT_EXT_STARTTLS].want = 1;
sp->exts[CLIENT_EXT_STARTTLS].must = 1;
+#endif
sp->exts[CLIENT_EXT_STARTTLS].state = CLIENT_STARTTLS;
sp->exts[CLIENT_EXT_STARTTLS].name = "STARTTLS";
-#endif
sp->exts[CLIENT_EXT_AUTH].want = 0;
sp->exts[CLIENT_EXT_AUTH].must = 0;
sp->exts[CLIENT_EXT_AUTH].state = CLIENT_AUTH;
sp->exts[CLIENT_EXT_AUTH].name = "AUTH";
- rv = 0;
-done:
- if (rv && sp != NULL) {
- free(sp->ehlo);
- free(sp->sender);
- if (sp->data)
- buf_free(sp->data);
- free(sp);
- sp = NULL;
- }
return (sp);
}
/*
- * client_verbose()
- *
- * Enable logging of SMTP commands.
- */
-void
-client_verbose(struct smtp_client *sp, FILE *fp)
-{
- sp->verbose = fp;
-}
-
-/*
- * client_ssl_smtps()
- *
* Request that connection be secured using SSL from the start.
*/
-int
+void
client_ssl_smtps(struct smtp_client *sp)
{
- /* check if too late */
- if (sp->state != CLIENT_INIT)
- return (-1);
-
-#ifndef CLIENT_NO_SSL
sp->state = CLIENT_SSL_INIT;
sp->exts[CLIENT_EXT_STARTTLS].want = 0;
sp->exts[CLIENT_EXT_STARTTLS].must = 0;
-
- return (0);
-#else
- return (-1);
-#endif
}
/*
- * client_ssl_optional()
- *
* Allow session to progress in plaintext if STARTTLS fails.
*/
-int
+void
client_ssl_optional(struct smtp_client *sp)
{
- if (sp->state != CLIENT_INIT)
- return (-1);
-
-#ifndef CLIENT_NO_SSL
sp->exts[CLIENT_EXT_STARTTLS].must = 0;
-
- return (0);
-#else
- return (-1);
-#endif
}
/*
- * client_certificate()
- *
* Use the provided certificate during SSL handshake.
*/
-int
+void
client_certificate(struct smtp_client *sp, char *cert, size_t certsz,
char *key, size_t keysz)
{
- int rv = -1;
-
- if (sp->state != CLIENT_INIT && sp->state != CLIENT_SSL_INIT)
- goto done;
-
if ((sp->auth.cert = malloc(certsz)) == NULL)
- goto done;
+ fatal(NULL);
if ((sp->auth.key = malloc(keysz)) == NULL)
- goto done;
+ fatal(NULL);
memcpy(sp->auth.cert, cert, certsz);
memcpy(sp->auth.key, key, keysz);
sp->auth.certsz = certsz;
sp->auth.keysz = keysz;
-
- rv = 0;
-done:
- if (rv) {
- free(sp->auth.cert);
- free(sp->auth.key);
- sp->auth.cert = NULL;
- sp->auth.certsz = 0;
- sp->auth.key = NULL;
- sp->auth.keysz = 0;
- }
- return (rv);
}
/*
- * client_auth()
- *
* Use the AUTH extension.
*/
-int
+void
client_auth(struct smtp_client *sp, char *secret)
{
- int rv = -1;
-
- if (sp->state != CLIENT_INIT && sp->state != CLIENT_SSL_INIT)
- goto done;
-
if ((sp->auth.plain = strdup(secret)) == NULL)
- goto done;
+ fatal(NULL);
sp->exts[CLIENT_EXT_AUTH].want = 1;
sp->exts[CLIENT_EXT_AUTH].must = 1;
-
- rv = 0;
-done:
- if (rv) {
- free(sp->auth.plain);
- sp->auth.plain = NULL;
- }
- return (rv);
}
/*
- * client_sender()
- *
* Set envelope sender. If not called, the sender is assumed to be "<>".
*/
-int
+void
client_sender(struct smtp_client *sp, char *fmt, ...)
{
va_list ap;
- char *mbox = NULL;
- int rv = -1;
-
- va_start(ap, fmt);
-
- /* check if too late */
- switch (sp->state) {
- case CLIENT_INIT:
- case CLIENT_SSL_INIT:
- case CLIENT_EHLO:
- case CLIENT_HELO:
- break;
- default:
- goto done;
- }
- if (vasprintf(&mbox, fmt, ap) == -1)
- goto done;
free(sp->sender);
- sp->sender = mbox;
- rv = 0;
-done:
+ va_start(ap, fmt);
+ if (vasprintf(&sp->sender, fmt, ap) == -1)
+ fatal("client_sender: vasprintf");
va_end(ap);
- if (rv)
- free(mbox);
- return (rv);
}
/*
- * client_rcpt()
- *
* Add mail recipient.
*/
-int
-client_rcpt(struct smtp_client *sp, char *fmt, ...)
+void
+client_rcpt(struct smtp_client *sp, void *p, char *fmt, ...)
{
va_list ap;
struct rcpt *rp = NULL;
- int rv = -1;
-
- va_start(ap, fmt);
-
- /* check if too late */
- switch (sp->state) {
- case CLIENT_INIT:
- case CLIENT_SSL_INIT:
- case CLIENT_EHLO:
- case CLIENT_HELO:
- case CLIENT_MAILFROM:
- break;
- default:
- goto done;
- }
if ((rp = calloc(1, sizeof(*rp))) == NULL)
- goto done;
- if (vasprintf(&rp->mbox, fmt, ap) == -1)
- goto done;
- TAILQ_INSERT_TAIL(&sp->recipients, rp, entry);
+ fatal(NULL);
+ rp->p = p;
- rv = 0;
-done:
+ va_start(ap, fmt);
+ if (vasprintf(&rp->mbox, fmt, ap) == -1)
+ fatal("client_rcpt: vasprintf");
va_end(ap);
- if (rv) {
- if (rp)
- free(rp->mbox);
- free(rp);
- }
- return (rv);
+
+ TAILQ_INSERT_TAIL(&sp->recipients, rp, entry);
+ sp->rcpt = TAILQ_FIRST(&sp->recipients);
}
/*
- * client_data_fd()
- *
* Append file referenced by fd to the data buffer.
*/
-int
+void
client_data_fd(struct smtp_client *sp, int fd)
{
struct stat sb;
- char *map = NULL;
- int rv = -1;
+ char *map;
if (fstat(fd, &sb) == -1)
- goto done;
+ fatal("client_data_fd: fstat");
if ((size_t)sb.st_size > SIZE_T_MAX)
- goto done;
+ fatalx("client_data_fd: file too large");
if (!S_ISREG(sb.st_mode))
- goto done;
+ fatalx("client_data_fd: non-regular file");
map = mmap(NULL, sb.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd,
(off_t)0);
if (map == MAP_FAILED)
- goto done;
+ fatal("client_data_fd: mmap failed");
madvise(map, sb.st_size, MADV_SEQUENTIAL);
-
- if (client_data_add(sp, map, sb.st_size) < 0)
- goto done;
-
- rv = 0;
-done:
- if (map)
- munmap(map, sb.st_size);
- return (rv);
+ client_data_add(sp, map, sb.st_size);
+ munmap(map, sb.st_size);
}
/*
- * client_data_printf()
- *
* Append string to the data buffer.
*/
-int
+void
client_data_printf(struct smtp_client *sp, char *fmt, ...)
{
va_list ap;
char *p = NULL;
- int len, rv = -1;
+ int len;
va_start(ap, fmt);
if ((len = vasprintf(&p, fmt, ap)) == -1)
- goto done;
-
- if (client_data_add(sp, p, len) < 0)
- goto done;
-
- rv = 0;
-done:
+ fatal("client_data_printf: vasprintf");
va_end(ap);
+ client_data_add(sp, p, len);
free(p);
- return (rv);
}
/*
- * client_udata_set()
- *
- * Associate user pointer with the most recently recorded recipient.
+ * Routine called by the user to progress the session.
*/
-void
-client_udata_set(struct smtp_client *sp, void *p)
+int
+client_talk(struct smtp_client *sp)
{
- struct rcpt *lastrcpt;
+ int ret;
- lastrcpt = TAILQ_LAST(&sp->recipients, rlist);
- lastrcpt->udata = p;
-}
+ ret = sp->handler(sp);
-/*
- * client_udata_get()
- *
- * Return user pointer associated with most recently sent recipient.
- */
-void *
-client_udata_get(struct smtp_client *sp)
-{
- return (sp->rcptsent->udata);
+ if (ret == CLIENT_WANT_READ)
+ sp->handler = client_read;
+ else if (ret == CLIENT_WANT_WRITE)
+ sp->handler = client_write;
+
+ return (ret);
}
/*
- * client_read()
- *
* Handler to be called when socket becomes readable.
*/
int
client_read(struct smtp_client *sp)
{
- int rv = CLIENT_ERROR;
-
#ifndef CLIENT_NO_SSL
if (sp->state == CLIENT_SSL_CONNECT)
return client_ssl_connect(sp);
@@ -429,9 +292,8 @@ client_read(struct smtp_client *sp)
return (CLIENT_WANT_WRITE);
default:
- strlcpy(sp->ebuf, "130 ssl_buf_read error",
- sizeof(sp->ebuf));
- return (CLIENT_ERROR);
+ client_status(sp, "130 ssl_buf_read error");
+ return (CLIENT_DONE);
}
#else
if (0) {
@@ -440,18 +302,18 @@ client_read(struct smtp_client *sp)
errno = 0;
if (buf_read(sp->w.fd, &sp->r) == -1) {
if (errno)
- snprintf(sp->ebuf, sizeof(sp->ebuf),
- "130 buf_read: %s", strerror(errno));
+ client_status(sp, "130 buf_read: %s",
+ strerror(errno));
else
- snprintf(sp->ebuf, sizeof(sp->ebuf),
- "130 buf_read: connection closed");
- return (CLIENT_ERROR);
+ client_status(sp, "130 buf_read: "
+ "connection closed");
+ return (CLIENT_DONE);
}
}
/* get server reply */
if (client_getln(sp) < 0)
- return (CLIENT_ERROR);
+ goto quit2;
if (*sp->reply == '\0')
return (CLIENT_WANT_READ);
@@ -466,14 +328,14 @@ client_read(struct smtp_client *sp)
sp->state != CLIENT_RCPTTO &&
sp->state != CLIENT_DATA &&
sp->state != CLIENT_DATA_BODY) {
- memcpy(sp->reply, "190", 3);
- goto done;
+ client_status(sp, "190 untimely 5yz reply: %s", sp->reply);
+ goto quit2;
}
switch (sp->state) {
case CLIENT_INIT:
if (*sp->reply != '2')
- goto done;
+ goto quit;
else
sp->state = CLIENT_EHLO;
break;
@@ -482,19 +344,19 @@ client_read(struct smtp_client *sp)
if (*sp->reply != '2') {
if (sp->exts[CLIENT_EXT_STARTTLS].must ||
sp->exts[CLIENT_EXT_AUTH].must)
- goto done;
+ goto quit;
else
sp->state = CLIENT_HELO;
break;
}
if ((sp->state = client_next_state(sp)) == 0)
- return (CLIENT_ERROR);
+ goto quit2;
break;
case CLIENT_HELO:
if (*sp->reply != '2')
- goto done;
+ goto quit;
else
sp->state = CLIENT_MAILFROM;
break;
@@ -503,7 +365,7 @@ client_read(struct smtp_client *sp)
if (*sp->reply != '2') {
sp->exts[CLIENT_EXT_STARTTLS].fail = 1;
if ((sp->state = client_next_state(sp)) == 0)
- return (CLIENT_ERROR);
+ goto quit2;
} else
sp->state = CLIENT_SSL_INIT;
break;
@@ -515,56 +377,59 @@ client_read(struct smtp_client *sp)
sp->exts[CLIENT_EXT_AUTH].done = 1;
if ((sp->state = client_next_state(sp)) == 0)
- return (CLIENT_ERROR);
+ goto quit2;
break;
case CLIENT_MAILFROM:
if (*sp->reply != '2')
- goto done;
+ goto quit;
else
sp->state = CLIENT_RCPTTO;
break;
case CLIENT_RCPTTO:
- if (*sp->reply != '2') {
- rv = CLIENT_RCPT_FAIL;
- goto done;
- } else if (TAILQ_NEXT(sp->rcptsent, entry) == NULL)
- sp->state = CLIENT_DATA;
+ if (*sp->reply == '2') {
+ sp->rcptokay++;
+ sp->rcpt = TAILQ_NEXT(sp->rcpt, entry);
+ } else {
+ sp->rcptfail = sp->rcpt;
+ sp->rcpt = TAILQ_NEXT(sp->rcpt, entry);
+ return (CLIENT_RCPT_FAIL);
+ }
break;
case CLIENT_DATA:
if (*sp->reply != '3')
- goto done;
+ goto quit;
else
sp->state = CLIENT_DATA_BODY;
break;
case CLIENT_DATA_BODY:
- if (*sp->reply == '2')
- rv = CLIENT_DONE;
- goto done;
+ goto quit;
+
+ case CLIENT_QUIT:
+ return (CLIENT_WANT_READ);
default:
- abort();
+ fatalx("client_read: unexpected state");
}
- rv = CLIENT_WANT_WRITE;
-done:
- if (rv == CLIENT_ERROR)
- strlcpy(sp->ebuf, sp->reply, sizeof(sp->ebuf));
- return (rv);
+ return (CLIENT_WANT_WRITE);
+
+quit:
+ client_status(sp, "%s", sp->reply);
+quit2:
+ sp->state = CLIENT_QUIT;
+ return (CLIENT_WANT_WRITE);
}
/*
- * client_write()
- *
* Handler to be called when socket becomes writable.
*/
int
client_write(struct smtp_client *sp)
{
- int rv = CLIENT_ERROR;
#ifndef CLIENT_NO_SSL
if (sp->state == CLIENT_SSL_CONNECT)
@@ -578,15 +443,13 @@ client_write(struct smtp_client *sp)
switch (sp->state) {
#ifndef CLIENT_NO_SSL
case CLIENT_SSL_INIT:
- if (sp->verbose)
- fprintf(sp->verbose, "client: ssl handshake started\n");
+ log_debug("client: ssl handshake started");
sp->ssl_state = ssl_client_init(sp->w.fd,
sp->auth.cert, sp->auth.certsz,
sp->auth.key, sp->auth.keysz);
if (sp->ssl_state == NULL) {
- strlcpy(sp->ebuf, "130 SSL init failed",
- sizeof(sp->ebuf));
- return (CLIENT_ERROR);
+ client_status(sp, "130 SSL init failed");
+ return (CLIENT_DONE);
} else {
sp->state = CLIENT_SSL_CONNECT;
return (CLIENT_WANT_WRITE);
@@ -606,56 +469,53 @@ client_write(struct smtp_client *sp)
sp->exts[CLIENT_EXT_AUTH].have = 0;
sp->exts[CLIENT_EXT_AUTH].fail = 0;
- if (client_putln(sp, "%sLO %s",
- sp->state == CLIENT_EHLO ? "EH" : "HE", sp->ehlo) < 0)
- goto done;
+ client_putln(sp, "%s %s", sp->state == CLIENT_EHLO ? "EHLO" :
+ "HELO", sp->ehlo);
break;
case CLIENT_AUTH:
- if (client_putln(sp, "AUTH PLAIN %s", sp->auth.plain) < 0)
- goto done;
+ client_putln(sp, "AUTH PLAIN %s", sp->auth.plain);
break;
case CLIENT_STARTTLS:
- if (client_putln(sp, "STARTTLS") < 0)
- goto done;
+ client_putln(sp, "STARTTLS");
break;
case CLIENT_MAILFROM:
- if (client_putln(sp, "MAIL FROM:<%s>", sp->sender) < 0)
- goto done;
+ client_putln(sp, "MAIL FROM:<%s>", sp->sender);
break;
case CLIENT_RCPTTO:
- if (sp->rcptsent == NULL)
- sp->rcptsent = TAILQ_FIRST(&sp->recipients);
- else
- sp->rcptsent = TAILQ_NEXT(sp->rcptsent, entry);
-
- if (sp->rcptsent == NULL)
- goto done;
-
- if (client_putln(sp, "RCPT TO:<%s>", sp->rcptsent->mbox) < 0)
- goto done;
+ if (sp->rcpt == NULL) {
+ if (sp->rcptokay > 0)
+ sp->state = CLIENT_DATA;
+ else
+ sp->state = CLIENT_QUIT;
+ return (CLIENT_WANT_WRITE);
+ }
+ client_putln(sp, "RCPT TO:<%s>", sp->rcpt->mbox);
break;
case CLIENT_DATA:
- if (client_putln(sp, "DATA") < 0)
- goto done;
+ sp->timeout.tv_sec = 120;
+ client_putln(sp, "DATA");
break;
case CLIENT_DATA_BODY:
- if (buf_add(sp->data, ".\r\n", 3) < 0) {
- strlcpy(sp->ebuf, "190 buf_add error",
- sizeof(sp->ebuf));
- goto done;
- }
+ sp->timeout.tv_sec = 180;
+ if (buf_add(sp->data, ".\r\n", 3) < 0)
+ fatal("client_write: buf_add failed");
buf_close(&sp->w, sp->data);
sp->data = NULL;
break;
+ case CLIENT_QUIT:
+ sp->timeout.tv_sec = 300;
+ client_putln(sp, "QUIT");
+ break;
+
default:
- abort();
+ fatalx("client_write: unexpected state");
}
write:
@@ -666,38 +526,36 @@ write:
break;
case SSL_ERROR_WANT_READ:
- rv = CLIENT_WANT_READ;
- goto done;
+ return (CLIENT_WANT_READ);
case SSL_ERROR_WANT_WRITE:
- rv = CLIENT_WANT_WRITE;
- goto done;
+ return (CLIENT_WANT_WRITE);
default:
- strlcpy(sp->ebuf, "130 ssl_buf_write error",
- sizeof(sp->ebuf));
- goto done;
+ client_status(sp, "130 ssl_buf_write error");
+ return (CLIENT_DONE);
}
#else
if (0) {
#endif
} else {
if (buf_write(&sp->w) < 0) {
- strlcpy(sp->ebuf, "130 buf_write error",
- sizeof(sp->ebuf));
- goto done;
+ client_status(sp, "130 buf_write error");
+ return (CLIENT_DONE);
}
}
- rv = sp->w.queued ? CLIENT_WANT_WRITE : CLIENT_WANT_READ;
-done:
- return (rv);
+ /*
+ * Extend timeout after having sent final "." character.
+ */
+ if (sp->state == CLIENT_DATA_BODY && sp->w.queued == 0)
+ sp->timeout.tv_sec = 600;
+
+ return (sp->w.queued ? CLIENT_WANT_WRITE : CLIENT_WANT_READ);
}
#ifndef CLIENT_NO_SSL
/*
- * client_ssl_connect()
- *
* Progress SSL handshake.
*/
int
@@ -708,37 +566,31 @@ client_ssl_connect(struct smtp_client *sp)
ret = SSL_connect(sp->ssl_state);
switch (SSL_get_error(sp->ssl_state, ret)) {
- case SSL_ERROR_NONE:
- break;
-
case SSL_ERROR_WANT_READ:
return (CLIENT_WANT_READ);
case SSL_ERROR_WANT_WRITE:
return (CLIENT_WANT_WRITE);
+ case SSL_ERROR_NONE:
+ break;
+
default:
- if (sp->verbose)
- fprintf(sp->verbose, "client: ssl handshake failed\n");
+ log_debug("client: ssl handshake failed");
if (sp->exts[CLIENT_EXT_STARTTLS].want) {
sp->exts[CLIENT_EXT_STARTTLS].fail = 1;
-
SSL_free(sp->ssl_state);
sp->ssl_state = NULL;
-
- if ((sp->state = client_next_state(sp)) == 0)
- return (CLIENT_ERROR);
- else
+ if ((sp->state = client_next_state(sp)) != 0)
return (CLIENT_WANT_WRITE);
- } else {
- strlcpy(sp->ebuf, "130 SSL_connect error", sizeof(sp->ebuf));
- return (CLIENT_ERROR);
- }
+ } else
+ client_status(sp, "130 SSL_connect error");
+
+ return (CLIENT_DONE);
}
- if (sp->verbose)
- fprintf(sp->verbose, "client: ssl handshake completed\n");
+ log_debug("client: ssl handshake completed");
if (sp->exts[CLIENT_EXT_STARTTLS].want)
sp->state = CLIENT_EHLO;
@@ -752,33 +604,6 @@ client_ssl_connect(struct smtp_client *sp)
#endif
/*
- * client_strerror()
- *
- * Access error string explaining most recent client_{read,write} failure.
- */
-char *
-client_strerror(struct smtp_client *sp)
-{
- if (sp->ebuf[0] == '\0')
- return (NULL);
- else
- return (sp->ebuf);
-}
-
-/*
- * client_reply()
- *
- * Access string containing most recent server reply.
- */
-char *
-client_reply(struct smtp_client *sp)
-{
- return (sp->reply);
-}
-
-/*
- * client_close()
- *
* Deinitialization routine.
*/
void
@@ -808,8 +633,6 @@ client_close(struct smtp_client *sp)
}
/*
- * client_next_state()
- *
* Decide if any extensions need to be requested before proceeding to
* the MAIL FROM command.
*/
@@ -826,8 +649,7 @@ client_next_state(struct smtp_client *sp)
if (e->have && !e->fail)
return (e->state);
else if (e->must) {
- snprintf(sp->ebuf, sizeof(sp->ebuf),
- "%s %s", e->name,
+ client_status(sp, "600 %s %s", e->name,
e->fail ? "failed" : "not available");
return (0);
}
@@ -838,37 +660,25 @@ client_next_state(struct smtp_client *sp)
}
/*
- * client_timeout()
- *
- * Return a timeout that applies to the current session state.
+ * Update status field which the caller uses to check if any errors were
+ * encountered.
*/
-struct timeval *
-client_timeout(struct smtp_client *sp)
+void
+client_status(struct smtp_client *sp, char *fmt, ...)
{
- switch (sp->state) {
- case CLIENT_DATA:
- sp->timeout.tv_sec = 120;
- break;
-
- case CLIENT_DATA_BODY:
- if (sp->w.queued)
- sp->timeout.tv_sec = 180;
- else
- sp->timeout.tv_sec = 600;
- break;
+ va_list ap;
- default:
- sp->timeout.tv_sec = 300;
- }
-
- sp->timeout.tv_usec = 0;
+ /* Don't record errors that occurred at QUIT. */
+ if (sp->state == CLIENT_QUIT)
+ return;
- return (&sp->timeout);
+ va_start(ap, fmt);
+ if (vsnprintf(sp->status, sizeof(sp->status), fmt, ap) == -1)
+ fatal("client_status: vnprintf");
+ va_end(ap);
}
/*
- * client_getln()
- *
* Read and validate next line from the input buffer.
*/
int
@@ -890,8 +700,7 @@ client_getln(struct smtp_client *sp)
goto done;
}
- if (sp->verbose)
- fprintf(sp->verbose, "<<< %s\n", ln);
+ fprintf(sp->verbose, "<<< %s\n", ln);
/* 3-char replies are invalid on their own, append space */
if (strlen(ln) == 3) {
@@ -941,96 +750,65 @@ client_getln(struct smtp_client *sp)
rv = 0;
done:
if (rv)
- strlcpy(sp->ebuf, cause, sizeof(sp->ebuf));
+ client_status(sp, cause);
free(ln);
return (rv);
}
/*
- * client_putln()
- *
* Add a line to the output buffer.
*/
-int
+void
client_putln(struct smtp_client *sp, char *fmt, ...)
{
struct buf *cmd = NULL;
char *p = NULL;
- int len, rv = -1;
+ int len;
va_list ap;
va_start(ap, fmt);
if ((len = vasprintf(&p, fmt, ap)) == -1)
- goto done;
+ fatal("client_putln: vasprintf");
+ va_end(ap);
- if (sp->verbose)
- fprintf(sp->verbose, ">>> %s\n", p);
+ fprintf(sp->verbose, ">>> %s\n", p);
if ((cmd = buf_open(len + 2)) == NULL)
- goto done;
+ fatal(NULL);
if (buf_add(cmd, p, len) < 0)
- goto done;
+ fatal(NULL);
if (buf_add(cmd, "\r\n", 2) < 0)
- goto done;
+ fatal(NULL);
buf_close(&sp->w, cmd);
- cmd = NULL;
-
- rv = 0;
-done:
- if (rv)
- snprintf(sp->ebuf, sizeof(sp->ebuf), "190 %s", strerror(errno));
- va_end(ap);
free(p);
- if (cmd)
- buf_free(cmd);
- return (rv);
}
/*
- * client_data_add()
- *
* Append buffer to the data buffer, performing necessary transformations.
*/
-int
+void
client_data_add(struct smtp_client *sp, char *buf, size_t len)
{
char *ln;
- /* check if too late */
- switch (sp->state) {
- case CLIENT_INIT:
- case CLIENT_SSL_INIT:
- case CLIENT_EHLO:
- case CLIENT_HELO:
- case CLIENT_MAILFROM:
- case CLIENT_RCPTTO:
- break;
- default:
- return (-1);
- }
-
/* must end with a newline */
if (len == 0 || buf[len - 1] != '\n')
- return (-1);
+ fatalx("client_data_add: bad buffer");
buf[len - 1] = '\0';
/* split into lines, deal with dot escaping etc. */
while ((ln = strsep(&buf, "\n"))) {
if (*ln == '.' && buf_add(sp->data, ".", 1) < 0)
- return (-1);
+ fatal(NULL);
if (buf_add(sp->data, ln, strlen(ln)) < 0)
- return (-1);
+ fatal(NULL);
if (buf_add(sp->data, "\r\n", 2) < 0)
- return (-1);
+ fatal(NULL);
}
-
- return (0);
}
/*
- * buf_getln()
- *
* Read a full line from the read buffer.
*/
char *
@@ -1063,8 +841,6 @@ buf_getln(struct buf_read *r)
}
/*
- * buf_read()
- *
* I/O routine for reading UNIX socket.
*/
int
diff --git a/usr.sbin/smtpd/client.h b/usr.sbin/smtpd/client.h
index 14fefbe2e5f..d038ab0acc4 100644
--- a/usr.sbin/smtpd/client.h
+++ b/usr.sbin/smtpd/client.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: client.h,v 1.4 2009/09/22 12:24:06 jacekm Exp $ */
+/* $OpenBSD: client.h,v 1.5 2009/12/12 10:33:11 jacekm Exp $ */
/*
* Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
@@ -24,10 +24,9 @@ struct smtp_client;
/* return codes for io routines */
#define CLIENT_DONE 0 /* finished ok */
-#define CLIENT_ERROR -1 /* generic error */
-#define CLIENT_WANT_READ -2 /* need more data */
-#define CLIENT_WANT_WRITE -3 /* have to send sth */
-#define CLIENT_RCPT_FAIL -4 /* recipient refused */
+#define CLIENT_WANT_READ -1 /* need more data */
+#define CLIENT_WANT_WRITE -2 /* have to send sth */
+#define CLIENT_RCPT_FAIL -3 /* recipient refused */
/* client states */
#define CLIENT_SSL_INIT 0x1
@@ -41,6 +40,7 @@ struct smtp_client;
#define CLIENT_RCPTTO 0x9
#define CLIENT_DATA 0xa
#define CLIENT_DATA_BODY 0xb
+#define CLIENT_QUIT 0xc
/* smtp extensions */
#define CLIENT_EXT_STARTTLS 0
@@ -50,7 +50,7 @@ struct smtp_client;
struct rcpt {
TAILQ_ENTRY(rcpt) entry;
char *mbox;
- void *udata;
+ void *p;
};
struct client_auth {
@@ -76,35 +76,31 @@ struct smtp_client {
char *ehlo;
char *sender;
TAILQ_HEAD(rlist,rcpt) recipients;
- struct rcpt *rcptsent;
+ struct rcpt *rcpt;
+ struct rcpt *rcptfail;
+ size_t rcptokay;
struct buf_read r;
struct msgbuf w;
struct buf *data;
struct client_ext exts[CLIENT_EXT_MAX];
+ int (*handler)(struct smtp_client *);
void *ssl_state;
struct client_auth auth;
struct timeval timeout;
char reply[1024];
- char ebuf[1024];
+ char status[1024];
FILE *verbose;
};
-struct smtp_client *client_init(int, char *);
-struct timeval *client_timeout(struct smtp_client *);
-void client_verbose(struct smtp_client *, FILE *);
-int client_ssl_smtps(struct smtp_client *);
-int client_ssl_optional(struct smtp_client *);
-int client_certificate(struct smtp_client *, char *,
+struct smtp_client *client_init(int, char *, int);
+void client_ssl_smtps(struct smtp_client *);
+void client_ssl_optional(struct smtp_client *);
+void client_certificate(struct smtp_client *, char *,
size_t, char *, size_t);
-int client_auth(struct smtp_client *, char *);
-int client_sender(struct smtp_client *, char *, ...);
-int client_rcpt(struct smtp_client *, char *, ...);
-void client_udata_set(struct smtp_client *, void *);
-void *client_udata_get(struct smtp_client *);
-int client_data_fd(struct smtp_client *, int);
-int client_data_printf(struct smtp_client *, char *, ...);
-int client_read(struct smtp_client *);
-int client_write(struct smtp_client *);
+void client_auth(struct smtp_client *, char *);
+void client_sender(struct smtp_client *, char *, ...);
+void client_rcpt(struct smtp_client *, void *, char *, ...);
+void client_data_fd(struct smtp_client *, int);
+void client_data_printf(struct smtp_client *, char *, ...);
+int client_talk(struct smtp_client *);
void client_close(struct smtp_client *);
-char *client_reply(struct smtp_client *);
-char *client_strerror(struct smtp_client *);
diff --git a/usr.sbin/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c
index 8adef042444..f5f6c949f95 100644
--- a/usr.sbin/smtpd/enqueue.c
+++ b/usr.sbin/smtpd/enqueue.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: enqueue.c,v 1.27 2009/12/12 10:14:07 jacekm Exp $ */
+/* $OpenBSD: enqueue.c,v 1.28 2009/12/12 10:33:11 jacekm Exp $ */
/*
* Copyright (c) 2005 Henning Brauer <henning@bulabula.org>
@@ -78,7 +78,6 @@ struct {
};
#define SMTP_LINELEN 1000
-#define SMTP_TIMEOUT 120
#define TIMEOUTMSG "Timeout\n"
#define WSP(c) (c == ' ' || c == '\t')
@@ -122,10 +121,10 @@ sighdlr(int sig)
int
enqueue(int argc, char *argv[])
{
- int i, ch, tflag = 0, noheader, ret;
+ int i, ch, tflag = 0, noheader;
char *fake_from = NULL;
struct passwd *pw;
- struct smtp_client *sp;
+ struct smtp_client *pcb;
struct buf *body;
bzero(&msg, sizeof(msg));
@@ -186,15 +185,12 @@ enqueue(int argc, char *argv[])
}
signal(SIGALRM, sighdlr);
- alarm(SMTP_TIMEOUT);
+ alarm(300);
msg.fd = open_connection();
/* init session */
- if ((sp = client_init(msg.fd, "localhost")) == NULL)
- err(1, "client_init failed");
- if (verbose)
- client_verbose(sp, stdout);
+ pcb = client_init(msg.fd, "localhost", verbose);
/* parse message */
if ((body = buf_dynamic(0, SIZE_T_MAX)) < 0)
@@ -202,67 +198,59 @@ enqueue(int argc, char *argv[])
noheader = parse_message(stdin, fake_from == NULL, tflag, body);
/* set envelope from */
- if (client_sender(sp, "%s", msg.from) < 0)
- err(1, "client_sender failed");
+ client_sender(pcb, "%s", msg.from);
/* add recipients */
if (msg.rcpt_cnt == 0)
errx(1, "no recipients");
for (i = 0; i < msg.rcpt_cnt; i++)
- if (client_rcpt(sp, "%s", msg.rcpts[i]) < 0)
- err(1, "client_rcpt failed");
+ client_rcpt(pcb, "%s", msg.rcpts[i]);
/* add From */
- if (!msg.saw_from) {
- if (msg.fromname != NULL) {
- if (client_data_printf(sp,
- "From: %s <%s>\n", msg.fromname, msg.from) < 0)
- err(1, "client_data_printf failed");
- } else
- if (client_data_printf(sp,
- "From: %s\n", msg.from) < 0)
- err(1, "client_data_printf failed");
- }
+ if (!msg.saw_from)
+ client_data_printf(pcb, "From: %s%s<%s>\n",
+ msg.fromname ? msg.fromname : "",
+ msg.fromname ? " " : "",
+ msg.from);
/* add Date */
if (!msg.saw_date)
- if (client_data_printf(sp,
- "Date: %s\n", time_to_text(timestamp)) < 0)
- err(1, "client_data_printf failed");
+ client_data_printf(pcb, "Date: %s\n", time_to_text(timestamp));
/* add Message-Id */
if (!msg.saw_msgid)
- if (client_data_printf(sp,
- "Message-Id: <%llu.enqueue@%s>\n",
- generate_uid(), host) < 0)
- err(1, "client_data_printf failed");
+ client_data_printf(pcb, "Message-Id: <%llu.enqueue@%s>\n",
+ generate_uid(), host);
/* add separating newline */
if (noheader)
- if (client_data_printf(sp, "\n") < 0)
- err(1, "client_data_printf failed");
+ client_data_printf(pcb, "\n");
- if (client_data_printf(sp, "%.*s", buf_size(body), body->buf) < 0)
- err(1, "client_data_printf failed");
+ client_data_printf(pcb, "%.*s", buf_size(body), body->buf);
buf_free(body);
/* run the protocol engine */
for (;;) {
- while ((ret = client_read(sp)) == CLIENT_WANT_READ)
- ;
- if (ret == CLIENT_ERROR)
- errx(1, "read error: %s", client_strerror(sp));
- if (ret == CLIENT_RCPT_FAIL)
- errx(1, "recipient refused: %s", client_reply(sp));
- if (ret == CLIENT_DONE)
+ alarm(pcb->timeout.tv_sec);
+
+ switch (client_talk(pcb)) {
+ case CLIENT_WANT_READ:
+ case CLIENT_WANT_WRITE:
+ continue;
+ case CLIENT_RCPT_FAIL:
+ errx(1, "%s", pcb->reply);
+ case CLIENT_DONE:
break;
- while ((ret = client_write(sp)) == CLIENT_WANT_WRITE)
- ;
- if (ret == CLIENT_ERROR)
- errx(1, "write error: %s", client_strerror(sp));
+ default:
+ errx(1, "client_talk: unexpected code");
+ }
+
+ if (pcb->status[0] != '2')
+ errx(1, "%s", pcb->status);
+ break;
}
- client_close(sp);
+ client_close(pcb);
close(msg.fd);
exit (0);
diff --git a/usr.sbin/smtpd/mta.c b/usr.sbin/smtpd/mta.c
index 216d1ab1c7c..99ecc7ccd3f 100644
--- a/usr.sbin/smtpd/mta.c
+++ b/usr.sbin/smtpd/mta.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta.c,v 1.79 2009/12/10 15:02:30 jacekm Exp $ */
+/* $OpenBSD: mta.c,v 1.80 2009/12/12 10:33:11 jacekm Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -56,10 +56,10 @@ void mta_event(int, short, void *);
void mta_status(struct mta_session *, const char *, ...);
void mta_message_status(struct message *, char *);
+void mta_message_log(struct mta_session *, struct message *);
void mta_message_done(struct mta_session *, struct message *);
void mta_connect_done(int, short, void *);
void mta_request_datafd(struct mta_session *);
-size_t mta_todo(struct mta_session *);
void
mta_sig_handler(int sig, short event, void *p)
@@ -537,6 +537,7 @@ mta_enter_state(struct mta_session *s, int newstate, void *p)
struct mta_relay *relay;
struct sockaddr *sa;
struct message *m;
+ struct smtp_client *pcb;
int max_reuse;
s->state = newstate;
@@ -606,7 +607,8 @@ mta_enter_state(struct mta_session *s, int newstate, void *p)
if (connect(s->fd, sa, sa->sa_len) == -1) {
if (errno != EINPROGRESS) {
- mta_status(s, "110 connect error: %s", strerror(errno));
+ mta_status(s, "110 connect error: %s",
+ strerror(errno));
close(s->fd);
continue;
}
@@ -634,10 +636,7 @@ mta_enter_state(struct mta_session *s, int newstate, void *p)
*/
log_debug("mta: entering smtp phase");
- s->smtp_state = client_init(s->fd, s->env->sc_hostname);
- if (s->smtp_state == NULL)
- fatal("mta: client_init failed");
- client_verbose(s->smtp_state, stderr);
+ pcb = client_init(s->fd, s->env->sc_hostname, 1);
/* lookup SSL certificate */
if (s->cert) {
@@ -646,57 +645,46 @@ mta_enter_state(struct mta_session *s, int newstate, void *p)
strlcpy(key.ssl_name, s->cert, sizeof(key.ssl_name));
res = SPLAY_FIND(ssltree, s->env->sc_ssl, &key);
if (res == NULL) {
- client_close(s->smtp_state);
- s->smtp_state = NULL;
+ client_close(pcb);
mta_status(s, "190 certificate not found");
mta_enter_state(s, MTA_DONE, NULL);
break;
}
- if (client_certificate(s->smtp_state,
+ client_certificate(pcb,
res->ssl_cert, res->ssl_cert_len,
- res->ssl_key, res->ssl_key_len) < 0)
- fatal("mta: client_certificate failed");
+ res->ssl_key, res->ssl_key_len);
}
/* choose SMTPS vs. STARTTLS */
relay = TAILQ_FIRST(&s->relays);
- if ((s->flags & MTA_FORCE_ANYSSL) && relay->used == 1) {
- if (client_ssl_smtps(s->smtp_state) < 0)
- fatal("mta: client_ssl_smtps failed");
- } else if (s->flags & MTA_FORCE_SMTPS) {
- if (client_ssl_smtps(s->smtp_state) < 0)
- fatal("mta: client_ssl_smtps failed");
- } else if (s->flags & MTA_ALLOW_PLAIN) {
- if (client_ssl_optional(s->smtp_state) < 0)
- fatal("mta: client_ssl_optional failed");
- }
+ if ((s->flags & MTA_FORCE_ANYSSL) && relay->used == 1)
+ client_ssl_smtps(pcb);
+ else if (s->flags & MTA_FORCE_SMTPS)
+ client_ssl_smtps(pcb);
+ else if (s->flags & MTA_ALLOW_PLAIN)
+ client_ssl_optional(pcb);
/* enable AUTH */
if (s->secret)
- if (client_auth(s->smtp_state, s->secret) < 0)
- fatal("mta: client_auth failed");
+ client_auth(pcb, s->secret);
/* set envelope sender */
m = TAILQ_FIRST(&s->recipients);
if (m->sender.user[0] && m->sender.domain[0])
- if (client_sender(s->smtp_state, "%s@%s",
- m->sender.user, m->sender.domain) < 0)
- fatal("mta: client_sender failed");
+ client_sender(pcb, "%s@%s", m->sender.user,
+ m->sender.domain);
/* set envelope recipients */
- TAILQ_FOREACH(m, &s->recipients, entry) {
- if (client_rcpt(s->smtp_state, "%s@%s", m->recipient.user,
- m->recipient.domain) < 0)
- fatal("mta: client_rcpt failed");
- client_udata_set(s->smtp_state, m);
- }
+ TAILQ_FOREACH(m, &s->recipients, entry)
+ client_rcpt(pcb, m, "%s@%s", m->recipient.user,
+ m->recipient.domain);
/* load message body */
- if (client_data_fd(s->smtp_state, s->datafd) < 0)
- fatal("mta: client_data_fd failed");
+ client_data_fd(pcb, s->datafd);
+ s->pcb = pcb;
event_set(&s->ev, s->fd, EV_WRITE, mta_event, s);
- event_add(&s->ev, client_timeout(s->smtp_state));
+ event_add(&s->ev, &pcb->timeout);
break;
case MTA_DONE:
@@ -781,7 +769,7 @@ mta_pickup(struct mta_session *s, void *p)
/* Remote accepted/rejected connection. */
error = session_socket_error(s->fd);
if (error) {
- mta_status(s, "110 connect error");
+ mta_status(s, "110 connect error: %s", strerror(error));
close(s->fd);
mta_enter_state(s, MTA_CONNECT, NULL);
} else
@@ -801,57 +789,48 @@ void
mta_event(int fd, short event, void *p)
{
struct mta_session *s = p;
- int error = 0;
- int (*iofunc)(struct smtp_client *);
+ struct smtp_client *pcb = s->pcb;
if (event & EV_TIMEOUT) {
- log_debug("mta: leaving smtp phase due to timeout");
mta_status(s, "150 timeout");
-
- client_close(s->smtp_state);
- s->smtp_state = NULL;
-
- mta_enter_state(s, MTA_CONNECT, NULL);
- return;
+ goto out;
}
- if (event & EV_READ)
- iofunc = client_read;
- else
- iofunc = client_write;
-
- switch (iofunc(s->smtp_state)) {
- case CLIENT_RCPT_FAIL:
- mta_message_status(client_udata_get(s->smtp_state),
- client_reply(s->smtp_state));
- case CLIENT_WANT_WRITE:
- event_set(&s->ev, fd, EV_WRITE, mta_event, s);
- event_add(&s->ev, client_timeout(s->smtp_state));
- return;
+ switch (client_talk(pcb)) {
case CLIENT_WANT_READ:
- event_set(&s->ev, fd, EV_READ, mta_event, s);
- event_add(&s->ev, client_timeout(s->smtp_state));
- return;
- case CLIENT_ERROR:
- error = 1;
+ goto read;
+ case CLIENT_WANT_WRITE:
+ goto write;
+ case CLIENT_RCPT_FAIL:
+ mta_message_status(pcb->rcptfail->p, pcb->reply);
+ mta_message_log(s, pcb->rcptfail->p);
+ mta_message_done(s, pcb->rcptfail->p);
+ goto write;
case CLIENT_DONE:
+ mta_status(s, "%s", pcb->status);
break;
+ default:
+ fatalx("mta_event: unexpected code");
}
- log_debug("mta: leaving smtp phase");
-
- if (error)
- mta_status(s, "%s", client_strerror(s->smtp_state));
- else
- mta_status(s, "%s", client_reply(s->smtp_state));
-
- client_close(s->smtp_state);
- s->smtp_state = NULL;
+out:
+ client_close(pcb);
+ pcb = NULL;
- if (mta_todo(s) == 0)
+ if (TAILQ_EMPTY(&s->recipients))
mta_enter_state(s, MTA_DONE, NULL);
else
mta_enter_state(s, MTA_CONNECT, NULL);
+ return;
+
+read:
+ event_set(&s->ev, fd, EV_READ, mta_event, s);
+ event_add(&s->ev, &pcb->timeout);
+ return;
+
+write:
+ event_set(&s->ev, fd, EV_WRITE, mta_event, s);
+ event_add(&s->ev, &pcb->timeout);
}
void
@@ -859,7 +838,6 @@ mta_status(struct mta_session *s, const char *fmt, ...)
{
char *status;
struct message *m, *next;
- struct mta_relay *relay;
va_list ap;
va_start(ap, fmt);
@@ -873,19 +851,9 @@ mta_status(struct mta_session *s, const char *fmt, ...)
/* save new status */
mta_message_status(m, status);
- relay = TAILQ_FIRST(&s->relays);
-
/* remove queue entry */
if (*status == '2' || *status == '5' || *status == '6') {
- log_info("%s: to=<%s@%s>, delay=%d, relay=%s [%s],"
- " stat=%s (%s)",
- m->message_id, m->recipient.user,
- m->recipient.domain, time(NULL) - m->creation,
- relay ? relay->fqdn : "(none)",
- relay ? ss_to_text(&relay->sa) : "",
- *status == '2' ? "Sent" :
- *status == '5' ? "RemoteError" : "LocalError",
- m->session_errorline + 4);
+ mta_message_log(s, m);
mta_message_done(s, m);
}
}
@@ -911,6 +879,23 @@ mta_message_status(struct message *m, char *status)
}
void
+mta_message_log(struct mta_session *s, struct message *m)
+{
+ struct mta_relay *relay = TAILQ_FIRST(&s->relays);
+ char *status = m->session_errorline;
+
+ log_info("%s: to=<%s@%s>, delay=%d, relay=%s [%s], stat=%s (%s)",
+ m->message_id, m->recipient.user,
+ m->recipient.domain, time(NULL) - m->creation,
+ relay ? relay->fqdn : "(none)",
+ relay ? ss_to_text(&relay->sa) : "",
+ *status == '2' ? "Sent" :
+ *status == '5' ? "RemoteError" :
+ *status == '4' ? "RemoteError" : "LocalError",
+ status + 4);
+}
+
+void
mta_message_done(struct mta_session *s, struct message *m)
{
switch (m->session_errorline[0]) {
@@ -950,15 +935,4 @@ mta_request_datafd(struct mta_session *s)
0, 0, -1, &b, sizeof(b));
}
-size_t
-mta_todo(struct mta_session *s)
-{
- struct message *m;
- size_t n = 0;
-
- TAILQ_FOREACH(m, &s->recipients, entry)
- n++;
- return (n);
-}
-
SPLAY_GENERATE(mtatree, mta_session, entry, mta_session_cmp);
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index 137427e181e..43830fb1492 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.159 2009/11/13 11:27:52 jacekm Exp $ */
+/* $OpenBSD: smtpd.h,v 1.160 2009/12/12 10:33:11 jacekm Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -793,7 +793,7 @@ struct mta_session {
int datafd;
struct event ev;
char *cert;
- void *smtp_state;
+ void *pcb;
};
/* aliases.c */