summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd/mda.c
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2018-09-04 13:04:43 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2018-09-04 13:04:43 +0000
commitb1e325dab97025d042a8b031884ffa9fc4cbf14a (patch)
tree330c1b1c68cacab0087958be6eaeddc195ddcfeb /usr.sbin/smtpd/mda.c
parentba248291fb0b9a18313016d166fea208a1ed4a21 (diff)
upon mda failure, smtpd would assume tempfail and retry. this is at odds
with the other MTA which assume a permfail unless the exit status is one of a specific set. make smtpd honour the same exit statuses as postfix. note that all errors that occur before the user mda is executed (fork, pipe and related) are still considered tempfail, only errors coming from the mda itself are handled as permfail. this commit is a temporary solution as i believe the SIGCHLD handler is way more complex than it should be and we'll simplify it after 6.4 is out. ok eric@
Diffstat (limited to 'usr.sbin/smtpd/mda.c')
-rw-r--r--usr.sbin/smtpd/mda.c80
1 files changed, 73 insertions, 7 deletions
diff --git a/usr.sbin/smtpd/mda.c b/usr.sbin/smtpd/mda.c
index 091d8506d1a..71fa3ac9695 100644
--- a/usr.sbin/smtpd/mda.c
+++ b/usr.sbin/smtpd/mda.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mda.c,v 1.133 2018/07/08 13:06:37 gilles Exp $ */
+/* $OpenBSD: mda.c,v 1.134 2018/09/04 13:04:42 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -35,6 +35,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sysexits.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
@@ -102,6 +103,7 @@ static const char *mda_user_to_text(const struct mda_user *);
static struct mda_envelope *mda_envelope(const struct envelope *);
static void mda_envelope_free(struct mda_envelope *);
static struct mda_session * mda_session(struct mda_user *);
+static const char *mda_sysexit_to_str(int);
static struct tree sessions;
static struct tree users;
@@ -118,12 +120,14 @@ mda_imsg(struct mproc *p, struct imsg *imsg)
struct deliver deliver;
struct msg m;
const void *data;
- const char *error, *parent_error;
+ const char *error, *parent_error, *syserror;
uint64_t reqid;
size_t sz;
char out[256], buf[LINE_MAX];
int n;
enum lka_resp_status status;
+ enum mda_resp_status mda_status;
+ int mda_sysexit;
switch (imsg->hdr.type) {
case IMSG_MDA_LOOKUP_USERINFO:
@@ -311,6 +315,8 @@ mda_imsg(struct mproc *p, struct imsg *imsg)
case IMSG_MDA_DONE:
m_msg(&m, imsg);
m_get_id(&m, &reqid);
+ m_get_int(&m, (int *)&mda_status);
+ m_get_int(&m, (int *)&mda_sysexit);
m_get_string(&m, &parent_error);
m_end(&m);
@@ -322,29 +328,48 @@ mda_imsg(struct mproc *p, struct imsg *imsg)
out[0] = '\0';
if (imsg->fd != -1)
mda_getlastline(imsg->fd, out, sizeof(out));
+
/*
* Choose between parent's description of error and
* child's output, the latter having preference over
* the former.
*/
error = NULL;
- if (strcmp(parent_error, "exited okay") == 0) {
- if (s->datafp || (s->io && io_queued(s->io)))
+ if (mda_status == MDA_OK) {
+ if (s->datafp || (s->io && io_queued(s->io))) {
error = "mda exited prematurely";
+ mda_status = MDA_TEMPFAIL;
+ }
} else
error = out[0] ? out : parent_error;
+ syserror = NULL;
+ if (mda_sysexit) {
+ syserror = mda_sysexit_to_str(mda_sysexit);
+ if (syserror)
+ error = syserror;
+ }
+
/* update queue entry */
- if (error) {
+ switch (mda_status) {
+ case MDA_TEMPFAIL:
mda_queue_tempfail(e->id, error,
ESC_OTHER_MAIL_SYSTEM_STATUS);
(void)snprintf(buf, sizeof buf,
"Error (%s)", error);
mda_log(e, "TempFail", buf);
- }
- else {
+ break;
+ case MDA_PERMFAIL:
+ mda_queue_permfail(e->id, error,
+ ESC_OTHER_MAIL_SYSTEM_STATUS);
+ (void)snprintf(buf, sizeof buf,
+ "Error (%s)", error);
+ mda_log(e, "PermFail", buf);
+ break;
+ case MDA_OK:
mda_queue_ok(e->id);
mda_log(e, "Ok", "Delivered");
+ break;
}
mda_done(s);
return;
@@ -836,3 +861,44 @@ mda_session(struct mda_user * u)
return (s);
}
+
+static const char *
+mda_sysexit_to_str(int sysexit)
+{
+ switch (sysexit) {
+ case EX_USAGE:
+ return "command line usage error";
+ case EX_DATAERR:
+ return "data format error";
+ case EX_NOINPUT:
+ return "cannot open input";
+ case EX_NOUSER:
+ return "user unknown";
+ case EX_NOHOST:
+ return "host name unknown";
+ case EX_UNAVAILABLE:
+ return "service unavailable";
+ case EX_SOFTWARE:
+ return "internal software error";
+ case EX_OSERR:
+ return "system resource problem";
+ case EX_OSFILE:
+ return "critical OS file missing";
+ case EX_CANTCREAT:
+ return "can't create user output file";
+ case EX_IOERR:
+ return "input/output error";
+ case EX_TEMPFAIL:
+ return "temporary failure";
+ case EX_PROTOCOL:
+ return "remote error in protocol";
+ case EX_NOPERM:
+ return "permission denied";
+ case EX_CONFIG:
+ return "local configuration error";
+ default:
+ break;
+ }
+ return NULL;
+}
+