Hi,
This diff implements Delivery Status Notification as specified in
RFC3461 for NOTIFY=SUCCESS case. OpenSMTPD can now relay DSN related
parameters to DSN capable server as well as advertises 250-DSN in
banner. It's now possible to mail -s subject [email protected] -N success
-R hdrs and get a notification from the relaying server. If OpenSMTPD
recieves a mail for which DSN is requested it now generates a DSN
to the sender. Note though that this diff currently only works for
NOTIFY=success case, however, OpenSMTPD already handles "delay" and
"failure" as part of bounce handling. The bounce generation is not
bound by DSN parameters yet. I tested it with postfix server and
would appreciate test reports against other DSN capable servers.
Comments?
diff --git a/smtpd/bounce.c b/smtpd/bounce.c
index ef3e345..7791f1b 100644
--- a/smtpd/bounce.c
+++ b/smtpd/bounce.c
@@ -93,6 +93,7 @@ static void bounce_status(struct bounce_session *, const char
*, ...);
static void bounce_io(struct io *, int);
static void bounce_timeout(int, short, void *);
static void bounce_free(struct bounce_session *);
+static const char *bounce_strtype(enum bounce_type);
static struct tree wait_fd;
static struct bounce_message_tree messages;
@@ -317,6 +318,9 @@ const char *notice_warning2 =
" The message is kept in the queue for up to %s.\n"
" You DO NOT NEED to re-send the message to these recipients.\n\n";
+const char *notice_success =
+ " Your message was successfully delivered to these recipients.\n\n";
+
static int
bounce_next_message(struct bounce_session *s)
{
@@ -400,18 +404,28 @@ bounce_next(struct bounce_session *s)
"\n"
NOTICE_INTRO
"\n",
- (s->msg->bounce.type == B_ERROR) ? "error" : "warning",
+ bounce_strtype(s->msg->bounce.type),
env->sc_hostname,
s->msg->to,
time_to_text(time(NULL)));
- if (s->msg->bounce.type == B_ERROR)
+ switch (s->msg->bounce.type) {
+ case B_ERROR:
iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
notice_error);
- else
+ break;
+ case B_WARNING:
iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
notice_warning,
bounce_duration(s->msg->bounce.delay));
+ break;
+ case B_DSN:
+ iobuf_xfqueue(&s->iobuf, "bounce_next: BODY",
+ notice_success);
+ break;
+ default:
+ log_warn("warn: bounce: unknown bounce_type");
+ }
TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
iobuf_xfqueue(&s->iobuf,
@@ -655,5 +669,21 @@ bounce_message_cmp(const struct bounce_message *a,
return memcmp(&a->bounce, &b->bounce, sizeof (a->bounce));
}
+static const char *
+bounce_strtype(enum bounce_type t)
+{
+ switch (t) {
+ case B_ERROR:
+ return ("error");
+ case B_WARNING:
+ return ("warning");
+ case B_DSN:
+ return ("dsn");
+ default:
+ log_warn("warn: bounce: unknown bounce_type");
+ return ("");
+ }
+}
+
SPLAY_GENERATE(bounce_message_tree, bounce_message, sp_entry,
bounce_message_cmp);
diff --git a/smtpd/enqueue.c b/smtpd/enqueue.c
index b74efd8..4b6914c 100644
--- a/smtpd/enqueue.c
+++ b/smtpd/enqueue.c
@@ -105,6 +105,9 @@ struct {
char *from;
char *fromname;
char **rcpts;
+ char *dsn_notify;
+ char *dsn_ret;
+ char *dsn_envid;
int rcpt_cnt;
int need_linesplit;
int saw_date;
@@ -161,7 +164,7 @@ enqueue(int argc, char *argv[])
char *fake_from = NULL, *buf;
struct passwd *pw;
FILE *fp, *fout;
- size_t len;
+ size_t len, envid_sz;
char *line;
int dotted;
int inheaders = 0;
@@ -175,7 +178,7 @@ enqueue(int argc, char *argv[])
save_argv = argv;
while ((ch = getopt(argc, argv,
- "A:B:b:E::e:F:f:iJ::L:mN:o:p:qR:tvx")) != -1) {
+ "A:B:b:E::e:F:f:iJ::L:mN:o:p:qR:tvV:x")) != -1) {
switch (ch) {
case 'f':
fake_from = optarg;
@@ -183,12 +186,21 @@ enqueue(int argc, char *argv[])
case 'F':
msg.fromname = optarg;
break;
+ case 'N':
+ msg.dsn_notify = optarg;
+ break;
+ case 'R':
+ msg.dsn_ret = optarg;
+ break;
case 't':
tflag = 1;
break;
case 'v':
verbose = 1;
break;
+ case 'V':
+ msg.dsn_envid = optarg;
+ break;
/* all remaining: ignored, sendmail compat */
case 'A':
case 'B':
@@ -198,10 +210,8 @@ enqueue(int argc, char *argv[])
case 'i':
case 'L':
case 'm':
- case 'N': /* XXX: DSN */
case 'o':
case 'p':
- case 'R':
case 'x':
break;
case 'q':
@@ -265,11 +275,20 @@ enqueue(int argc, char *argv[])
send_line(fout, verbose, "EHLO localhost\n");
get_responses(fout, 1);
- send_line(fout, verbose, "MAIL FROM:<%s>\n", msg.from);
+ envid_sz = strlen(msg.dsn_envid);
+ send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\n",
+ msg.from,
+ msg.dsn_ret ? "RET=" : "",
+ msg.dsn_ret ? msg.dsn_ret : "",
+ envid_sz ? "ENVID=" : "",
+ envid_sz ? msg.dsn_envid : "");
get_responses(fout, 1);
for (i = 0; i < msg.rcpt_cnt; i++) {
- send_line(fout, verbose, "RCPT TO:<%s>\n", msg.rcpts[i]);
+ send_line(fout, verbose, "RCPT TO:<%s> %s%s\n",
+ msg.rcpts[i],
+ msg.dsn_notify ? "NOTIFY=" : "",
+ msg.dsn_notify ? msg.dsn_notify : "");
get_responses(fout, 1);
}
diff --git a/smtpd/envelope.c b/smtpd/envelope.c
index 146d737..4c7da61 100644
--- a/smtpd/envelope.c
+++ b/smtpd/envelope.c
@@ -43,6 +43,7 @@
#include "smtpd.h"
#include "log.h"
+static int ascii_load_uint8(uint8_t *, char *);
static int ascii_load_uint16(uint16_t *, char *);
static int ascii_load_uint32(uint32_t *, char *);
static int ascii_load_time(time_t *, char *);
@@ -55,7 +56,9 @@ static int ascii_load_flags(enum envelope_flags *, char *);
static int ascii_load_mta_relay_url(struct relayhost *, char *);
static int ascii_load_mta_relay_flags(uint16_t *, char *);
static int ascii_load_bounce_type(enum bounce_type *, char *);
+static int ascii_load_dsn_ret(enum dsn_ret *, char *);
+static int ascii_dump_uint8(uint8_t, char *, size_t);
static int ascii_dump_uint16(uint16_t, char *, size_t);
static int ascii_dump_uint32(uint32_t, char *, size_t);
static int ascii_dump_time(time_t, char *, size_t);
@@ -67,6 +70,7 @@ static int ascii_dump_flags(enum envelope_flags, char *,
size_t);
static int ascii_dump_mta_relay_url(const struct relayhost *, char *, size_t);
static int ascii_dump_mta_relay_flags(uint16_t, char *, size_t);
static int ascii_dump_bounce_type(enum bounce_type, char *, size_t);
+static int ascii_dump_dsn_ret(enum dsn_ret, char *, size_t);
void
envelope_set_errormsg(struct envelope *e, char *fmt, ...)
@@ -120,6 +124,10 @@ envelope_load_buffer(struct envelope *ep, const char
*ibuf, size_t buflen)
EVP_BOUNCE_TYPE,
EVP_BOUNCE_DELAY,
EVP_BOUNCE_EXPIRE,
+ EVP_DSN_ENVID,
+ EVP_DSN_NOTIFY,
+ EVP_DSN_ORCPT,
+ EVP_DSN_RET
};
char *field, *nextline;
char lbuf[sizeof(*ep)], *buf;
@@ -198,7 +206,11 @@ envelope_dump_buffer(const struct envelope *ep, char
*dest, size_t len)
EVP_LASTBOUNCE,
EVP_EXPIRE,
EVP_RETRY,
- EVP_FLAGS
+ EVP_FLAGS,
+ EVP_DSN_ENVID,
+ EVP_DSN_NOTIFY,
+ EVP_DSN_ORCPT,
+ EVP_DSN_RET
};
enum envelope_field mda_fields[] = {
EVP_MDA_METHOD,
@@ -345,6 +357,14 @@ envelope_ascii_field_name(enum envelope_field field)
return "bounce-delay";
case EVP_BOUNCE_EXPIRE:
return "bounce-expire";
+ case EVP_DSN_ENVID:
+ return "dsn-envid";
+ case EVP_DSN_NOTIFY:
+ return "dsn-notify";
+ case EVP_DSN_ORCPT:
+ return "dsn-orcpt";
+ case EVP_DSN_RET:
+ return "dsn-ret";
}
return NULL;
@@ -430,6 +450,15 @@ envelope_ascii_load(enum envelope_field field, struct
envelope *ep, char *buf)
return ascii_load_time(&ep->agent.bounce.delay, buf);
case EVP_BOUNCE_EXPIRE:
return ascii_load_time(&ep->agent.bounce.expire, buf);
+ case EVP_DSN_NOTIFY:
+ return ascii_load_uint8(&ep->dsn_notify, buf);
+ case EVP_DSN_ORCPT:
+ return ascii_load_mailaddr(&ep->dsn_orcpt, buf);
+ case EVP_DSN_RET:
+ return ascii_load_dsn_ret(&ep->dsn_ret, buf);
+ case EVP_DSN_ENVID:
+ return ascii_load_string(ep->dsn_envid, buf,
+ sizeof ep->dsn_envid);
}
return 0;
}
@@ -510,11 +539,32 @@ envelope_ascii_dump(enum envelope_field field, const
struct envelope *ep,
if (ep->agent.bounce.type != B_WARNING)
return (1);
return ascii_dump_time(ep->agent.bounce.expire, buf, len);
+ case EVP_DSN_NOTIFY:
+ return ascii_dump_uint8(ep->dsn_notify, buf, len);
+ case EVP_DSN_RET:
+ return ascii_dump_dsn_ret(ep->dsn_ret, buf, len);
+ case EVP_DSN_ORCPT:
+ if (ep->dsn_orcpt.user[0] && ep->dsn_orcpt.domain[0])
+ return ascii_dump_mailaddr(&ep->dsn_orcpt, buf, len);
+ return 1;
+ case EVP_DSN_ENVID:
+ return ascii_dump_string(ep->dsn_envid, buf, len);
}
return 0;
}
static int
+ascii_load_uint8(uint8_t *dest, char *buf)
+{
+ const char *errstr;
+
+ *dest = strtonum(buf, 0, 0xff, &errstr);
+ if (errstr)
+ return 0;
+ return 1;
+}
+
+static int
ascii_load_uint16(uint16_t *dest, char *buf)
{
const char *errstr;
@@ -677,12 +727,32 @@ ascii_load_bounce_type(enum bounce_type *dest, char *buf)
*dest = B_ERROR;
else if (strcasecmp(buf, "warn") == 0)
*dest = B_WARNING;
+ else if (strcasecmp(buf, "dsn") == 0)
+ *dest = B_DSN;
+ else
+ return 0;
+ return 1;
+}
+
+static int
+ascii_load_dsn_ret(enum dsn_ret *ret, char *buf)
+{
+ if (strcasecmp(buf, "HDRS") == 0)
+ *ret = DSN_RETHDRS;
+ else if (strcasecmp(buf, "FULL") == 0)
+ *ret = DSN_RETFULL;
else
return 0;
return 1;
}
static int
+ascii_dump_uint8(uint8_t src, char *dest, size_t len)
+{
+ return bsnprintf(dest, len, "%d", src);
+}
+
+static int
ascii_dump_uint16(uint16_t src, char *dest, size_t len)
{
return bsnprintf(dest, len, "%d", src);
@@ -826,8 +896,25 @@ ascii_dump_bounce_type(enum bounce_type type, char *dest,
size_t len)
case B_WARNING:
p = "warn";
break;
+ case B_DSN:
+ p = "dsn";
+ break;
default:
return 0;
}
return bsnprintf(dest, len, "%s", p);
}
+
+static int
+ascii_dump_dsn_ret(enum dsn_ret flag, char *dest, size_t len)
+{
+ size_t cpylen = 0;
+
+ dest[0] = '\0';
+ if (flag == DSN_RETFULL)
+ cpylen = strlcat(dest, "FULL", len);
+ else if (flag == DSN_RETHDRS)
+ cpylen = strlcat(dest, "HDRS", len);
+
+ return cpylen < len ? 1 : 0;
+}
diff --git a/smtpd/mta.c b/smtpd/mta.c
index decdf17..3acd170 100644
--- a/smtpd/mta.c
+++ b/smtpd/mta.c
@@ -244,6 +244,16 @@ mta_imsg(struct mproc *p, struct imsg *imsg)
if (strcmp(buf, e->dest))
e->rcpt = xstrdup(buf, "mta_envelope:rcpt");
e->task = task;
+ if (evp.dsn_orcpt.user[0] && evp.dsn_orcpt.domain[0]) {
+ snprintf(buf, sizeof buf, "%s@%s",
+ evp.dsn_orcpt.user, evp.dsn_orcpt.domain);
+ e->dsn_orcpt = xstrdup(buf,
+ "mta_envelope:dsn_orcpt");
+ }
+ strlcpy(e->dsn_envid, evp.dsn_envid,
+ sizeof e->dsn_envid);
+ e->dsn_notify = evp.dsn_notify;
+ e->dsn_ret = evp.dsn_ret;
TAILQ_INSERT_TAIL(&task->envelopes, e, entry);
log_debug("debug: mta: received evp:%016" PRIx64
@@ -699,7 +709,10 @@ mta_delivery_notify(struct mta_envelope *e, int delivery,
const char *status,
uint32_t penalty)
{
if (delivery == IMSG_DELIVERY_OK) {
- queue_ok(e->id);
+ m_create(p_queue, IMSG_DELIVERY_OK, 0, 0, -1);
+ m_add_evpid(p_queue, e->id);
+ m_add_int(p_queue, e->ext);
+ m_close(p_queue);
}
else if (delivery == IMSG_DELIVERY_TEMPFAIL) {
queue_tempfail(e->id, penalty, status);
@@ -1309,6 +1322,7 @@ mta_flush(struct mta_relay *relay, int fail, const char
*error)
free(e->dest);
free(e->rcpt);
+ free(e->dsn_orcpt);
free(e);
n++;
}
diff --git a/smtpd/mta_session.c b/smtpd/mta_session.c
index f7b65e3..2ca23af 100644
--- a/smtpd/mta_session.c
+++ b/smtpd/mta_session.c
@@ -96,7 +96,7 @@ enum mta_state {
#define MTA_EXT_AUTH 0x04
#define MTA_EXT_AUTH_PLAIN 0x08
#define MTA_EXT_AUTH_LOGIN 0x10
-
+#define MTA_EXT_DSN 0x20
struct failed_evp {
int delivery;
@@ -156,6 +156,8 @@ static void mta_start_tls(struct mta_session *);
static int mta_verify_certificate(struct mta_session *);
static struct mta_session *mta_tree_pop(struct tree *, uint64_t);
static void mta_flush_failedqueue(struct mta_session *);
+static const char * dsn_strret(enum dsn_ret);
+static const char * dsn_strnotify(uint8_t);
void mta_hoststat_update(const char *, const char *);
void mta_hoststat_reschedule(const char *);
void mta_hoststat_cache(const char *, uint64_t);
@@ -590,6 +592,8 @@ mta_connect(struct mta_session *s)
static void
mta_enter_state(struct mta_session *s, int newstate)
{
+ struct mta_envelope *e;
+ size_t envid_sz;
int oldstate;
ssize_t q;
char ibuf[SMTPD_MAXLINESIZE];
@@ -771,15 +775,41 @@ mta_enter_state(struct mta_session *s, int newstate)
break;
case MTA_MAIL:
+ e = s->currevp;
+ if (e == NULL)
+ s->currevp = e = TAILQ_FIRST(&s->task->envelopes);
+
s->hangon = 0;
s->msgtried++;
- mta_send(s, "MAIL FROM:<%s>", s->task->sender);
+ envid_sz = strlen(e->dsn_envid);
+ if (s->ext & MTA_EXT_DSN) {
+ mta_send(s, "MAIL FROM:<%s> %s%s %s%s",
+ s->task->sender,
+ e->dsn_ret ? "RET=" : "",
+ e->dsn_ret ? dsn_strret(e->dsn_ret) : "",
+ envid_sz ? "ENVID=" : "",
+ envid_sz ? e->dsn_envid : "");
+ } else
+ mta_send(s, "MAIL FROM:<%s>", s->task->sender);
break;
case MTA_RCPT:
- if (s->currevp == NULL)
- s->currevp = TAILQ_FIRST(&s->task->envelopes);
- mta_send(s, "RCPT TO:<%s>", s->currevp->dest);
+ e = s->currevp;
+ if (e == NULL) {
+ s->currevp = e = TAILQ_FIRST(&s->task->envelopes);
+ e->ext = s->ext;
+ }
+
+ if (s->ext & MTA_EXT_DSN) {
+ mta_send(s, "RCPT TO:<%s> %s%s %s%s",
+ e->dest,
+ e->dsn_notify ? "NOTIFY=" : "",
+ e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "",
+ e->dsn_orcpt ? "ORCPT=" : "",
+ e->dsn_orcpt ? e->dsn_orcpt : "");
+ } else
+ mta_send(s, "RCPT TO:<%s>", e->dest);
+
s->rcptcount++;
break;
@@ -968,6 +998,8 @@ mta_response(struct mta_session *s, char *line)
}
s->currevp = TAILQ_NEXT(s->currevp, entry);
+ if (s->currevp != NULL)
+ s->currevp->ext = s->ext;
if (line[0] == '2') {
mta_flush_failedqueue(s);
/*
@@ -1203,6 +1235,8 @@ mta_io(struct io *io, int evt)
}
else if (strcmp(msg, "PIPELINING") == 0)
s->ext |= MTA_EXT_PIPELINING;
+ else if (strcmp(msg, "DSN") == 0)
+ s->ext |= MTA_EXT_DSN;
}
if (cont)
@@ -1591,6 +1625,35 @@ mta_verify_certificate(struct mta_session *s)
return 1;
}
+static const char *
+dsn_strret(enum dsn_ret ret)
+{
+ if (ret == DSN_RETHDRS)
+ return "HDRS";
+ else if (ret == DSN_RETFULL)
+ return "FULL";
+ else {
+ log_debug("mta: invalid ret %d", ret);
+ return "???";
+ }
+}
+
+static const char *
+dsn_strnotify(uint8_t arg)
+{
+ if (arg & DSN_SUCCESS)
+ return "SUCCESS";
+ else if (arg & DSN_FAILURE)
+ return "FAILURE";
+ else if (arg & DSN_DELAY)
+ return "DELAY";
+ else if (arg & DSN_NEVER)
+ return "NEVER";
+ else {
+ log_debug("mta: invalid notify %u", arg);
+ return "???";
+ }
+}
#define CASE(x) case x : return #x
diff --git a/smtpd/queue.c b/smtpd/queue.c
index 3736647..15b7c68 100644
--- a/smtpd/queue.c
+++ b/smtpd/queue.c
@@ -69,7 +69,7 @@ queue_imsg(struct mproc *p, struct imsg *imsg)
uint32_t msgid;
uint32_t penalty;
time_t nexttry;
- int fd, ret, v, flags;
+ int fd, mta_ext, ret, v, flags;
if (p->proc == PROC_SMTP) {
@@ -336,7 +336,21 @@ queue_imsg(struct mproc *p, struct imsg *imsg)
case IMSG_DELIVERY_OK:
m_msg(&m, imsg);
m_get_evpid(&m, &evpid);
+ if (p->proc == PROC_MTA)
+ m_get_int(&m, &mta_ext);
m_end(&m);
+ if (queue_envelope_load(evpid, &evp) == 0) {
+ log_warn("queue: dsn: failed to load envelope");
+ return;
+ }
+ if (p->proc == PROC_MDA) { /* XXX */
+ if (evp.dsn_notify & DSN_SUCCESS) {
+ bounce.type = B_DSN;
+ bounce.delay = 0;
+ bounce.expire = 0;
+ queue_bounce(&evp, &bounce);
+ }
+ }
queue_envelope_delete(evpid);
m_create(p_scheduler, IMSG_DELIVERY_OK, 0, 0, -1);
m_add_evpid(p_scheduler, evpid);
diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c
index b3ab6ba..9f3ad1a 100644
--- a/smtpd/smtp_session.c
+++ b/smtpd/smtp_session.c
@@ -163,6 +163,7 @@ static void smtp_enter_state(struct smtp_session *, int);
static void smtp_reply(struct smtp_session *, char *, ...);
static void smtp_command(struct smtp_session *, char *);
static int smtp_parse_mail_args(struct smtp_session *, char *);
+static int smtp_parse_rcpt_args(struct smtp_session *, char *);
static void smtp_rfc4954_auth_plain(struct smtp_session *, char *);
static void smtp_rfc4954_auth_login(struct smtp_session *, char *);
static void smtp_message_write(struct smtp_session *, const char *);
@@ -172,6 +173,7 @@ static void smtp_wait_mfa(struct smtp_session *s, int);
static void smtp_free(struct smtp_session *, const char *);
static const char *smtp_strstate(int);
static int smtp_verify_certificate(struct smtp_session *);
+static uint8_t dsn_notify_str_to_uint8(const char *);
static void smtp_auth_failure_pause(struct smtp_session *);
static void smtp_auth_failure_resume(int, short, void *);
@@ -678,6 +680,7 @@ smtp_mfa_response(struct smtp_session *s, int status,
uint32_t code,
smtp_reply(s, "250-8BITMIME");
smtp_reply(s, "250-ENHANCEDSTATUSCODES");
smtp_reply(s, "250-SIZE %zu", env->sc_maxsize);
+ smtp_reply(s, "250-DSN");
if (ADVERTISE_TLS(s))
smtp_reply(s, "250-STARTTLS");
if (ADVERTISE_AUTH(s))
@@ -1136,11 +1139,8 @@ smtp_command(struct smtp_session *s, char *line)
"553 Recipient address syntax error");
break;
}
- if (*args) {
- smtp_reply(s,
- "553 No option supported on RCPT TO");
+ if (args && smtp_parse_rcpt_args(s, args) == -1)
break;
- }
m_create(p_mfa, IMSG_MFA_REQ_RCPT, 0, 0, -1);
m_add_id(p_mfa, s->id);
@@ -1313,6 +1313,60 @@ abort:
smtp_enter_state(s, STATE_HELO);
}
+static uint8_t
+dsn_notify_str_to_uint8(const char *arg)
+{
+ if (strcasecmp(arg, "SUCCESS") == 0)
+ return DSN_SUCCESS;
+ else if (strcasecmp(arg, "FAILURE") == 0)
+ return DSN_FAILURE;
+ else if (strcasecmp(arg, "DELAY") == 0)
+ return DSN_DELAY;
+ else if (strcasecmp(arg, "NEVER") == 0)
+ return DSN_NEVER;
+
+ return (0);
+}
+
+static int
+smtp_parse_rcpt_args(struct smtp_session *s, char *args)
+{
+ char *b, *p;
+ uint8_t flag;
+
+ while ((b = strsep(&args, " "))) {
+ if (*b == '\0')
+ continue;
+
+ if (strncasecmp(b, "NOTIFY=", 7) == 0) {
+ b += 7;
+ while ((p = strsep(&b, ","))) {
+ if (*p == '\0')
+ continue;
+
+ if ((flag = dsn_notify_str_to_uint8(p)) == 0)
+ continue;
+
+ s->evp.dsn_notify |= flag;
+ }
+ if (s->evp.dsn_notify > DSN_NEVER) {
+ smtp_reply(s,
+ "553 NOTIFY option NEVER cannot be \
+ combined with other options");
+ return (-1);
+ }
+ } else if (strncasecmp(b, "ORCPT=", 6) == 0) {
+ b += 6;
+ /* XXX */
+ } else {
+ smtp_reply(s, "503 Unsupported option %s", b);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
static int
smtp_parse_mail_args(struct smtp_session *s, char *args)
{
@@ -1326,12 +1380,21 @@ smtp_parse_mail_args(struct smtp_session *s, char *args)
log_debug("debug: smtp: AUTH in MAIL FROM command");
else if (strncasecmp(b, "SIZE=", 5) == 0)
log_debug("debug: smtp: SIZE in MAIL FROM command");
- else if (!strcasecmp(b, "BODY=7BIT"))
+ else if (strcasecmp(b, "BODY=7BIT") == 0)
/* XXX only for this transaction */
s->flags &= ~SF_8BITMIME;
else if (strcasecmp(b, "BODY=8BITMIME") == 0)
;
- else {
+ else if (strncasecmp(b, "RET=", 4) == 0) {
+ b += 4;
+ if (strcasecmp(b, "HDRS") == 0)
+ s->evp.dsn_ret = DSN_RETHDRS;
+ else if (strcasecmp(b, "FULL") == 0)
+ s->evp.dsn_ret = DSN_RETFULL;
+ } else if (strncasecmp(b, "ENVID=", 6) == 0) {
+ b += 6;
+ strlcpy(s->evp.dsn_envid, b, sizeof(s->evp.dsn_envid));
+ } else {
smtp_reply(s, "503 Unsupported option %s", b);
return (-1);
}
diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h
index 4a603b8..2d685ce 100644
--- a/smtpd/smtpd.h
+++ b/smtpd/smtpd.h
@@ -378,9 +378,20 @@ struct delivery_mta {
struct relayhost relay;
};
+enum dsn_type {
+ DSN_SUCCESS,
+ DSN_FAILURE,
+ DSN_DELAY
+};
+
+struct delivery_dsn {
+ enum dsn_type type;
+};
+
enum bounce_type {
B_ERROR,
B_WARNING,
+ B_DSN
};
struct delivery_bounce {
@@ -430,6 +441,16 @@ struct expand {
struct expandnode *parent;
};
+#define DSN_SUCCESS 0x01
+#define DSN_FAILURE 0x02
+#define DSN_DELAY 0x04
+#define DSN_NEVER 0x08
+
+enum dsn_ret {
+ DSN_RETFULL = 1,
+ DSN_RETHDRS
+};
+
#define SMTPD_ENVELOPE_VERSION 2
struct envelope {
TAILQ_ENTRY(envelope) entry;
@@ -454,6 +475,7 @@ struct envelope {
struct delivery_mda mda;
struct delivery_mta mta;
struct delivery_bounce bounce;
+ struct delivery_dsn dsn;
} agent;
uint16_t retry;
@@ -462,6 +484,11 @@ struct envelope {
time_t lasttry;
time_t nexttry;
time_t lastbounce;
+
+ struct mailaddr dsn_orcpt;
+ char dsn_envid[101];
+ uint8_t dsn_notify;
+ enum dsn_ret dsn_ret;
};
enum envelope_field {
@@ -495,6 +522,10 @@ enum envelope_field {
EVP_BOUNCE_TYPE,
EVP_BOUNCE_DELAY,
EVP_BOUNCE_EXPIRE,
+ EVP_DSN_ENVID,
+ EVP_DSN_NOTIFY,
+ EVP_DSN_ORCPT,
+ EVP_DSN_RET,
};
struct listener {
@@ -783,6 +814,11 @@ struct mta_envelope {
char *rcpt;
struct mta_task *task;
int delivery;
+ int ext;
+ char *dsn_orcpt;
+ char dsn_envid[101];
+ uint8_t dsn_notify;
+ enum dsn_ret dsn_ret;
};
struct mta_task {
--
You received this mail because you are subscribed to [email protected]
To unsubscribe, send a mail to: [email protected]