aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Faurot <eric@faurot.net>2013-09-12 18:40:55 +0200
committerEric Faurot <eric@faurot.net>2013-09-12 18:40:55 +0200
commit1ce72ae6c50b7c10faaa4d92a4c65769584efe58 (patch)
tree5eca8128d79fe597e093d4c2ab007973e8c14d92
parentMerge branch 'master' into portable (diff)
parentMerge branch 'master' of ssh.poolp.org:/git/opensmtpd (diff)
downloadOpenSMTPD-opensmtpd-201309121848p1.tar.xz
OpenSMTPD-opensmtpd-201309121848p1.zip
Merge branch 'master' into portableopensmtpd-201309121848p1
-rw-r--r--smtpd/lka.c18
-rw-r--r--smtpd/parse.y126
-rw-r--r--smtpd/ruleset.c31
-rw-r--r--smtpd/smtp.c1
-rw-r--r--smtpd/smtp_session.c64
-rw-r--r--smtpd/smtpd.conf.5122
-rw-r--r--smtpd/smtpd.h9
-rw-r--r--smtpd/to.c17
8 files changed, 288 insertions, 100 deletions
diff --git a/smtpd/lka.c b/smtpd/lka.c
index 80114e5f..b3292795 100644
--- a/smtpd/lka.c
+++ b/smtpd/lka.c
@@ -109,6 +109,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 f0022371..0d5708ed 100644
--- a/smtpd/parse.y
+++ b/smtpd/parse.y
@@ -104,16 +104,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 *);
@@ -133,16 +133,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.number> port auth ssl size expire address_family mask_source
+%type <v.table> table listen_hostnames
+%type <v.number> port auth ssl size expire address_family mask_source negation
%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 */
@@ -283,13 +283,14 @@ tag : TAG STRING {
| /* empty */ { $$ = NULL; }
;
-tagged : TAGGED STRING {
- if (($$ = strdup($2)) == NULL) {
+tagged : TAGGED negation STRING {
+ if (($$ = strdup($3)) == NULL) {
yyerror("strdup");
- free($2);
+ free($3);
YYERROR;
}
- free($2);
+ free($3);
+ rule->r_nottag = $2;
}
| /* empty */ { $$ = NULL; }
;
@@ -335,7 +336,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; }
;
@@ -413,11 +426,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,
@@ -566,7 +579,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;
@@ -574,8 +587,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) {
@@ -602,9 +616,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;
}
@@ -612,9 +626,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;
}
@@ -623,9 +637,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;
}
@@ -864,19 +878,25 @@ userbase : USERBASE tables {
-destination : DOMAIN tables {
- struct table *t = $2;
+destination : negation DOMAIN tables {
+ struct table *t = $3;
if (! table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
yyerror("invalid use of table \"%s\" as DOMAIN parameter",
t->t_name);
YYERROR;
}
-
+ rule->r_notdestination = $1;
$$ = t;
}
- | LOCAL { $$ = table_find("<localnames>", NULL); }
- | ANY { $$ = 0; }
+ | negation LOCAL {
+ rule->r_notdestination = $1;
+ $$ = table_find("<localnames>", NULL);
+ }
+ | negation ANY {
+ rule->r_notdestination = $1;
+ $$ = 0;
+ }
;
@@ -948,37 +968,43 @@ action : userbase DELIVER TO MAILDIR {
}
;
-from : FROM tables {
- struct table *t = $2;
+negation : '!' { $$ = 1; }
+ | /* empty */ { $$ = 0; }
+ ;
+
+from : FROM negation tables {
+ struct table *t = $3;
if (! table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) {
yyerror("invalid use of table \"%s\" as FROM parameter",
t->t_name);
YYERROR;
}
-
+ rule->r_notsources = $2;
$$ = t;
}
- | FROM ANY {
+ | FROM negation ANY {
$$ = table_find("<anyhost>", NULL);
+ rule->r_notsources = $2;
}
- | FROM LOCAL {
+ | FROM negation LOCAL {
$$ = table_find("<localhost>", NULL);
+ rule->r_notsources = $2;
}
| /* empty */ {
$$ = table_find("<localhost>", NULL);
}
;
-sender : SENDER tables {
- struct table *t = $2;
+sender : SENDER negation tables {
+ struct table *t = $3;
if (! table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
yyerror("invalid use of table \"%s\" as SENDER parameter",
t->t_name);
YYERROR;
}
-
+ rule->r_notsenders = $2;
$$ = t;
}
| /* empty */ { $$ = NULL; }
@@ -1102,8 +1128,8 @@ lookup(char *s)
{ "filter", FILTER },
{ "for", FOR },
{ "from", FROM },
- { "helo", HELO },
{ "hostname", HOSTNAME },
+ { "hostnames", HOSTNAMES },
{ "include", INCLUDE },
{ "inet4", INET4 },
{ "inet6", INET6 },
@@ -1667,14 +1693,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';
@@ -1686,7 +1712,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 *
@@ -1738,7 +1766,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;
@@ -1783,7 +1811,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++;
@@ -1799,7 +1827,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;
@@ -1812,18 +1840,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;
@@ -1871,7 +1899,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/ruleset.c b/smtpd/ruleset.c
index 5f76e305..e523e80c 100644
--- a/smtpd/ruleset.c
+++ b/smtpd/ruleset.c
@@ -49,15 +49,20 @@ ruleset_match(const struct envelope *evp)
TAILQ_FOREACH(r, env->sc_rules, r_entry) {
- if (r->r_tag[0] != '\0' && strcmp(r->r_tag, evp->tag) != 0)
- continue;
+ if (r->r_tag[0] != '\0') {
+ ret = strcmp(r->r_tag, evp->tag);
+ if (ret != 0 && !r->r_nottag)
+ continue;
+ if (ret == 0 && r->r_nottag)
+ continue;
+ }
ret = ruleset_check_source(r->r_sources, ss, evp->flags);
if (ret == -1) {
errno = EAGAIN;
return (NULL);
}
- if (ret == 0)
+ if ((ret == 0 && !r->r_notsources) || (ret != 0 && r->r_notsources))
continue;
if (r->r_senders) {
@@ -66,7 +71,7 @@ ruleset_match(const struct envelope *evp)
errno = EAGAIN;
return (NULL);
}
- if (ret == 0)
+ if ((ret == 0 && !r->r_notsenders) || (ret != 0 && r->r_notsenders))
continue;
}
@@ -77,19 +82,21 @@ ruleset_match(const struct envelope *evp)
errno = EAGAIN;
return NULL;
}
- if (ret) {
- if (r->r_desttype == DEST_VDOM &&
- (r->r_action == A_RELAY || r->r_action == A_RELAYVIA)) {
- if (! aliases_virtual_check(r->r_mapping,
- &evp->rcpt)) {
- return NULL;
- }
+ if ((ret == 0 && !r->r_notdestination) || (ret != 0 && r->r_notdestination))
+ continue;
+
+ if (r->r_desttype == DEST_VDOM &&
+ (r->r_action == A_RELAY || r->r_action == A_RELAYVIA)) {
+ if (! aliases_virtual_check(r->r_mapping,
+ &evp->rcpt)) {
+ return NULL;
}
- goto matched;
}
+ goto matched;
}
errno = 0;
+ log_trace(TRACE_RULES, "no rule matched");
return (NULL);
matched:
diff --git a/smtpd/smtp.c b/smtpd/smtp.c
index 6d1bcbfb..7dc2d9d4 100644
--- a/smtpd/smtp.c
+++ b/smtpd/smtp.c
@@ -69,6 +69,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 031361d8..646fa0c4 100644
--- a/smtpd/smtp_session.c
+++ b/smtpd/smtp_session.c
@@ -116,6 +116,7 @@ struct smtp_session {
struct listener *listener;
struct sockaddr_storage ss;
char hostname[SMTPD_MAXHOSTNAMELEN];
+ char smtpname[SMTPD_MAXHOSTNAMELEN];
int flags;
int phase;
@@ -156,6 +157,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);
@@ -191,6 +193,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;
@@ -208,6 +211,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);
@@ -249,6 +253,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;
@@ -275,7 +281,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;
@@ -320,6 +326,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);
@@ -387,7 +407,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" : "",
@@ -635,8 +655,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:
@@ -651,7 +670,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));
@@ -783,9 +802,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);
@@ -1081,7 +1099,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;
}
@@ -1109,7 +1127,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;
@@ -1345,6 +1363,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 33588caf..69b100c7 100644
--- a/smtpd/smtpd.conf.5
+++ b/smtpd/smtpd.conf.5
@@ -80,33 +80,57 @@ The first matching rule decides what action is taken.
If no rule matches the message,
the default action is to reject the message.
.Pp
+Whenever \! is used, it will performe a reverse match.
+.Pp
Following the accept/reject
-decision comes the client's IP address filter:
+decision comes the optional tag matching:
+.Bl -tag -width Ds
+.It Xo
+.Ic tagged
+.Op Ic \!
+.Ic tag
+.Xc
+If specified, the rule will only be matched if the client session was tagged
+.Ar tag .
+.El
+.Pp
+Then, comes the client's IP address filter:
.Bl -tag -width Ds
.It Ic from any
Make the rule match regardless of the IP of connecting client.
-.It Ic from local
+.It Xo
+.Ic from
+.Op Ic \!
+.Ic local
+.Xc
The rule matches only locally originating connections.
This is the default,
and may be omitted.
-.It Ic from Ar network
+.It Xo
+.Ic from
+.Op Ic \!
+.Ic network
+.Xc
The rule matches if the connection is made from the specified
.Ar network ,
specified in CIDR notation.
-.It Ic from Aq Ar table
+.It Xo
+.Ic from
+.Op Ic \!
+.Ic table
+.Xc
The rule matches if the connection is made from a client whose address
is declared in the table
.Ar table .
-.It Ic tagged Ar tag
-If specified, the rule will only be matched if the client session was tagged
-.Ar tag .
-Mails enqueued via the local socket are tagged
-.Qq local .
.El
.Pp
In addition, finer filtering may be achieved on the sender if desired:
.Bl -tag -width Ds
-.It Ic sender Ar senders
+.It Xo
+.Ic sender
+.Op Ic \!
+.Ic senders
+.Xc
If specified, the rule will only be matched if the sender email address
is found in the table
.Ar senders .
@@ -126,7 +150,13 @@ Make the rule match regardless of the domain it is sent to.
The
.Ar vmap
table will be used as the virtual domain mapping.
-.It Ic for domain Ar domain Op Ic alias Aq Ar aliases
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic domain
+.Ar domain
+.Op Ic alias Aq Ar aliases
+.Xc
This rule applies to mail destined for the specified
.Ar domain .
This parameter supports the
@@ -141,7 +171,13 @@ If specified, the table
.Ar aliases
is used for looking up alternative destinations for addresses in this
.Ar domain .
-.It Ic for domain Aq Ar domains Op Ic alias Aq Ar aliases
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic domain
+.Aq Ar domains
+.Op Ic alias Aq Ar aliases
+.Xc
This rule applies to mail destined to domains which are part of the table
.Ar domains .
.Pp
@@ -149,7 +185,13 @@ If specified, the table
.Ar aliases
is used for looking up alternative destinations for addresses in these
.Ar domains .
-.It Ic for domain Ar domain Ic virtual Aq Ar users
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic domain
+.Ar domain
+.Ic virtual Aq Ar users
+.Xc
This rule applies to mail destined for the specified virtual
.Ar domain .
This parameter supports the
@@ -168,7 +210,13 @@ For an example of how to configure the
.Ar users
table, see
.Xr makemap 8 .
-.It Ic for domain Ao Ar domains Ac Ic virtual Aq Ar users
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic domain
+.Ao Ar domains
+.Ac Ic virtual Aq Ar users
+.Xc
This rule applies to mail destined for the virtual domains specified
in the table
.Ar domains .
@@ -180,7 +228,12 @@ For an example of how to configure the
.Ar users
table, see
.Xr makemap 8 .
-.It Ic for local Op Ic alias Aq Ar aliases
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic local
+.Op Ic alias Aq Ar aliases
+.Xc
This rule applies to mail destined to
.Dq localhost
and to the default server name.
@@ -189,7 +242,12 @@ See the
entry for
.Pa /etc/mail/mailname
below for details of how the server name is determined.
-.It Ic for local virtual Aq Ar vmap
+.It Xo
+.Ic for
+.Op Ic \!
+.Ic local
+.Ic virtual Aq Ar vmap
+.Xc
This rule applies to mail destined to
.Dq localhost
and to the default server name.
@@ -243,7 +301,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.
@@ -277,7 +335,7 @@ If the
parameter is specified,
.Xr smtpd 8
will explicitly bind to an address found in the table referenced by
-.Ar table
+.Ar source
when connecting to the relay.
If the table contains more than one address, they are picked in turn each
time a new connection is opened.
@@ -286,7 +344,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 +359,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 +438,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 +500,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
@@ -486,8 +545,9 @@ a key
and Diffie-Hellman parameters
.Ao Ar name Ac Ns .dh
are searched for.
-A certificate authority may be appended to the .crt
-file to create a certificate chain.
+A certificate chain, which may include an intermediate
+and/or Certificate Authority certificate, may be appended to
+the .crt file to create a certificate chain.
If no
.Ic certificate
is specified,
@@ -500,8 +560,12 @@ and
.Pa fxp0.dh .
If no DH parameters are provided, smtpd will use
built-in parameters.
+A Certificate Authority certificate can be placed in a .ca file
+to have it available to OpenSMTPD.
Creation of certificates is documented in
-.Xr starttls 8 .
+.Xr starttls 8 ,
+and creation of Diffie-Hellman parameters is documented in
+.Xr openssl 1 .
.Pp
If the
.Ic auth
@@ -531,6 +595,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 7d24b9b6..c6602b12 100644
--- a/smtpd/smtpd.h
+++ b/smtpd/smtpd.h
@@ -364,10 +364,16 @@ enum decision {
struct rule {
TAILQ_ENTRY(rule) r_entry;
enum decision r_decision;
+ uint8_t r_nottag;
char r_tag[MAX_TAG_SIZE];
+
+ uint8_t r_notsources;
struct table *r_sources;
+
+ uint8_t r_notsenders;
struct table *r_senders;
+ uint8_t r_notdestination;
enum dest_type r_desttype;
struct table *r_destination;
@@ -525,7 +531,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;
};
diff --git a/smtpd/to.c b/smtpd/to.c
index 1d9e49b9..047af7dd 100644
--- a/smtpd/to.c
+++ b/smtpd/to.c
@@ -537,19 +537,26 @@ rule_to_text(struct rule *r)
bzero(buf, sizeof buf);
strlcpy(buf, r->r_decision == R_ACCEPT ? "accept" : "reject", sizeof buf);
if (r->r_tag[0]) {
- strlcat(buf, " on ", sizeof buf);
+ strlcat(buf, " tagged ", sizeof buf);
+ if (r->r_nottag)
+ strlcat(buf, "! ", sizeof buf);
strlcat(buf, r->r_tag, sizeof buf);
}
strlcat(buf, " from ", sizeof buf);
+ if (r->r_notsources)
+ strlcat(buf, "! ", sizeof buf);
strlcat(buf, r->r_sources->t_name, sizeof buf);
+ strlcat(buf, " for ", sizeof buf);
+ if (r->r_notdestination)
+ strlcat(buf, "! ", sizeof buf);
switch (r->r_desttype) {
case DEST_DOM:
if (r->r_destination == NULL) {
- strlcat(buf, " for any", sizeof buf);
+ strlcat(buf, " any", sizeof buf);
break;
}
- strlcat(buf, " for domain ", sizeof buf);
+ strlcat(buf, " domain ", sizeof buf);
strlcat(buf, r->r_destination->t_name, sizeof buf);
if (r->r_mapping) {
strlcat(buf, " alias ", sizeof buf);
@@ -558,11 +565,11 @@ rule_to_text(struct rule *r)
break;
case DEST_VDOM:
if (r->r_destination == NULL) {
- strlcat(buf, " for any virtual ", sizeof buf);
+ strlcat(buf, " any virtual ", sizeof buf);
strlcat(buf, r->r_mapping->t_name, sizeof buf);
break;
}
- strlcat(buf, " for domain ", sizeof buf);
+ strlcat(buf, " domain ", sizeof buf);
strlcat(buf, r->r_destination->t_name, sizeof buf);
strlcat(buf, " virtual ", sizeof buf);
strlcat(buf, r->r_mapping->t_name, sizeof buf);