summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd/parse.y
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2018-05-24 11:38:25 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2018-05-24 11:38:25 +0000
commitace804cafcf33d9452774666bb47840ac1899bd0 (patch)
tree1b3ee00426c097326c9b50cc3bc062f20c89b0f9 /usr.sbin/smtpd/parse.y
parentb22354e5b6362967535adf6c2e0d2c0d978e6ee0 (diff)
switch smtpd to new grammar
ok eric@
Diffstat (limited to 'usr.sbin/smtpd/parse.y')
-rw-r--r--usr.sbin/smtpd/parse.y1229
1 files changed, 642 insertions, 587 deletions
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index 2ebfe459fa9..2275e6a88df 100644
--- a/usr.sbin/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.200 2018/04/26 14:12:19 krw Exp $ */
+/* $OpenBSD: parse.y,v 1.201 2018/05/24 11:38:24 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -93,15 +93,17 @@ char *symget(const char *);
struct smtpd *conf = NULL;
static int errors = 0;
-static uint64_t ruleid = 0;
struct filter_conf *filter = NULL;
struct table *table = NULL;
-struct rule *rule = NULL;
struct mta_limits *limits;
static struct pki *pki;
static struct ca *sca;
+struct dispatcher *dispatcher;
+struct rule *rule;
+
+
enum listen_options {
LO_FAMILY = 0x000001,
LO_PORT = 0x000002,
@@ -168,28 +170,46 @@ typedef struct {
%}
-%token AS QUEUE COMPRESSION ENCRYPTION MAXMESSAGESIZE MAXMTADEFERRED LISTEN ON ANY PORT EXPIRE
-%token TABLE SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6 NODSN SESSION
-%token RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX RCPTTO HOSTNAME HOSTNAMES
-%token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA PKI SCHEDULER
-%token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER KEY CA DHE
-%token AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER SENDERS MASK_SOURCE VERIFY FORWARDONLY RECIPIENT
-%token CIPHERS RECEIVEDAUTH MASQUERADE SOCKET SUBADDRESSING_DELIM AUTHENTICATED
+%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL
+%token BACKUP BOUNCE
+%token CA CERT CIPHERS COMPRESSION
+%token DHE DOMAIN
+%token ENCRYPTION ERROR EXPAND_ONLY
+%token FILTER FOR FORWARD_ONLY FROM
+%token HELO HELO_SRC HOST HOSTNAME HOSTNAMES
+%token INCLUDE INET4 INET6
+%token KEY
+%token LIMIT LISTEN LOCAL
+%token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX
+%token NODSN
+%token ON
+%token PKI PORT
+%token QUEUE
+%token RCPT_TO RECIPIENT RECEIVEDAUTH RELAY REJECT
+%token SCHEDULER SENDER SENDERS SET SMTP SMTPS SOCKET SRC SUB_ADDR_DELIM
+%token TABLE TAG TAGGED TLS TLS_REQUIRE TO TTL
+%token USER USERBASE
+%token VERIFY VIRTUAL
+%token WARN_INTERVAL
+
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.table> table
%type <v.number> size negation
-%type <v.table> tables tablenew tableref alias virtual userbase
-%type <v.string> tagged
+%type <v.table> tables tablenew tableref
%%
grammar : /* empty */
| grammar '\n'
| grammar include '\n'
| grammar varset '\n'
- | grammar main '\n'
+ | grammar limit '\n'
+ | grammar listen '\n'
+ | grammar set '\n'
+ | grammar pkica '\n'
| grammar table '\n'
- | grammar rule '\n'
+ | grammar dispatcher '\n'
+ | grammar match '\n'
| grammar error '\n' { file->errors++; }
;
@@ -238,6 +258,532 @@ optnl : '\n' optnl
nl : '\n' optnl
;
+
+dispatcher_local_option:
+USER STRING {
+ if (dispatcher->u.local.requires_root) {
+ yyerror("user may not be specified for this dispatcher");
+ YYERROR;
+ }
+
+ if (dispatcher->u.local.forward_only) {
+ yyerror("user may not be specified for forward-only");
+ YYERROR;
+ }
+
+ if (dispatcher->u.local.expand_only) {
+ yyerror("user may not be specified for expand-only");
+ YYERROR;
+ }
+
+ if (dispatcher->u.local.user) {
+ yyerror("user already specified for this dispatcher");
+ YYERROR;
+ }
+
+ dispatcher->u.local.user = $2;
+}
+| ALIAS tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.local.table_alias) {
+ yyerror("alias mapping already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if (dispatcher->u.local.table_virtual) {
+ yyerror("virtual mapping already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
+ yyerror("table \"%s\" may not be used for alias lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.local.table_alias = strdup(t->t_name);
+}
+| VIRTUAL tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.local.table_virtual) {
+ yyerror("virtual mapping already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if (dispatcher->u.local.table_alias) {
+ yyerror("alias mapping already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
+ yyerror("table \"%s\" may not be used for virtual lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.local.table_virtual = strdup(t->t_name);
+}
+| USERBASE tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.local.table_userbase) {
+ yyerror("userbase mapping already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) {
+ yyerror("table \"%s\" may not be used for userbase lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.local.table_userbase = strdup(t->t_name);
+}
+;
+
+dispatcher_local_options:
+dispatcher_local_option dispatcher_local_options
+| /* empty */
+;
+
+dispatcher_local:
+MBOX {
+ dispatcher->u.local.requires_root = 1;
+ dispatcher->u.local.user = xstrdup("root", "dispatcher_mda");
+ asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.local -f %%{sender} %%{user.username}");
+} dispatcher_local_options
+| MAILDIR {
+ asprintf(&dispatcher->u.local.command,
+ "/usr/libexec/mail.maildir -p %%{user.directory}/Maildir -r %%{dest.user}");
+} dispatcher_local_options
+| MAILDIR STRING {
+ if (strncmp($2, "~/", 2) == 0)
+ asprintf(&dispatcher->u.local.command,
+ "/usr/libexec/mail.maildir -p %%{user.directory}/%s -r %%{dest.user}}", $2+2);
+ else
+ asprintf(&dispatcher->u.local.command,
+ "/usr/libexec/mail.maildir -p %s -r %%{dest.user}}", $2);
+} dispatcher_local_options
+| MDA STRING {
+ asprintf(&dispatcher->u.local.command,
+ "/usr/libexec/mail.mda \"%s\"", $2);
+} dispatcher_local_options
+| FORWARD_ONLY {
+ dispatcher->u.local.forward_only = 1;
+} dispatcher_local_options
+| EXPAND_ONLY {
+ dispatcher->u.local.expand_only = 1;
+} dispatcher_local_options
+
+;
+
+dispatcher_remote_option:
+HELO STRING {
+ if (dispatcher->u.remote.helo) {
+ yyerror("helo already specified for this dispatcher");
+ YYERROR;
+ }
+
+ dispatcher->u.remote.helo = $2;
+}
+| HELO_SRC tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.remote.helo_source) {
+ yyerror("helo-source mapping already specified for this dispatcher");
+ YYERROR;
+ }
+ if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
+ yyerror("table \"%s\" may not be used for helo-source lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.remote.helo_source = strdup(t->t_name);
+}
+| PKI STRING {
+ if (dispatcher->u.remote.pki) {
+ yyerror("pki already specified for this dispatcher");
+ YYERROR;
+ }
+
+ dispatcher->u.remote.pki = $2;
+}
+| CA STRING {
+ if (dispatcher->u.remote.ca) {
+ yyerror("ca already specified for this dispatcher");
+ YYERROR;
+ }
+
+ dispatcher->u.remote.ca = $2;
+}
+| SRC tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.remote.source) {
+ yyerror("source mapping already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) {
+ yyerror("table \"%s\" may not be used for source lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.remote.source = strdup(t->t_name);
+}
+| MAIL_FROM STRING {
+ if (dispatcher->u.remote.mail_from) {
+ yyerror("mail-from already specified for this dispatcher");
+ YYERROR;
+ }
+
+ dispatcher->u.remote.mail_from = $2;
+}
+| BACKUP MX STRING {
+ if (dispatcher->u.remote.backup) {
+ yyerror("backup already specified for this dispatcher");
+ YYERROR;
+ }
+ if (dispatcher->u.remote.smarthost) {
+ yyerror("backup and host are mutually exclusive");
+ YYERROR;
+ }
+
+ dispatcher->u.remote.backup = 1;
+ dispatcher->u.remote.backupmx = $3;
+}
+| BACKUP {
+ if (dispatcher->u.remote.backup) {
+ yyerror("backup already specified for this dispatcher");
+ YYERROR;
+ }
+ if (dispatcher->u.remote.smarthost) {
+ yyerror("backup and host are mutually exclusive");
+ YYERROR;
+ }
+
+ dispatcher->u.remote.backup = 1;
+}
+| HOST tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.remote.smarthost) {
+ yyerror("host mapping already specified for this dispatcher");
+ YYERROR;
+ }
+ if (dispatcher->u.remote.backup) {
+ yyerror("backup and host are mutually exclusive");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_RELAYHOST)) {
+ yyerror("table \"%s\" may not be used for host lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.remote.smarthost = strdup(t->t_name);
+}
+| AUTH tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.remote.smarthost == NULL) {
+ yyerror("auth may not be specified without host on a dispatcher");
+ YYERROR;
+ }
+
+ if (dispatcher->u.remote.auth) {
+ yyerror("auth mapping already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) {
+ yyerror("table \"%s\" may not be used for auth lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.remote.auth = strdup(t->t_name);
+}
+;
+
+dispatcher_remote_options:
+dispatcher_remote_option dispatcher_remote_options
+| /* empty */
+;
+
+dispatcher_remote :
+RELAY dispatcher_remote_options
+;
+
+dispatcher_type:
+dispatcher_local {
+ dispatcher->type = DISPATCHER_LOCAL;
+}
+| dispatcher_remote {
+ dispatcher->type = DISPATCHER_REMOTE;
+}
+;
+
+dispatcher_option:
+TTL STRING {
+ if (dispatcher->ttl) {
+ yyerror("ttl already specified for this dispatcher");
+ YYERROR;
+ }
+
+ dispatcher->ttl = delaytonum($2);
+ if (dispatcher->ttl == -1) {
+ yyerror("ttl delay \"%s\" is invalid", $2);
+ free($2);
+ YYERROR;
+ }
+ free($2);
+}
+;
+
+dispatcher_options:
+dispatcher_option dispatcher_options
+| /* empty */
+;
+
+dispatcher:
+ACTION STRING {
+ if (dict_get(conf->sc_dispatchers, $2)) {
+ yyerror("dispatcher already declared with that name: %s", $2);
+ YYERROR;
+ }
+ dispatcher = xcalloc(1, sizeof *dispatcher, "dispatcher");
+} dispatcher_type dispatcher_options {
+ if (dispatcher->type == DISPATCHER_LOCAL)
+ if (dispatcher->u.local.table_userbase == NULL)
+ dispatcher->u.local.table_userbase = "<getpwnam>";
+ dict_set(conf->sc_dispatchers, $2, dispatcher);
+ dispatcher = NULL;
+}
+;
+
+match_option:
+negation TAG tables {
+ struct table *t = $3;
+
+ if (rule->flag_tag) {
+ yyerror("tag already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING)) {
+ yyerror("table \"%s\" may not be used for tag lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_tag = $1 ? -1 : 1;
+ rule->table_tag = strdup(t->t_name);
+}
+| negation HELO tables {
+ struct table *t = $3;
+
+ if (rule->flag_smtp_helo) {
+ yyerror("mail-helo already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
+ yyerror("table \"%s\" may not be used for helo lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_smtp_helo = $1 ? -1 : 1;
+ rule->table_smtp_helo = strdup(t->t_name);
+}
+| negation TLS {
+ if (rule->flag_smtp_starttls) {
+ yyerror("tls already specified for this rule");
+ YYERROR;
+ }
+ rule->flag_smtp_starttls = $1 ? -1 : 1;
+}
+| negation AUTH {
+ if (rule->flag_smtp_auth) {
+ yyerror("auth already specified for this rule");
+ YYERROR;
+ }
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+}
+| negation AUTH tables {
+ struct table *t = $3;
+
+ if (rule->flag_smtp_auth) {
+ yyerror("auth already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_CREDENTIALS)) {
+ yyerror("table \"%s\" may not be used for auth lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+ rule->table_smtp_auth = strdup(t->t_name);
+}
+| negation MAIL_FROM tables {
+ struct table *t = $3;
+
+ if (rule->flag_smtp_mail_from) {
+ yyerror("mail-from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
+ yyerror("table \"%s\" may not be used for mail-from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_smtp_mail_from = $1 ? -1 : 1;
+ rule->table_smtp_mail_from = strdup(t->t_name);
+}
+| negation RCPT_TO tables {
+ struct table *t = $3;
+
+ if (rule->flag_smtp_rcpt_to) {
+ yyerror("rcpt-to already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
+ yyerror("table \"%s\" may not be used for rcpt-to lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
+ rule->table_smtp_rcpt_to = strdup(t->t_name);
+}
+
+| negation FROM SOCKET {
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+ rule->flag_from = $1 ? -1 : 1;
+ rule->flag_from_socket = 1;
+}
+| negation FROM LOCAL {
+ struct table *t = table_find("<localhost>", NULL);
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+ rule->flag_from = $1 ? -1 : 1;
+ rule->table_from = strdup(t->t_name);
+}
+| negation FROM ANY {
+ struct table *t = table_find("<anyhost>", NULL);
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+ rule->flag_from = $1 ? -1 : 1;
+ rule->table_from = strdup(t->t_name);
+}
+| negation FROM SRC tables {
+ struct table *t = $4;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = $1 ? -1 : 1;
+ rule->table_from = strdup(t->t_name);
+}
+
+| negation FOR LOCAL {
+ struct table *t = table_find("<localnames>", NULL);
+
+ if (rule->flag_for) {
+ yyerror("for already specified for this rule");
+ YYERROR;
+ }
+ rule->flag_for = $1 ? -1 : 1;
+ rule->table_for = strdup(t->t_name);
+}
+| negation FOR ANY {
+ struct table *t = table_find("<anydestination>", NULL);
+
+ if (rule->flag_for) {
+ yyerror("for already specified for this rule");
+ YYERROR;
+ }
+ rule->flag_for = $1 ? -1 : 1;
+ rule->table_for = strdup(t->t_name);
+}
+| negation FOR DOMAIN tables {
+ struct table *t = $4;
+
+ if (rule->flag_for) {
+ yyerror("for already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
+ yyerror("table \"%s\" may not be used for 'for' lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_for = $1 ? -1 : 1;
+ rule->table_for = strdup(t->t_name);
+}
+;
+
+match_options:
+match_option match_options
+| /* empty */
+;
+
+match_dispatcher:
+STRING {
+ if (dict_get(conf->sc_dispatchers, $1) == NULL) {
+ yyerror("no such dispatcher: %s", $1);
+ YYERROR;
+ }
+ rule->dispatcher = $1;
+}
+;
+
+action:
+REJECT {
+ rule->reject = 1;
+}
+| ACTION match_dispatcher
+;
+
+match:
+MATCH {
+ rule = xcalloc(1, sizeof *rule, "rule");
+} match_options action {
+ TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
+ rule = NULL;
+}
+;
+
size : NUMBER {
if ($1 < 0) {
yyerror("invalid size: %" PRId64, $1);
@@ -258,24 +804,6 @@ size : NUMBER {
}
;
-tagged : TAGGED negation STRING {
- if (strlcpy(rule->r_tag, $3, sizeof rule->r_tag)
- >= sizeof rule->r_tag) {
- yyerror("tag name too long: %s", $3);
- free($3);
- YYERROR;
- }
- free($3);
- rule->r_nottag = $2;
- }
- ;
-
-authenticated : negation AUTHENTICATED {
- rule->r_wantauth = 1;
- rule->r_negwantauth = $1;
- }
- ;
-
bouncedelay : STRING {
time_t d;
int i;
@@ -294,10 +822,10 @@ bouncedelay : STRING {
break;
}
}
+ ;
bouncedelays : bouncedelays ',' bouncedelay
| bouncedelay
- | /* EMPTY */
;
opt_limit_mda : STRING NUMBER {
@@ -325,11 +853,11 @@ opt_limit_mda : STRING NUMBER {
}
;
-limits_session : opt_limit_session limits_session
+limits_smtp : opt_limit_smtp limits_smtp
| /* empty */
;
-opt_limit_session : STRING NUMBER {
+opt_limit_smtp : STRING NUMBER {
if (!strcmp($1, "max-rcpt")) {
conf->sc_session_max_rcpt = $2;
}
@@ -395,7 +923,7 @@ limits_scheduler: opt_limit_scheduler limits_scheduler
| /* empty */
;
-opt_ca : CERTIFICATE STRING {
+opt_ca : CERT STRING {
sca->ca_cert_file = $2;
}
;
@@ -403,7 +931,7 @@ opt_ca : CERTIFICATE STRING {
ca : opt_ca
;
-opt_pki : CERTIFICATE STRING {
+opt_pki : CERT STRING {
pki->pki_cert_file = $2;
}
| KEY STRING {
@@ -434,7 +962,7 @@ opt_sock_listen : FILTER STRING {
YYERROR;
}
}
- | MASK_SOURCE {
+ | MASK_SRC {
if (config_lo_mask_source(&listen_opts)) {
YYERROR;
}
@@ -622,7 +1150,7 @@ opt_if_listen : INET4 {
}
listen_opts.hostnametable = t;
}
- | MASK_SOURCE {
+ | MASK_SRC {
if (config_lo_mask_source(&listen_opts)) {
YYERROR;
}
@@ -704,168 +1232,19 @@ if_listen : opt_if_listen if_listen
| /* empty */
;
-opt_relay_common: AS STRING {
- struct mailaddr maddr, *maddrp;
-
- if (!text_to_mailaddr(&maddr, $2)) {
- yyerror("invalid parameter to AS: %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
-
- if (maddr.user[0] == '\0' && maddr.domain[0] == '\0') {
- yyerror("invalid empty parameter to AS");
- YYERROR;
- }
- else if (maddr.domain[0] == '\0') {
- if (strlcpy(maddr.domain, conf->sc_hostname,
- sizeof (maddr.domain))
- >= sizeof (maddr.domain)) {
- yyerror("hostname too long for AS parameter: %s",
- conf->sc_hostname);
- YYERROR;
- }
- }
- rule->r_as = xmemdup(&maddr, sizeof (*maddrp), "parse relay_as: AS");
- }
- | SOURCE tables {
- struct table *t = $2;
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) {
- yyerror("invalid use of table \"%s\" as "
- "SOURCE parameter", t->t_name);
- YYERROR;
- }
- (void)strlcpy(rule->r_value.relayhost.sourcetable, t->t_name,
- sizeof rule->r_value.relayhost.sourcetable);
- }
- | HOSTNAME STRING {
- (void)strlcpy(rule->r_value.relayhost.heloname, $2,
- sizeof rule->r_value.relayhost.heloname);
- free($2);
- }
- | 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;
- }
- (void)strlcpy(rule->r_value.relayhost.helotable, t->t_name,
- sizeof rule->r_value.relayhost.helotable);
- }
- | PKI STRING {
- if (!lowercase(rule->r_value.relayhost.pki_name, $2,
- sizeof(rule->r_value.relayhost.pki_name))) {
- yyerror("pki name too long: %s", $2);
- free($2);
- YYERROR;
- }
- if (dict_get(conf->sc_pki_dict,
- rule->r_value.relayhost.pki_name) == NULL) {
- log_warnx("pki name not found: %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
- }
- | CA STRING {
- if (!lowercase(rule->r_value.relayhost.ca_name, $2,
- sizeof(rule->r_value.relayhost.ca_name))) {
- yyerror("ca name too long: %s", $2);
- free($2);
- YYERROR;
- }
- if (dict_get(conf->sc_ca_dict,
- rule->r_value.relayhost.ca_name) == NULL) {
- log_warnx("ca name not found: %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
- }
- ;
-
-opt_relay : BACKUP STRING {
- rule->r_value.relayhost.flags |= F_BACKUP;
- if (strlcpy(rule->r_value.relayhost.hostname, $2,
- sizeof (rule->r_value.relayhost.hostname))
- >= sizeof (rule->r_value.relayhost.hostname)) {
- log_warnx("hostname too long: %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
- }
- | BACKUP {
- rule->r_value.relayhost.flags |= F_BACKUP;
- (void)strlcpy(rule->r_value.relayhost.hostname,
- conf->sc_hostname,
- sizeof (rule->r_value.relayhost.hostname));
- }
- | TLS {
- rule->r_value.relayhost.flags |= F_STARTTLS;
- }
- | TLS VERIFY {
- rule->r_value.relayhost.flags |= F_STARTTLS|F_TLS_VERIFY;
- }
- ;
-
-relay : opt_relay_common relay
- | opt_relay relay
- | /* empty */
- ;
-
-opt_relay_via : AUTH tables {
- struct table *t = $2;
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) {
- yyerror("invalid use of table \"%s\" as AUTH parameter",
- t->t_name);
- YYERROR;
- }
- (void)strlcpy(rule->r_value.relayhost.authtable, t->t_name,
- sizeof(rule->r_value.relayhost.authtable));
- }
- | VERIFY {
- if (!(rule->r_value.relayhost.flags & F_SSL)) {
- yyerror("cannot \"verify\" with insecure protocol");
- YYERROR;
- }
- rule->r_value.relayhost.flags |= F_TLS_VERIFY;
- }
- ;
-
-relay_via : opt_relay_common relay_via
- | opt_relay_via relay_via
- | /* empty */
- ;
-
-main : BOUNCEWARN {
+set : SET BOUNCE WARN_INTERVAL {
memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn);
} bouncedelays
- | SUBADDRESSING_DELIM STRING {
- if (strlen($2) != 1) {
- yyerror("subaddressing-delimiter must be one character");
- free($2);
- YYERROR;
- }
-
- if (isspace((int)*$2) || !isprint((int)*$2) || *$2== '@') {
- yyerror("subaddressing-delimiter uses invalid character");
- free($2);
- YYERROR;
- }
-
- conf->sc_subaddressing_delim = $2;
+ | SET MTA MAX_DEFERRED NUMBER {
+ conf->sc_mta_max_deferred = $4;
}
- | QUEUE COMPRESSION {
+ | SET QUEUE COMPRESSION {
conf->sc_queue_flags |= QUEUE_COMPRESSION;
}
- | QUEUE ENCRYPTION {
+ | SET QUEUE ENCRYPTION {
conf->sc_queue_flags |= QUEUE_ENCRYPTION;
}
- | QUEUE ENCRYPTION KEY STRING {
+ | SET QUEUE ENCRYPTION STRING {
if (strcasecmp($4, "stdin") == 0 || strcasecmp($4, "-") == 0) {
conf->sc_queue_key = "stdin";
free($4);
@@ -874,22 +1253,37 @@ main : BOUNCEWARN {
conf->sc_queue_key = $4;
conf->sc_queue_flags |= QUEUE_ENCRYPTION;
}
- | EXPIRE STRING {
- conf->sc_qexpire = delaytonum($2);
- if (conf->sc_qexpire == -1) {
- yyerror("invalid expire delay: %s", $2);
- free($2);
+ | SET QUEUE TTL STRING {
+ conf->sc_ttl = delaytonum($4);
+ if (conf->sc_ttl == -1) {
+ yyerror("invalid ttl delay: %s", $4);
+ free($4);
YYERROR;
}
- free($2);
+ free($4);
+ }
+ | SET SMTP CIPHERS STRING {
+ conf->sc_tls_ciphers = $4;
}
- | MAXMESSAGESIZE size {
- conf->sc_maxsize = $2;
+ | SET SMTP MAX_MESSAGE_SIZE size {
+ conf->sc_maxsize = $4;
}
- | MAXMTADEFERRED NUMBER {
- conf->sc_mta_max_deferred = $2;
+ | SET SMTP SUB_ADDR_DELIM STRING {
+ if (strlen($4) != 1) {
+ yyerror("subaddressing-delimiter must be one character");
+ free($4);
+ YYERROR;
+ }
+ if (isspace((int)*$4) || !isprint((int)*$4) || *$4== '@') {
+ yyerror("sub-addr-delim uses invalid character");
+ free($4);
+ YYERROR;
+ }
+ conf->sc_subaddressing_delim = $4;
}
- | LIMIT SESSION limits_session
+ ;
+
+limit : LIMIT SMTP limits_smtp
| LIMIT MDA limits_mda
| LIMIT MTA FOR DOMAIN STRING {
struct mta_limits *d;
@@ -907,12 +1301,9 @@ main : BOUNCEWARN {
limits = dict_get(conf->sc_limits_dict, "default");
} limits_mta
| LIMIT SCHEDULER limits_scheduler
- | LISTEN {
- memset(&listen_opts, 0, sizeof listen_opts);
- listen_opts.family = AF_UNSPEC;
- listen_opts.flags |= F_EXT_DSN;
- } ON listener_type
- | PKI STRING {
+ ;
+
+pkica : PKI STRING {
char buf[HOST_NAME_MAX+1];
/* if not catchall, check that it is a valid domain */
@@ -952,9 +1343,13 @@ main : BOUNCEWARN {
dict_set(conf->sc_ca_dict, sca->ca_name, sca);
}
} ca
- | CIPHERS STRING {
- conf->sc_tls_ciphers = $2;
- }
+ ;
+
+listen : LISTEN {
+ memset(&listen_opts, 0, sizeof listen_opts);
+ listen_opts.family = AF_UNSPEC;
+ listen_opts.flags |= F_EXT_DSN;
+ } ON listener_type
;
table : TABLE STRING STRING {
@@ -1064,363 +1459,10 @@ tables : tablenew { $$ = $1; }
| tableref { $$ = $1; }
;
-alias : ALIAS tables {
- struct table *t = $2;
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
- yyerror("invalid use of table \"%s\" as ALIAS parameter",
- t->t_name);
- YYERROR;
- }
-
- $$ = t;
- }
- ;
-
-virtual : VIRTUAL tables {
- struct table *t = $2;
-
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
- yyerror("invalid use of table \"%s\" as VIRTUAL parameter",
- t->t_name);
- YYERROR;
- }
- $$ = t;
- }
- ;
-
-usermapping : alias {
- if (rule->r_mapping) {
- yyerror("alias specified multiple times");
- YYERROR;
- }
- rule->r_desttype = DEST_DOM;
- rule->r_mapping = $1;
- }
- | virtual {
- if (rule->r_mapping) {
- yyerror("virtual specified multiple times");
- YYERROR;
- }
- rule->r_desttype = DEST_VDOM;
- rule->r_mapping = $1;
- }
- ;
-
-userbase : USERBASE tables {
- struct table *t = $2;
-
- if (rule->r_userbase) {
- yyerror("userbase specified multiple times");
- YYERROR;
- }
- if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) {
- yyerror("invalid use of table \"%s\" as USERBASE parameter",
- t->t_name);
- YYERROR;
- }
- rule->r_userbase = t;
- }
- ;
-
-deliver_as : AS STRING {
- if (strlcpy(rule->r_delivery_user, $2,
- sizeof(rule->r_delivery_user))
- >= sizeof(rule->r_delivery_user))
- fatal("username too long");
- free($2);
- }
- | /* empty */ {}
- ;
-
-deliver_action : DELIVER TO MAILDIR {
- rule->r_action = A_MAILDIR;
- if (strlcpy(rule->r_value.buffer, "~/Maildir",
- sizeof(rule->r_value.buffer)) >=
- sizeof(rule->r_value.buffer))
- fatal("pathname too long");
- }
- | DELIVER TO MAILDIR STRING {
- rule->r_action = A_MAILDIR;
- if (strlcpy(rule->r_value.buffer, $4,
- sizeof(rule->r_value.buffer)) >=
- sizeof(rule->r_value.buffer))
- fatal("pathname too long");
- free($4);
- }
- | DELIVER TO MBOX {
- rule->r_action = A_MBOX;
- if (strlcpy(rule->r_value.buffer, _PATH_MAILDIR "/%u",
- sizeof(rule->r_value.buffer))
- >= sizeof(rule->r_value.buffer))
- fatal("pathname too long");
- }
- | DELIVER TO LMTP 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");
- free($4);
- }
- | DELIVER TO LMTP STRING RCPTTO 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");
- if (strlcat(rule->r_value.buffer, " rcpt-to",
- sizeof(rule->r_value.buffer))
- >= sizeof(rule->r_value.buffer))
- fatal("lmtp recipient too long");
- } else
- fatal("invalid lmtp destination");
- free($4);
- }
- | DELIVER TO MDA STRING deliver_as {
- rule->r_action = A_MDA;
- if (strlcpy(rule->r_value.buffer, $4,
- sizeof(rule->r_value.buffer))
- >= sizeof(rule->r_value.buffer))
- fatal("command too long");
- free($4);
- }
- ;
-
-relay_action : RELAY relay {
- rule->r_action = A_RELAY;
- }
- | RELAY VIA STRING {
- rule->r_action = A_RELAYVIA;
- if (!text_to_relayhost(&rule->r_value.relayhost, $3)) {
- yyerror("error: invalid url: %s", $3);
- free($3);
- YYERROR;
- }
- free($3);
- } relay_via {
- /* no worries, F_AUTH cant be set without SSL */
- if (rule->r_value.relayhost.flags & F_AUTH) {
- if (rule->r_value.relayhost.authtable[0] == '\0') {
- yyerror("error: auth without auth table");
- YYERROR;
- }
- }
- }
- ;
-
negation : '!' { $$ = 1; }
| /* empty */ { $$ = 0; }
;
-from : FROM negation SOURCE tables {
- struct table *t = $4;
-
- if (rule->r_sources) {
- yyerror("from specified multiple times");
- YYERROR;
- }
- 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;
- rule->r_sources = t;
- }
- | FROM negation ANY {
- if (rule->r_sources) {
- yyerror("from specified multiple times");
- YYERROR;
- }
- rule->r_sources = table_find("<anyhost>", NULL);
- rule->r_notsources = $2;
- }
- | FROM negation LOCAL {
- if (rule->r_sources) {
- yyerror("from specified multiple times");
- YYERROR;
- }
- rule->r_sources = table_find("<localhost>", NULL);
- rule->r_notsources = $2;
- }
- ;
-
-for : FOR negation DOMAIN tables {
- struct table *t = $4;
-
- if (rule->r_destination) {
- yyerror("for specified multiple times");
- YYERROR;
- }
- 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 = $2;
- rule->r_destination = t;
- }
- | FOR negation ANY {
- if (rule->r_destination) {
- yyerror("for specified multiple times");
- YYERROR;
- }
- rule->r_notdestination = $2;
- rule->r_destination = table_find("<anydestination>", NULL);
- }
- | FOR negation LOCAL {
- if (rule->r_destination) {
- yyerror("for specified multiple times");
- YYERROR;
- }
- rule->r_notdestination = $2;
- rule->r_destination = table_find("<localnames>", NULL);
- }
- ;
-
-sender : SENDER negation tables {
- struct table *t = $3;
-
- if (rule->r_senders) {
- yyerror("sender specified multiple times");
- YYERROR;
- }
-
- 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;
- rule->r_senders = t;
- }
- ;
-
-recipient : RECIPIENT negation tables {
- struct table *t = $3;
-
- if (rule->r_recipients) {
- yyerror("recipient specified multiple times");
- YYERROR;
- }
-
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
- yyerror("invalid use of table \"%s\" as RECIPIENT parameter",
- t->t_name);
- YYERROR;
- }
- rule->r_notrecipients = $2;
- rule->r_recipients = t;
- }
- ;
-
-forwardonly : FORWARDONLY {
- if (rule->r_forwardonly) {
- yyerror("forward-only specified multiple times");
- YYERROR;
- }
- rule->r_forwardonly = 1;
- }
- ;
-
-expire : EXPIRE STRING {
- if (rule->r_qexpire != -1) {
- yyerror("expire specified multiple times");
- YYERROR;
- }
- rule->r_qexpire = delaytonum($2);
- if (rule->r_qexpire == -1) {
- yyerror("invalid expire delay: %s", $2);
- free($2);
- YYERROR;
- }
- free($2);
- }
- ;
-
-opt_decision : sender
- | recipient
- | from
- | for
- | tagged
- | authenticated
- ;
-decision : opt_decision decision
- |
- ;
-
-opt_lookup : userbase
- | usermapping
- ;
-lookup : opt_lookup lookup
- |
- ;
-
-action : deliver_action
- | relay_action
- |
- ;
-
-opt_accept : expire
- | forwardonly
- ;
-
-accept_params : opt_accept accept_params
- |
- ;
-
-rule : ACCEPT {
- rule = xcalloc(1, sizeof(*rule), "parse rule: ACCEPT");
- rule->r_id = ++ruleid;
- rule->r_action = A_NONE;
- rule->r_decision = R_ACCEPT;
- rule->r_desttype = DEST_DOM;
- rule->r_qexpire = -1;
- } decision lookup action accept_params {
- if (!rule->r_sources)
- rule->r_sources = table_find("<localhost>", NULL);
- if (!rule->r_destination)
- rule->r_destination = table_find("<localnames>", NULL);
- if (!rule->r_userbase)
- rule->r_userbase = table_find("<getpwnam>", NULL);
- if (rule->r_qexpire == -1)
- rule->r_qexpire = conf->sc_qexpire;
- if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) {
- if (rule->r_userbase != table_find("<getpwnam>", NULL)) {
- yyerror("userbase may not be used with a relay rule");
- YYERROR;
- }
- if (rule->r_mapping) {
- yyerror("aliases/virtual may not be used with a relay rule");
- YYERROR;
- }
- }
- if (rule->r_forwardonly && rule->r_action != A_NONE) {
- yyerror("forward-only may not be used with a default action");
- YYERROR;
- }
- TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
- rule = NULL;
- }
- | REJECT {
- rule = xcalloc(1, sizeof(*rule), "parse rule: REJECT");
- rule->r_id = ++ruleid;
- rule->r_decision = R_REJECT;
- rule->r_desttype = DEST_DOM;
- } decision {
- if (!rule->r_sources)
- rule->r_sources = table_find("<localhost>", NULL);
- if (!rule->r_destination)
- rule->r_destination = table_find("<localnames>", NULL);
- TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
- rule = NULL;
- }
- ;
%%
struct keywords {
@@ -1455,28 +1497,28 @@ lookup(char *s)
{
/* this has to be sorted always */
static const struct keywords keywords[] = {
- { "accept", ACCEPT },
+ { "action", ACTION },
{ "alias", ALIAS },
{ "any", ANY },
- { "as", AS },
{ "auth", AUTH },
{ "auth-optional", AUTH_OPTIONAL },
- { "authenticated", AUTHENTICATED },
{ "backup", BACKUP },
- { "bounce-warn", BOUNCEWARN },
+ { "bounce", BOUNCE },
{ "ca", CA },
- { "certificate", CERTIFICATE },
+ { "cert", CERT },
{ "ciphers", CIPHERS },
{ "compression", COMPRESSION },
- { "deliver", DELIVER },
{ "dhe", DHE },
{ "domain", DOMAIN },
{ "encryption", ENCRYPTION },
- { "expire", EXPIRE },
+ { "expand-only", EXPAND_ONLY },
{ "filter", FILTER },
{ "for", FOR },
- { "forward-only", FORWARDONLY },
+ { "forward-only", FORWARD_ONLY },
{ "from", FROM },
+ { "helo", HELO },
+ { "helo-src", HELO_SRC },
+ { "host", HOST },
{ "hostname", HOSTNAME },
{ "hostnames", HOSTNAMES },
{ "include", INCLUDE },
@@ -1485,44 +1527,48 @@ lookup(char *s)
{ "key", KEY },
{ "limit", LIMIT },
{ "listen", LISTEN },
- { "lmtp", LMTP },
{ "local", LOCAL },
+ { "mail-from", MAIL_FROM },
{ "maildir", MAILDIR },
- { "mask-source", MASK_SOURCE },
+ { "mask-src", MASK_SRC },
{ "masquerade", MASQUERADE },
- { "max-message-size", MAXMESSAGESIZE },
- { "max-mta-deferred", MAXMTADEFERRED },
+ { "match", MATCH },
+ { "max-deferred", MAX_DEFERRED },
+ { "max-message-size", MAX_MESSAGE_SIZE },
{ "mbox", MBOX },
{ "mda", MDA },
{ "mta", MTA },
+ { "mx", MX },
{ "no-dsn", NODSN },
{ "on", ON },
{ "pki", PKI },
{ "port", PORT },
{ "queue", QUEUE },
- { "rcpt-to", RCPTTO },
+ { "rcpt-to", RCPT_TO },
{ "received-auth", RECEIVEDAUTH },
{ "recipient", RECIPIENT },
{ "reject", REJECT },
{ "relay", RELAY },
{ "scheduler", SCHEDULER },
- { "sender", SENDER },
{ "senders", SENDERS },
- { "session", SESSION },
+ { "set", SET },
+ { "smtp", SMTP },
{ "smtps", SMTPS },
{ "socket", SOCKET },
- { "source", SOURCE },
- { "subaddressing-delimiter", SUBADDRESSING_DELIM },
+ { "src", SRC },
+ { "sub-addr-delim", SUB_ADDR_DELIM },
{ "table", TABLE },
{ "tag", TAG },
{ "tagged", TAGGED },
{ "tls", TLS },
{ "tls-require", TLS_REQUIRE },
{ "to", TO },
+ { "ttl", TTL },
+ { "user", USER },
{ "userbase", USERBASE },
{ "verify", VERIFY },
- { "via", VIA },
{ "virtual", VIRTUAL },
+ { "warn-interval", WARN_INTERVAL },
};
const struct keywords *p;
@@ -1871,6 +1917,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict));
conf->sc_rules = calloc(1, sizeof(*conf->sc_rules));
+ conf->sc_dispatchers = calloc(1, sizeof(*conf->sc_dispatchers));
conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners));
conf->sc_ca_dict = calloc(1, sizeof(*conf->sc_ca_dict));
conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict));
@@ -1882,6 +1929,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
if (conf->sc_tables_dict == NULL ||
conf->sc_rules == NULL ||
+ conf->sc_dispatchers == NULL ||
conf->sc_listeners == NULL ||
conf->sc_ca_dict == NULL ||
conf->sc_pki_dict == NULL ||
@@ -1890,6 +1938,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
log_warn("warn: cannot allocate memory");
free(conf->sc_tables_dict);
free(conf->sc_rules);
+ free(conf->sc_dispatchers);
free(conf->sc_listeners);
free(conf->sc_ca_dict);
free(conf->sc_pki_dict);
@@ -1901,8 +1950,8 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
errors = 0;
table = NULL;
- rule = NULL;
+ dict_init(conf->sc_dispatchers);
dict_init(conf->sc_ca_dict);
dict_init(conf->sc_pki_dict);
dict_init(conf->sc_ssl_dict);
@@ -1916,7 +1965,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
TAILQ_INIT(conf->sc_listeners);
TAILQ_INIT(conf->sc_rules);
- conf->sc_qexpire = SMTPD_QUEUE_EXPIRY;
+ conf->sc_ttl = SMTPD_QUEUE_EXPIRY;
conf->sc_opts = opts;
conf->sc_mta_max_deferred = 100;
@@ -1958,6 +2007,12 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
table_create("getpwnam", "<getpwnam>", NULL, NULL);
+ /* bounce dispatcher */
+ dispatcher = xcalloc(1, sizeof *dispatcher, "dispatcher");
+ dispatcher->type = DISPATCHER_BOUNCE;
+ conf->sc_dispatcher_bounce = dispatcher;
+ dispatcher = NULL;
+
/*
* parse configuration
*/