Hi,
please find below a diff which enhances deliver to LMTP.
Gilles suggested to bring this diff to misc@ to gain a wider audience
and hopefully receive some comments from actual LMTP users.
tl;dr
"deliver to lmtp" delivers to (system) users only, making it hard to
be used in common virtual users setups (with single system user).
Therefore, I provide a diff which adds a parameter to
specify/expands RCPT TO within LMTP.
This was described earlier already [1], but no solution.
Let me explain the rationale/background of the diff below a bit: One of
my production servers runs Sendmail/Dovecot and I would like to migrate
it to OpenSMTPD/Dovecot. The server uses virtual users in a common
scenario: with all mail addresses mapped to a single system user/uid/gid
(called vmail in my case). Dovecot uses a (static) userdb to map the
IMAP maildirs and addresses. Now I have three options to bring incoming
mails from OpenSMTPD to Dovecot:
1. deliver to maildir
Unfortunately, not an option in my case, because I heavily use Sieve
and Quotas on the Dovecot server. Both require updates/processing on
delivery.
2. deliver to mda "dovecot-lda -a ${rcpt} ..."
Would work fine, and is what I have done before with Sendmail as
well. But requires me to fiddle with dovecot-lda options, poor
performance, and expansions and user permissions, as it needs
adjusting for the "single system user all virtual" setup mentioned
above... thus a bit annoying to setup.
3. deliver to lmtp "/var/dovecot/lmtp"
IMHO, the most easy and straight forward option, also preferred
nowadays, due to better performance [2]. Not useable in my
scenarios, as deliver_lmtp.c always uses the (system) user in
RCPT TO.
So the diff below addresses this and makes it possible to
specify/expand the RCPT TO.
Items for Discussion:
- In a first version of this diff and as suggested in [1], I added a
new keyword RCPT, e.g. ... deliver to lmtp "..." rcpt "${rcpt}", I
decided to leave it away as it simplifies things and does not require
to mess with rule_to_text() and A_LMTP
- In a second version I added an additional variable to store and
separate actual lmtp host:port/socket and expanded recipient...
(similar to r_delivery_user) but this required a lot of more changes
in several files to copy things around, and even an additional enum
expand_type might be required: EXPAND_LMTP, or something. So for the
sake of simplicity I decided against this approach.
- Another approach to solve this problem, as suggested in Dovecot
Wiki [3], is to override the RCPT TO with setting
X-Original-Recipient Header (different approach, different diff).
I do not like this idea of injecting additional header in LMTP
session, thus I went with the approach below.
Please: comments, suggestions, other approaches, right/wrong direction,
rants, flames,... ?
Thanks,
Regards,
Joerg
[1] https://www.mail-archive.com/[email protected]/msg00531.html
[2] http://wiki2.dovecot.org/LDA
[3] http://wiki2.dovecot.org/LMTP
----------------------------------------------------------------
smtpd/delivery_lmtp.c | 10 ++++------
smtpd/parse.y | 22 ++++++++++++++++++++++
smtpd/smtpd.conf.5 | 13 ++++++++++++-
3 files changed, 38 insertions(+), 7 deletions(-)
diff --git a/smtpd/delivery_lmtp.c b/smtpd/delivery_lmtp.c
index 5080c46..6c55994 100644
--- a/smtpd/delivery_lmtp.c
+++ b/smtpd/delivery_lmtp.c
@@ -137,16 +137,14 @@ unix_socket(char *path) {
static void
delivery_lmtp_open(struct deliver *deliver)
{
- char *buffer;
- char *lbuf;
+ char *buffer, *lbuf, *rcpt = deliver->to;
char lhloname[255];
int s;
- FILE *fp;
+ FILE *fp = NULL;
enum lmtp_state state = LMTP_BANNER;
size_t len;
- fp = NULL;
-
+ strsep(&rcpt, " ");
if (deliver->to[0] == '/')
s = unix_socket(deliver->to);
else
@@ -183,7 +181,7 @@ delivery_lmtp_open(struct deliver *deliver)
case LMTP_MAIL_FROM:
if (buffer[0] != '2')
errx(1, "MAIL FROM rejected: %s\n", buffer);
- fprintf(fp, "RCPT TO:<%s>\r\n", deliver->user);
+ fprintf(fp, "RCPT TO:<%s>\r\n", rcpt ? rcpt : deliver->user);
state = LMTP_RCPT_TO;
break;
diff --git a/smtpd/parse.y b/smtpd/parse.y
index b73ed7f..8523dd4 100644
--- a/smtpd/parse.y
+++ b/smtpd/parse.y
@@ -1161,6 +1161,28 @@ deliver_action : DELIVER TO MAILDIR {
fatal("invalid lmtp destination");
free($4);
}
+ | DELIVER TO LMTP STRING STRING deliver_as {
+ rule->r_action = A_LMTP;
+ if (strchr($4, ':') || $4[0] == '/') {
+ if (strlcpy(rule->r_value.buffer, $4,
+ sizeof(rule->r_value.buffer))
+ >= sizeof(rule->r_value.buffer))
+ fatal("lmtp destination too long");
+ } else
+ fatal("invalid lmtp destination");
+ if (strchr($5, '%')) {
+ if (strlcat(rule->r_value.buffer, " ",
+ sizeof(rule->r_value.buffer))
+ >= sizeof(rule->r_value.buffer) ||
+ strlcat(rule->r_value.buffer, $5,
+ sizeof(rule->r_value.buffer))
+ >= sizeof(rule->r_value.buffer))
+ fatal("lmtp recipient too long");
+ } else
+ fatal("invalid lmtp recipient");
+ free($4);
+ free($5);
+ }
| DELIVER TO MDA STRING deliver_as {
rule->r_action = A_MDA;
if (strlcpy(rule->r_value.buffer, $4,
diff --git a/smtpd/smtpd.conf.5 b/smtpd/smtpd.conf.5
index 72d3811..eb76ad6 100644
--- a/smtpd/smtpd.conf.5
+++ b/smtpd/smtpd.conf.5
@@ -287,13 +287,22 @@ accept for domain opensmtpd.org forward-only
.Pp
Finally, the method of delivery is specified:
.Bl -tag -width Ds
-.It Ic deliver to lmtp Op Ar host : Ns Ar port | socket
+.It Xo
+.Ic deliver to lmtp
+.Op Ar host : Ns Ar port | socket
+.Op Ar rcpt
+.Xc
Mail is delivered to
.Ar host : Ns Ar port ,
or to the
.Ux
.Ar socket
over LMTP.
+Optionally,
+.Ar rcpt
+might specify a recipient.
+The latter parameter may use conversion specifiers that are expanded before use
+.Pq see Sx FORMAT SPECIFIERS .
.It Ic deliver to maildir Ar path
Mail is added to a maildir.
Its location,
@@ -911,7 +920,9 @@ descriptions.
.Ss FORMAT SPECIFIERS
Some configuration directives support expansion of their parameters at runtime.
Such directives (for example
+.Ic deliver to lmtp ,
.Ic deliver to maildir ,
+and
.Ic deliver to mda )
may use format specifiers which will be expanded before delivery or
relaying.