aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Faurot <eric@faurot.net>2013-09-12 18:20:44 +0200
committerEric Faurot <eric@faurot.net>2013-09-12 18:20:44 +0200
commitc9c566e93f261a12dfb4f261f85185465d6b7dab (patch)
tree04e3e06aa6a097f4a0cb0f43c3dc3d1b74e7c5ba
parenta couple bzero() added just to be safe (diff)
downloadOpenSMTPD-c9c566e93f261a12dfb4f261f85185465d6b7dab.tar.xz
OpenSMTPD-c9c566e93f261a12dfb4f261f85185465d6b7dab.zip
Implement a "hostnames" listener option.
listen on egress hostnames <table> Here "table" maps IPs to hostnames. When accepting a connection, smtpd uses the hostname found in the mapping if there is an entry for the local IP. Otherwise, it uses the default server name. While there, change the "helo" keyword to "hostnames" on relay rules.
-rw-r--r--smtpd/lka.c18
-rw-r--r--smtpd/parse.y77
-rw-r--r--smtpd/smtp.c1
-rw-r--r--smtpd/smtp_session.c64
-rw-r--r--smtpd/smtpd.conf.519
-rw-r--r--smtpd/smtpd.h3
6 files changed, 137 insertions, 45 deletions
diff --git a/smtpd/lka.c b/smtpd/lka.c
index 94734357..5a02f571 100644
--- a/smtpd/lka.c
+++ b/smtpd/lka.c
@@ -105,6 +105,24 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
lka_session(reqid, &evp);
return;
+ case IMSG_LKA_HELO:
+ m_msg(&m, imsg);
+ m_get_id(&m, &reqid);
+ m_get_string(&m, &tablename);
+ m_get_sockaddr(&m, (struct sockaddr *)&ss);
+ m_end(&m);
+
+ ret = lka_addrname(tablename, (struct sockaddr*)&ss,
+ &addrname);
+
+ m_create(p, IMSG_LKA_HELO, 0, 0, -1);
+ m_add_id(p, reqid);
+ m_add_int(p, ret);
+ if (ret == LKA_OK)
+ m_add_string(p, addrname.name);
+ m_close(p);
+ return;
+
case IMSG_LKA_SSL_INIT:
req_ca_cert = imsg->data;
resp_ca_cert.reqid = req_ca_cert->reqid;
diff --git a/smtpd/parse.y b/smtpd/parse.y
index 5bd2c1c9..d7ffa3b6 100644
--- a/smtpd/parse.y
+++ b/smtpd/parse.y
@@ -98,16 +98,16 @@ struct mta_limits *limits;
static struct ssl *pki_ssl;
static void config_listener(struct listener *, const char *, const char *,
- const char *, in_port_t, const char *, uint16_t, const char *);
+ const char *, in_port_t, const char *, uint16_t, const char *, struct table *);
struct listener *host_v4(const char *, in_port_t);
struct listener *host_v6(const char *, in_port_t);
int host_dns(const char *, const char *, const char *,
struct listenerlist *, int, in_port_t, const char *,
- uint16_t, const char *);
+ uint16_t, const char *, struct table *);
int host(const char *, const char *, const char *,
- struct listenerlist *, int, in_port_t, const char *, uint16_t, const char *);
+ struct listenerlist *, int, in_port_t, const char *, uint16_t, const char *, struct table *);
int interface(const char *, int, const char *, const char *,
- struct listenerlist *, int, in_port_t, const char *, uint16_t, const char *);
+ struct listenerlist *, int, in_port_t, const char *, uint16_t, const char *, struct table *);
void set_localaddrs(void);
int delaytonum(char *);
int is_if_in_group(const char *, const char *);
@@ -127,16 +127,16 @@ typedef struct {
%token AS QUEUE COMPRESSION ENCRYPTION MAXMESSAGESIZE MAXMTADEFERRED MAXSCHEDULERINFLIGHT LISTEN ON ANY PORT EXPIRE
%token TABLE SECURE SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6
-%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX HOSTNAME HELO
+%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX HOSTNAME HOSTNAMES
%token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA PKI
%token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER KEY CA DHPARAMS
%token AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER MASK_SOURCE VERIFY
%token <v.string> STRING
%token <v.number> NUMBER
-%type <v.table> table
+%type <v.table> table listen_hostnames
%type <v.number> port auth ssl size expire address_family mask_source
%type <v.table> tables tablenew tableref destination alias virtual usermapping userbase from sender
-%type <v.string> pkiname tag tagged listen_helo
+%type <v.string> pkiname tag tagged listen_hostname
%%
grammar : /* empty */
@@ -329,7 +329,19 @@ address_family : INET4 { $$ = AF_INET; }
| /* empty */ { $$ = AF_UNSPEC; }
;
-listen_helo : HOSTNAME STRING { $$ = $2; }
+listen_hostname : HOSTNAME STRING { $$ = $2; }
+ | /* empty */ { $$ = NULL; }
+ ;
+
+listen_hostnames: HOSTNAMES tables {
+ struct table *t = $2;
+ if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
+ yyerror("invalid use of table \"%s\" as "
+ "HOSTNAMES parameter", t->t_name);
+ YYERROR;
+ }
+ $$ = t;
+ }
| /* empty */ { $$ = NULL; }
;
@@ -407,11 +419,11 @@ opt_relay_common: AS STRING {
strlcpy(rule->r_value.relayhost.sourcetable, t->t_name,
sizeof rule->r_value.relayhost.sourcetable);
}
- | HELO tables {
+ | HOSTNAMES tables {
struct table *t = $2;
if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
yyerror("invalid use of table \"%s\" as "
- "HELO parameter", t->t_name);
+ "HOSTNAMES parameter", t->t_name);
YYERROR;
}
strlcpy(rule->r_value.relayhost.helotable, t->t_name,
@@ -560,7 +572,7 @@ main : BOUNCEWARN {
} limits
| LISTEN {
bzero(&l, sizeof l);
- } ON STRING address_family port ssl pkiname auth tag listen_helo mask_source {
+ } ON STRING address_family port ssl pkiname auth tag listen_hostname listen_hostnames mask_source {
char *ifx = $4;
int family = $5;
in_port_t port = $6;
@@ -568,8 +580,9 @@ main : BOUNCEWARN {
char *pki = $8;
uint16_t auth = $9;
char *tag = $10;
- char *helo = $11;
- uint16_t masksrc = $12;
+ char *hostname = $11;
+ struct table *hostnametable = $12;
+ uint16_t masksrc = $13;
uint16_t flags;
if (port != 0 && ssl == F_SSL) {
@@ -596,9 +609,9 @@ main : BOUNCEWARN {
if (port == 0) {
if (ssl & F_SMTPS) {
if (! interface(ifx, family, tag, pki, conf->sc_listeners,
- MAX_LISTEN, 465, l.authtable, F_SMTPS|flags, helo)) {
+ MAX_LISTEN, 465, l.authtable, F_SMTPS|flags, hostname, hostnametable)) {
if (host(ifx, tag, pki, conf->sc_listeners,
- MAX_LISTEN, 465, l.authtable, ssl|flags, helo) <= 0) {
+ MAX_LISTEN, 465, l.authtable, ssl|flags, hostname, hostnametable) <= 0) {
yyerror("invalid virtual ip or interface: %s", ifx);
YYERROR;
}
@@ -606,9 +619,9 @@ main : BOUNCEWARN {
}
if (! ssl || (ssl & ~F_SMTPS)) {
if (! interface(ifx, family, tag, pki, conf->sc_listeners,
- MAX_LISTEN, 25, l.authtable, (ssl&~F_SMTPS)|flags, helo)) {
+ MAX_LISTEN, 25, l.authtable, (ssl&~F_SMTPS)|flags, hostname, hostnametable)) {
if (host(ifx, tag, pki, conf->sc_listeners,
- MAX_LISTEN, 25, l.authtable, ssl|flags, helo) <= 0) {
+ MAX_LISTEN, 25, l.authtable, ssl|flags, hostname, hostnametable) <= 0) {
yyerror("invalid virtual ip or interface: %s", ifx);
YYERROR;
}
@@ -617,9 +630,9 @@ main : BOUNCEWARN {
}
else {
if (! interface(ifx, family, tag, pki, conf->sc_listeners,
- MAX_LISTEN, port, l.authtable, ssl|auth, helo)) {
+ MAX_LISTEN, port, l.authtable, ssl|auth, hostname, hostnametable)) {
if (host(ifx, tag, pki, conf->sc_listeners,
- MAX_LISTEN, port, l.authtable, ssl|flags, helo) <= 0) {
+ MAX_LISTEN, port, l.authtable, ssl|flags, hostname, hostnametable) <= 0) {
yyerror("invalid virtual ip or interface: %s", ifx);
YYERROR;
}
@@ -1096,8 +1109,8 @@ lookup(char *s)
{ "filter", FILTER },
{ "for", FOR },
{ "from", FROM },
- { "helo", HELO },
{ "hostname", HOSTNAME },
+ { "hostnames", HOSTNAMES },
{ "include", INCLUDE },
{ "inet4", INET4 },
{ "inet6", INET6 },
@@ -1661,14 +1674,14 @@ symget(const char *nam)
static void
config_listener(struct listener *h, const char *name, const char *tag,
const char *pki, in_port_t port, const char *authtable, uint16_t flags,
- const char *helo)
+ const char *hostname, struct table *hostnametable)
{
h->fd = -1;
h->port = port;
h->flags = flags;
- if (helo == NULL)
- helo = conf->sc_hostname;
+ if (hostname == NULL)
+ hostname = conf->sc_hostname;
h->ssl = NULL;
h->ssl_cert_name[0] = '\0';
@@ -1680,7 +1693,9 @@ config_listener(struct listener *h, const char *name, const char *tag,
if (tag != NULL)
(void)strlcpy(h->tag, tag, sizeof(h->tag));
- (void)strlcpy(h->helo, helo, sizeof(h->helo));
+ (void)strlcpy(h->hostname, hostname, sizeof(h->hostname));
+ if (hostnametable)
+ (void)strlcpy(h->hostnametable, hostnametable->t_name, sizeof(h->hostnametable));
}
struct listener *
@@ -1728,7 +1743,7 @@ host_v6(const char *s, in_port_t port)
int
host_dns(const char *s, const char *tag, const char *pki,
struct listenerlist *al, int max, in_port_t port, const char *authtable,
- uint16_t flags, const char *helo)
+ uint16_t flags, const char *hostname, struct table *hostnametable)
{
struct addrinfo hints, *res0, *res;
int error, cnt = 0;
@@ -1769,7 +1784,7 @@ host_dns(const char *s, const char *tag, const char *pki,
sin6->sin6_port = port;
}
- config_listener(h, s, tag, pki, port, authtable, flags, helo);
+ config_listener(h, s, tag, pki, port, authtable, flags, hostname, hostnametable);
TAILQ_INSERT_HEAD(al, h, entry);
cnt++;
@@ -1785,7 +1800,7 @@ host_dns(const char *s, const char *tag, const char *pki,
int
host(const char *s, const char *tag, const char *pki, struct listenerlist *al,
int max, in_port_t port, const char *authtable, uint16_t flags,
- const char *helo)
+ const char *hostname, struct table *hostnametable)
{
struct listener *h;
@@ -1798,18 +1813,18 @@ host(const char *s, const char *tag, const char *pki, struct listenerlist *al,
h = host_v6(s, port);
if (h != NULL) {
- config_listener(h, s, tag, pki, port, authtable, flags, helo);
+ config_listener(h, s, tag, pki, port, authtable, flags, hostname, hostnametable);
TAILQ_INSERT_HEAD(al, h, entry);
return (1);
}
- return (host_dns(s, tag, pki, al, max, port, authtable, flags, helo));
+ return (host_dns(s, tag, pki, al, max, port, authtable, flags, hostname, hostnametable));
}
int
interface(const char *s, int family, const char *tag, const char *pki,
struct listenerlist *al, int max, in_port_t port, const char *authtable,
- uint16_t flags, const char *helo)
+ uint16_t flags, const char *hostname, struct table *hostnametable)
{
struct ifaddrs *ifap, *p;
struct sockaddr_in *sain;
@@ -1853,7 +1868,7 @@ interface(const char *s, int family, const char *tag, const char *pki,
continue;
}
- config_listener(h, s, tag, pki, port, authtable, flags, helo);
+ config_listener(h, s, tag, pki, port, authtable, flags, hostname, hostnametable);
ret = 1;
TAILQ_INSERT_HEAD(al, h, entry);
}
diff --git a/smtpd/smtp.c b/smtpd/smtp.c
index 9b3ae73d..d8242f4c 100644
--- a/smtpd/smtp.c
+++ b/smtpd/smtp.c
@@ -66,6 +66,7 @@ smtp_imsg(struct mproc *p, struct imsg *imsg)
switch (imsg->hdr.type) {
case IMSG_DNS_PTR:
case IMSG_LKA_EXPAND_RCPT:
+ case IMSG_LKA_HELO:
case IMSG_LKA_AUTHENTICATE:
case IMSG_LKA_SSL_INIT:
case IMSG_LKA_SSL_VERIFY:
diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c
index 1fd7b42e..c1ff529e 100644
--- a/smtpd/smtp_session.c
+++ b/smtpd/smtp_session.c
@@ -114,6 +114,7 @@ struct smtp_session {
struct listener *listener;
struct sockaddr_storage ss;
char hostname[SMTPD_MAXHOSTNAMELEN];
+ char smtpname[SMTPD_MAXHOSTNAMELEN];
int flags;
int phase;
@@ -154,6 +155,7 @@ struct smtp_session {
static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *);
static void smtp_session_init(void);
static void smtp_connected(struct smtp_session *);
+static void smtp_send_banner(struct smtp_session *);
static void smtp_mfa_response(struct smtp_session *, int, uint32_t,
const char *);
static void smtp_io(struct io *, int);
@@ -189,6 +191,7 @@ static struct { int code; const char *cmd; } commands[] = {
};
static struct tree wait_lka_ptr;
+static struct tree wait_lka_helo;
static struct tree wait_lka_rcpt;
static struct tree wait_mfa_response;
static struct tree wait_mfa_data;
@@ -206,6 +209,7 @@ smtp_session_init(void)
if (!init) {
tree_init(&wait_lka_ptr);
+ tree_init(&wait_lka_helo);
tree_init(&wait_lka_rcpt);
tree_init(&wait_mfa_response);
tree_init(&wait_mfa_data);
@@ -247,6 +251,8 @@ smtp_session(struct listener *listener, int sock,
s->state = STATE_NEW;
s->phase = PHASE_INIT;
+ strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname));
+
/* For local enqueueing, the hostname is already set */
if (hostname) {
s->flags |= SF_AUTHENTICATED;
@@ -273,7 +279,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
void *ssl;
char user[SMTPD_MAXLOGNAME];
struct msg m;
- const char *line;
+ const char *line, *helo;
uint64_t reqid, evpid;
uint32_t code, msgid;
int status, success, dnserror;
@@ -318,6 +324,20 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
io_reload(&s->io);
return;
+ case IMSG_LKA_HELO:
+ m_msg(&m, imsg);
+ m_get_id(&m, &reqid);
+ s = tree_xpop(&wait_lka_helo, reqid);
+ m_get_int(&m, &status);
+ if (status == LKA_OK) {
+ m_get_string(&m, &helo);
+ strlcpy(s->smtpname, helo, sizeof(s->smtpname));
+ }
+ m_end(&m);
+ smtp_reply(s, SMTPD_BANNER, s->smtpname, SMTPD_NAME);
+ io_reload(&s->io);
+ return;
+
case IMSG_MFA_SMTP_DATA:
m_msg(&m, imsg);
m_get_id(&m, &reqid);
@@ -385,7 +405,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
ss_to_text(&s->ss));
}
fprintf(s->ofile, "by %s (%s) with %sSMTP%s%s id %08x;\n",
- s->listener->helo,
+ s->smtpname,
SMTPD_NAME,
s->flags & SF_EHLO ? "E" : "",
s->flags & SF_SECURE ? "S" : "",
@@ -633,8 +653,7 @@ smtp_mfa_response(struct smtp_session *s, int status, uint32_t code,
tree_xset(&wait_ssl_init, s->id, s);
return;
}
- smtp_reply(s, SMTPD_BANNER, s->listener->helo, SMTPD_NAME);
- io_reload(&s->io);
+ smtp_send_banner(s);
return;
case IMSG_MFA_REQ_HELO:
@@ -649,7 +668,7 @@ smtp_mfa_response(struct smtp_session *s, int status, uint32_t code,
smtp_enter_state(s, STATE_HELO);
smtp_reply(s, "250%c%s Hello %s [%s], pleased to meet you",
(s->flags & SF_EHLO) ? '-' : ' ',
- s->listener->helo,
+ s->smtpname,
s->evp.helo,
ss_to_text(&s->ss));
@@ -781,9 +800,8 @@ smtp_io(struct io *io, int evt)
if (s->listener->flags & F_SMTPS) {
stat_increment("smtp.smtps", 1);
- smtp_reply(s, SMTPD_BANNER, s->listener->helo,
- SMTPD_NAME);
io_set_write(&s->io);
+ smtp_send_banner(s);
}
else {
stat_increment("smtp.tls", 1);
@@ -1079,7 +1097,7 @@ smtp_command(struct smtp_session *s, char *line)
smtp_message_reset(s, 1);
if (smtp_mailaddr(&s->evp.sender, args, 1, &args,
- s->listener->helo) == 0) {
+ s->smtpname) == 0) {
smtp_reply(s, "553 Sender address syntax error");
break;
}
@@ -1107,7 +1125,7 @@ smtp_command(struct smtp_session *s, char *line)
}
if (smtp_mailaddr(&s->evp.rcpt, args, 0, &args,
- s->listener->helo) == 0) {
+ s->smtpname) == 0) {
smtp_reply(s,
"553 Recipient address syntax error");
break;
@@ -1343,6 +1361,34 @@ smtp_connected(struct smtp_session *s)
smtp_wait_mfa(s, IMSG_MFA_REQ_CONNECT);
}
+static void
+smtp_send_banner(struct smtp_session *s)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr *sa;
+ socklen_t sa_len;
+
+ if (s->listener->hostnametable[0]) {
+ sa_len = sizeof(ss);
+ sa = (struct sockaddr *)&ss;
+ if (getsockname(s->io.sock, sa, &sa_len) == -1) {
+ log_warn("warn: getsockname()");
+ }
+ else {
+ m_create(p_lka, IMSG_LKA_HELO, 0, 0, -1);
+ m_add_id(p_lka, s->id);
+ m_add_string(p_lka, s->listener->hostnametable);
+ m_add_sockaddr(p_lka, sa);
+ m_close(p_lka);
+ tree_xset(&wait_lka_helo, s->id, s);
+ return;
+ }
+ }
+
+ smtp_reply(s, SMTPD_BANNER, s->smtpname, SMTPD_NAME);
+ io_reload(&s->io);
+}
+
void
smtp_enter_state(struct smtp_session *s, int newstate)
{
diff --git a/smtpd/smtpd.conf.5 b/smtpd/smtpd.conf.5
index d2ee5a2e..a257c748 100644
--- a/smtpd/smtpd.conf.5
+++ b/smtpd/smtpd.conf.5
@@ -243,7 +243,7 @@ This parameter may use conversion specifiers that are expanded before use
.Op Ic backup Op Ar mx
.Op Ic as Ar address
.Op Ic source Ar source
-.Op Ic helo Ar names
+.Op Ic hostnames Ar names
.Xc
Mail is relayed.
The routing decision is based on the DNS system.
@@ -286,7 +286,7 @@ By default, when connecting to a remote server,
.Xr smtpd 8
advertises its default server name.
A
-.Ic helo
+.Ic hostnames
parameter may be specified to advertise an alternate hostname.
Table
.Ar names
@@ -301,7 +301,7 @@ when connected to the remote server.
.Op Ic auth Aq Ar auth
.Op Ic as Ar address
.Op Ic source Ar source
-.Op Ic helo Ar names
+.Op Ic hostnames Ar names
.Xc
Mail is relayed through the specified
.Ar host
@@ -380,7 +380,7 @@ By default, when connecting to a remote server,
.Xr smtpd 8
advertises its default server name.
A
-.Ic helo
+.Ic hostnames
parameter may be specified to advertise an alternate hostname.
Table
.Ar names
@@ -442,6 +442,7 @@ to MXs for this domain.
.Op Ic auth | auth-optional
.Op Ic tag Ar tag
.Op Ic hostname Ar hostname
+.Op Ic hostnames Ar names
.Op Ic mask-source
.Ek
.Xc
@@ -531,6 +532,16 @@ If the
parameter is used, then it will be used in the greeting banner
instead of the default server name.
.Pp
+The
+.Ic hostnames
+parameter allows to override the server name for specific addresses.
+Table
+.Ar names
+contains a mapping of IP addresses to hostnames and
+.Xr smtpd 8
+will use the host name that matches the address on which the connection arrives
+if it is found in the mapping.
+.Pp
If the
.Ic mask-source
parameter is used, then the listener will skip the "from" part
diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h
index af10cf26..1248190b 100644
--- a/smtpd/smtpd.h
+++ b/smtpd/smtpd.h
@@ -497,7 +497,8 @@ struct listener {
void *ssl_ctx;
char tag[MAX_TAG_SIZE];
char authtable[SMTPD_MAXLINESIZE];
- char helo[SMTPD_MAXHOSTNAMELEN];
+ char hostname[SMTPD_MAXHOSTNAMELEN];
+ char hostnametable[SMTPD_MAXPATHLEN];
TAILQ_ENTRY(listener) entry;
};