summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd/parse.y
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2013-01-26 09:37:25 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2013-01-26 09:37:25 +0000
commit52e93b0e61fd0a116dbb373054e2cd0ea3bfcf39 (patch)
tree41934d0fc43bfebf55ba5a199e0d699adf24aff1 /usr.sbin/smtpd/parse.y
parent3b78bd2481525635417ca0fc75396ef754c09171 (diff)
Sync with our smtpd repo:
* first bricks of ldap and sqlite support (not finished but both working) * new table API to replace map API, all lookups are done through tables * improved handling of temporary errors throughout the daemon * improved scheduler and mta logic: connection reuse, optimizes batches * improved queue: more tolerant to admin errors, new layout, less disk-IO * improved memory usage under high load * SSL certs/keys isolated to lookup process to avoid facing network * VIRTUAL support improved, fully virtual setups possible now * runtime tracing of processes through smtpctl trace * ssl_privsep.c sync-ed with relayd * ssl.c no longer contains smtpd specific interfaces * smtpd-specific ssl bits moved to ssl_smtpd.c * update mail address in copyright FLUSH YOUR QUEUE. FLUSH YOUR QUEUE. FLUSH YOUR QUEUE. FLUSH YOUR QUEUE. smtpd.conf(5) simplified, it will require adaptations ok eric@
Diffstat (limited to 'usr.sbin/smtpd/parse.y')
-rw-r--r--usr.sbin/smtpd/parse.y1000
1 files changed, 533 insertions, 467 deletions
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index 017eb61e382..02842b6e46d 100644
--- a/usr.sbin/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,7 +1,7 @@
-/* $OpenBSD: parse.y,v 1.110 2012/11/12 14:58:53 eric Exp $ */
+/* $OpenBSD: parse.y,v 1.111 2013/01/26 09:37:23 gilles Exp $ */
/*
- * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
+ * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
* Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
* Copyright (c) 2001 Markus Friedl. All rights reserved.
@@ -88,18 +88,18 @@ char *symget(const char *);
struct smtpd *conf = NULL;
static int errors = 0;
-struct map *map = NULL;
+struct table *table = NULL;
struct rule *rule = NULL;
-TAILQ_HEAD(condlist, cond) *conditions = NULL;
+struct listener l;
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, uint8_t);
int host(const char *, const char *, const char *,
- struct listenerlist *, int, in_port_t, uint8_t);
+ struct listenerlist *, int, in_port_t, const char *, uint8_t, const char *);
int interface(const char *, const char *, const char *,
- struct listenerlist *, int, in_port_t, uint8_t);
+ struct listenerlist *, int, in_port_t, const char *, uint8_t, const char *);
void set_localaddrs(void);
int delaytonum(char *);
int is_if_in_group(const char *, const char *);
@@ -108,8 +108,6 @@ typedef struct {
union {
int64_t number;
objid_t object;
- struct timeval tv;
- struct cond *cond;
char *string;
struct host *host;
struct mailaddr *maddr;
@@ -119,22 +117,19 @@ typedef struct {
%}
-%token AS QUEUE COMPRESSION SIZE LISTEN ON ANY PORT EXPIRE
-%token MAP HASH LIST SINGLE SSL SMTPS CERTIFICATE
-%token DB LDAP FILE DOMAIN SOURCE
-%token RELAY BACKUP VIA DELIVER TO MAILDIR MBOX HOSTNAME
-%token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR
-%token ARROW AUTH TLS LOCAL VIRTUAL TAG ALIAS FILTER KEY
-%token AUTH_OPTIONAL TLS_REQUIRE
+%token AS QUEUE COMPRESSION MAXMESSAGESIZE LISTEN ON ANY PORT EXPIRE
+%token TABLE SSL SMTPS CERTIFICATE DOMAIN BOUNCEWARN
+%token RELAY BACKUP VIA DELIVER TO MAILDIR MBOX HOSTNAME HELO
+%token ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE
+%token ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER KEY
+%token AUTH_OPTIONAL TLS_REQUIRE USERS SENDER
%token <v.string> STRING
%token <v.number> NUMBER
-%type <v.map> map
-%type <v.number> quantifier port from auth ssl size expire
-%type <v.cond> condition
-%type <v.tv> interval
-%type <v.object> mapref
+%type <v.table> table
+%type <v.number> port from auth ssl size expire sender
+%type <v.object> tables tablenew tableref destination alias virtual usermapping userbase credentials
%type <v.maddr> relay_as
-%type <v.string> certname tag on alias credentials compression
+%type <v.string> certificate tag tagged compression relay_source listen_helo relay_helo relay_backup
%%
grammar : /* empty */
@@ -142,7 +137,7 @@ grammar : /* empty */
| grammar include '\n'
| grammar varset '\n'
| grammar main '\n'
- | grammar map '\n'
+ | grammar table '\n'
| grammar rule '\n'
| grammar error '\n' { file->errors++; }
;
@@ -179,33 +174,9 @@ optnl : '\n' optnl
|
;
-optlbracket : '{'
- |
- ;
-
-optrbracket : '}'
- |
- ;
-
nl : '\n' optnl
;
-quantifier : /* empty */ { $$ = 1; }
- | 'm' { $$ = 60; }
- | 'h' { $$ = 3600; }
- | 'd' { $$ = 86400; }
- ;
-
-interval : NUMBER quantifier {
- if ($1 < 0) {
- yyerror("invalid interval: %" PRId64, $1);
- YYERROR;
- }
- $$.tv_usec = 0;
- $$.tv_sec = $1 * $2;
- }
- ;
-
size : NUMBER {
if ($1 < 0) {
yyerror("invalid size: %" PRId64, $1);
@@ -218,10 +189,10 @@ size : NUMBER {
if (scan_scaled($1, &result) == -1 || result < 0) {
yyerror("invalid size: %s", $1);
+ free($1);
YYERROR;
}
free($1);
-
$$ = result;
}
;
@@ -231,28 +202,31 @@ port : PORT STRING {
servent = getservbyname($2, "tcp");
if (servent == NULL) {
- yyerror("port %s is invalid", $2);
+ yyerror("invalid port: %s", $2);
free($2);
YYERROR;
}
- $$ = servent->s_port;
free($2);
+ $$ = ntohs(servent->s_port);
}
| PORT NUMBER {
if ($2 <= 0 || $2 >= (int)USHRT_MAX) {
yyerror("invalid port: %" PRId64, $2);
YYERROR;
}
- $$ = htons($2);
+ $$ = $2;
}
| /* empty */ {
$$ = 0;
}
;
-certname : CERTIFICATE STRING {
- if (($$ = strdup($2)) == NULL)
- fatal(NULL);
+certificate : CERTIFICATE STRING {
+ if (($$ = strdup($2)) == NULL) {
+ yyerror("strdup");
+ free($2);
+ YYERROR;
+ }
free($2);
}
| /* empty */ { $$ = NULL; }
@@ -265,8 +239,20 @@ ssl : SMTPS { $$ = F_SMTPS; }
| /* Empty */ { $$ = 0; }
;
-auth : AUTH { $$ = F_AUTH|F_AUTH_REQUIRE; }
- | AUTH_OPTIONAL { $$ = F_AUTH; }
+auth : AUTH {
+ $$ = F_AUTH|F_AUTH_REQUIRE;
+ }
+ | AUTH_OPTIONAL {
+ $$ = F_AUTH;
+ }
+ | AUTH tables {
+ strlcpy(l.authtable, table_find($2)->t_name, sizeof l.authtable);
+ $$ = F_AUTH|F_AUTH_REQUIRE;
+ }
+ | AUTH_OPTIONAL tables {
+ strlcpy(l.authtable, table_find($2)->t_name, sizeof l.authtable);
+ $$ = F_AUTH;
+ }
| /* empty */ { $$ = 0; }
;
@@ -282,10 +268,22 @@ tag : TAG STRING {
| /* empty */ { $$ = NULL; }
;
+tagged : TAGGED STRING {
+ if (($$ = strdup($2)) == NULL) {
+ yyerror("strdup");
+ free($2);
+ YYERROR;
+ }
+ free($2);
+ }
+ | /* empty */ { $$ = NULL; }
+ ;
+
expire : EXPIRE STRING {
$$ = delaytonum($2);
if ($$ == -1) {
yyerror("invalid expire delay: %s", $2);
+ free($2);
YYERROR;
}
free($2);
@@ -293,116 +291,161 @@ expire : EXPIRE STRING {
| /* empty */ { $$ = conf->sc_qexpire; }
;
-credentials : AUTH STRING {
- if ((map_findbyname($2)) == NULL) {
- yyerror("no such map: %s", $2);
- free($2);
+bouncedelay : STRING {
+ time_t d;
+ int i;
+
+ d = delaytonum($1);
+ if (d < 0) {
+ yyerror("invalid bounce delay: %s", $1);
+ free($1);
YYERROR;
}
- $$ = $2;
+ free($1);
+ for (i = 0; i < MAX_BOUNCE_WARN; i++) {
+ if (conf->sc_bounce_warn[i] != 0)
+ continue;
+ conf->sc_bounce_warn[i] = d;
+ break;
+ }
}
- | /* empty */ { $$ = 0; }
+
+bouncedelays : bouncedelays ',' bouncedelay
+ | bouncedelay
+ | /* EMPTY */
;
-compression : COMPRESSION STRING {
- $$ = $2;
+credentials : AUTH tables {
+ struct table *t = table_find($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;
+ }
+
+ $$ = t->t_id;
}
- | COMPRESSION {
- $$ = "gzip";
+ | /* empty */ { $$ = 0; }
+ ;
+
+compression : COMPRESSION {
+ $$ = strdup("gzip");
+ if ($$ == NULL) {
+ yyerror("strdup");
+ YYERROR;
+ }
}
- | /* empty */ { $$ = NULL; }
+ | COMPRESSION STRING { $$ = $2; }
;
+listen_helo : HOSTNAME STRING { $$ = $2; }
+ | /* empty */ { $$ = NULL; }
+
main : QUEUE compression {
- if ($2) {
- conf->sc_queue_flags |= QUEUE_COMPRESS;
- conf->sc_queue_compress_algo = strdup($2);
- log_debug("debug: queue compress using %s",
- conf->sc_queue_compress_algo);
- }
- if ($2 == NULL) {
- yyerror("invalid queue compress <algo>");
+ conf->sc_queue_compress_algo = strdup($2);
+ if (conf->sc_queue_compress_algo == NULL) {
+ yyerror("strdup");
+ free($2);
YYERROR;
}
+ conf->sc_queue_flags |= QUEUE_COMPRESS;
+ free($2);
}
+ | BOUNCEWARN {
+ bzero(conf->sc_bounce_warn, sizeof conf->sc_bounce_warn);
+ } bouncedelays
| EXPIRE STRING {
conf->sc_qexpire = delaytonum($2);
if (conf->sc_qexpire == -1) {
yyerror("invalid expire delay: %s", $2);
+ free($2);
YYERROR;
}
+ free($2);
}
- | SIZE size {
- conf->sc_maxsize = $2;
- }
- | LISTEN ON STRING port ssl certname auth tag {
- char *cert;
- char *tag;
- uint8_t flags;
-
- if ($5 == F_SSL) {
- yyerror("syntax error");
- free($8);
- free($6);
- free($3);
+ | MAXMESSAGESIZE size {
+ conf->sc_maxsize = $2;
+ }
+ | LISTEN {
+ bzero(&l, sizeof l);
+ } ON STRING port ssl certificate auth tag listen_helo {
+ char *ifx = $4;
+ in_port_t port = $5;
+ uint8_t ssl = $6;
+ char *cert = $7;
+ uint8_t auth = $8;
+ char *tag = $9;
+ char *helo = $10;
+
+ if (port != 0 && ssl == F_SSL) {
+ yyerror("invalid listen option: tls/smtps on same port");
YYERROR;
}
- if ($5 == 0 && ($6 != NULL || $7)) {
- yyerror("error: must specify tls or smtps");
- free($8);
- free($6);
- free($3);
+ if (auth != 0 && !ssl) {
+ yyerror("invalid listen option: auth requires tls/smtps");
YYERROR;
}
- if ($4 == 0) {
- if ($5 == F_SMTPS)
- $4 = htons(465);
- else
- $4 = htons(25);
+ if (port == 0) {
+ if (ssl & F_SMTPS) {
+ if (! interface(ifx, tag, cert, conf->sc_listeners,
+ MAX_LISTEN, 465, l.authtable, F_SMTPS|auth, helo)) {
+ if (host(ifx, tag, cert, conf->sc_listeners,
+ MAX_LISTEN, 465, l.authtable, ssl|auth, helo) <= 0) {
+ yyerror("invalid virtual ip or interface: %s", ifx);
+ YYERROR;
+ }
+ }
+ }
+ if (! ssl || (ssl & ~F_SMTPS)) {
+ if (! interface(ifx, tag, cert, conf->sc_listeners,
+ MAX_LISTEN, 25, l.authtable, (ssl&~F_SMTPS)|auth, helo)) {
+ if (host(ifx, tag, cert, conf->sc_listeners,
+ MAX_LISTEN, 25, l.authtable, ssl|auth, helo) <= 0) {
+ yyerror("invalid virtual ip or interface: %s", ifx);
+ YYERROR;
+ }
+ }
+ }
}
+ else {
+ if (! interface(ifx, tag, cert, conf->sc_listeners,
+ MAX_LISTEN, port, l.authtable, ssl|auth, helo)) {
+ if (host(ifx, tag, cert, conf->sc_listeners,
+ MAX_LISTEN, port, l.authtable, ssl|auth, helo) <= 0) {
+ yyerror("invalid virtual ip or interface: %s", ifx);
+ YYERROR;
+ }
+ }
+ }
+ }
+ | FILTER STRING {
+ struct filter *filter;
+ struct filter *tmp;
- cert = ($6 != NULL) ? $6 : $3;
- flags = $5 | $7; /* ssl | auth */
-
- if ($5 && ssl_load_certfile(cert, F_SCERT) < 0) {
- yyerror("cannot load certificate: %s", cert);
- free($8);
- free($6);
- free($3);
+ filter = xcalloc(1, sizeof *filter, "parse condition: FILTER");
+ if (strlcpy(filter->name, $2, sizeof (filter->name))
+ >= sizeof (filter->name)) {
+ yyerror("Filter name too long: %s", filter->name);
+ free($2);
YYERROR;
+
}
+ (void)snprintf(filter->path, sizeof filter->path,
+ PATH_FILTERS "/%s", filter->name);
- tag = $3;
- if ($8 != NULL)
- tag = $8;
-
- if (! interface($3, tag, cert, conf->sc_listeners,
- MAX_LISTEN, $4, flags)) {
- if (host($3, tag, cert, conf->sc_listeners,
- MAX_LISTEN, $4, flags) <= 0) {
- yyerror("invalid virtual ip or interface: %s", $3);
- free($8);
- free($6);
- free($3);
- YYERROR;
- }
- }
- free($8);
- free($6);
- free($3);
- }
- | HOSTNAME STRING {
- if (strlcpy(conf->sc_hostname, $2,
- sizeof(conf->sc_hostname)) >=
- sizeof(conf->sc_hostname)) {
- yyerror("hostname truncated");
+ tmp = dict_get(&conf->sc_filters, filter->name);
+ if (tmp == NULL)
+ dict_set(&conf->sc_filters, filter->name, filter);
+ else {
+ yyerror("ambiguous filter name: %s", filter->name);
free($2);
YYERROR;
}
free($2);
- }/*
+ }
| FILTER STRING STRING {
struct filter *filter;
struct filter *tmp;
@@ -419,12 +462,9 @@ main : QUEUE compression {
YYERROR;
}
- TAILQ_FOREACH(tmp, conf->sc_filters, f_entry) {
- if (strcasecmp(filter->name, tmp->name) == 0)
- break;
- }
+ tmp = dict_get(&conf->sc_filters, filter->name);
if (tmp == NULL)
- TAILQ_INSERT_TAIL(conf->sc_filters, filter, f_entry);
+ dict_set(&conf->sc_filters, filter->name, filter);
else {
yyerror("ambiguous filter name: %s", filter->name);
free($2);
@@ -434,55 +474,56 @@ main : QUEUE compression {
free($2);
free($3);
}
- */
- ;
-
-mapsource : SOURCE FILE STRING {
- map->m_src = S_FILE;
- if (strlcpy(map->m_config, $3, sizeof(map->m_config))
- >= sizeof(map->m_config))
- err(1, "pathname too long");
- }
- | STRING {
- map->m_src = S_FILE;
- if (strlcpy(map->m_config, $1, sizeof(map->m_config))
- >= sizeof(map->m_config))
- err(1, "pathname too long");
- }
- | SOURCE DB STRING {
- map->m_src = S_DB;
- if (strlcpy(map->m_config, $3, sizeof(map->m_config))
- >= sizeof(map->m_config))
- err(1, "pathname too long");
- }
-/*
- | SOURCE LDAP STRING {
- map->m_src = S_LDAP;
- if (strlcpy(map->m_config, $3, sizeof(map->m_config))
- >= sizeof(map->m_config))
- err(1, "pathname too long");
- }
-*/
;
-mapopt : mapsource { }
+table : TABLE STRING STRING {
+ char *p, *backend, *config;
-map : MAP STRING {
- map = map_create(S_NONE, $2);
- free($2);
- } optlbracket mapopt optrbracket {
- if (map->m_src == S_NONE) {
- yyerror("map %s has no source defined", $2);
- free(map);
- map = NULL;
+ p = $3;
+ if (*p == '/') {
+ backend = "static";
+ config = $3;
+ }
+ else {
+ backend = $3;
+ config = NULL;
+ for (p = $3; *p && *p != ':'; p++)
+ ;
+ if (*p == ':') {
+ *p = '\0';
+ backend = $3;
+ config = p+1;
+ }
+ }
+ if (config != NULL && *config != '/') {
+ yyerror("invalid backend parameter for table: %s",
+ $2);
+ free($2);
+ free($3);
YYERROR;
}
- map = NULL;
+ table = table_create(backend, $2, config);
+ if (! table->t_backend->config(table, config)) {
+ yyerror("invalid backend configuration for table %s",
+ table->t_name);
+ free($2);
+ free($3);
+ YYERROR;
+ }
+ free($2);
+ free($3);
+ }
+ | TABLE STRING {
+ table = table_create("static", $2, NULL);
+ free($2);
+ } '{' tableval_list '}' {
+ table = NULL;
}
;
keyval : STRING ARROW STRING {
- map_add(map, $1, $3);
+ table->t_type = T_HASH;
+ table_add(table, $1, $3);
free($1);
free($3);
}
@@ -493,7 +534,8 @@ keyval_list : keyval
;
stringel : STRING {
- map_add(map, $1, NULL);
+ table->t_type = T_LIST;
+ table_add(table, $1, NULL);
free($1);
}
;
@@ -502,245 +544,247 @@ string_list : stringel
| stringel comma string_list
;
-mapref : STRING {
- struct map *m;
+tableval_list : string_list { }
+ | keyval_list { }
+ ;
- m = map_create(S_NONE, NULL);
- map_add(m, $1, NULL);
- $$ = m->m_id;
- }
- | '(' {
- map = map_create(S_NONE, NULL);
- } string_list ')' {
- $$ = map->m_id;
+tablenew : STRING {
+ struct table *t;
+
+ t = table_create("static", NULL, NULL);
+ t->t_type = T_LIST;
+ table_add(t, $1, NULL);
+ free($1);
+ $$ = t->t_id;
+ table = table_create("static", NULL, NULL);
}
| '{' {
- map = map_create(S_NONE, NULL);
- } keyval_list '}' {
- $$ = map->m_id;
+ table = table_create("static", NULL, NULL);
+ } tableval_list '}' {
+ $$ = table->t_id;
}
- | MAP STRING {
- struct map *m;
+ ;
- if ((m = map_findbyname($2)) == NULL) {
- yyerror("no such map: %s", $2);
+tableref : '<' STRING '>' {
+ struct table *t;
+
+ if ((t = table_findbyname($2)) == NULL) {
+ yyerror("no such table: %s", $2);
free($2);
YYERROR;
}
free($2);
- $$ = m->m_id;
+ $$ = t->t_id;
}
;
-alias : ALIAS STRING { $$ = $2; }
- | /* empty */ { $$ = NULL; }
+tables : tablenew { $$ = $1; }
+ | tableref { $$ = $1; }
;
-condition : DOMAIN mapref alias {
- struct cond *c;
- struct map *m;
+alias : ALIAS tables {
+ struct table *t = table_find($2);
- if ($3) {
- if ((m = map_findbyname($3)) == NULL) {
- yyerror("no such map: %s", $3);
- free($3);
- YYERROR;
- }
- rule->r_amap = m->m_id;
+ if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
+ yyerror("invalid use of table \"%s\" as ALIAS parameter",
+ t->t_name);
+ YYERROR;
}
- c = xcalloc(1, sizeof *c, "parse condition: DOMAIN");
- c->c_type = COND_DOM;
- c->c_map = $2;
- $$ = c;
+ $$ = t->t_id;
}
- | VIRTUAL mapref {
- struct cond *c;
- struct map *m;
+ ;
- m = map_find($2);
- if (m->m_src == S_NONE) {
- yyerror("virtual parameter MUST be a map");
+virtual : VIRTUAL tables {
+ struct table *t = table_find($2);
+
+ if (! table_check_service(t, K_ALIAS)) {
+ yyerror("invalid use of table \"%s\" as VIRTUAL parameter",
+ t->t_name);
YYERROR;
}
- c = xcalloc(1, sizeof *c, "parse condition: VIRTUAL");
- c->c_type = COND_VDOM;
- c->c_map = $2;
- $$ = c;
+ $$ = t->t_id;
}
- | LOCAL alias {
- struct cond *c;
- struct map *m;
- char hostname[MAXHOSTNAMELEN];
+ ;
- if (gethostname(hostname, sizeof hostname) == -1) {
- yyerror("gethostname() failed");
- YYERROR;
- }
+usermapping : alias {
+ rule->r_desttype = DEST_DOM;
+ $$ = $1;
+ }
+ | virtual {
+ rule->r_desttype = DEST_VDOM;
+ $$ = $1;
+ }
+ | /**/ {
+ rule->r_desttype = DEST_DOM;
+ $$ = 0;
+ }
+ ;
- if ($2) {
- if ((m = map_findbyname($2)) == NULL) {
- yyerror("no such map: %s", $2);
- free($2);
- YYERROR;
- }
- rule->r_amap = m->m_id;
+userbase : USERS tables {
+ struct table *t = table_find($2);
+
+ if (! table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) {
+ yyerror("invalid use of table \"%s\" as USERS parameter",
+ t->t_name);
+ YYERROR;
}
- m = map_create(S_NONE, NULL);
- map_add(m, "localhost", NULL);
- map_add(m, hostname, NULL);
+ $$ = t->t_id;
+ }
+ | /**/ { $$ = table_findbyname("<getpwnam>")->t_id; }
+ ;
- c = xcalloc(1, sizeof *c, "parse condition: LOCAL");
- c->c_type = COND_DOM;
- c->c_map = m->m_id;
+
- $$ = c;
- }
- | ANY alias {
- struct cond *c;
- struct map *m;
- c = xcalloc(1, sizeof *c, "parse condition: ANY");
- c->c_type = COND_ANY;
+destination : DOMAIN tables {
+ struct table *t = table_find($2);
- if ($2) {
- if ((m = map_findbyname($2)) == NULL) {
- yyerror("no such map: %s", $2);
- free($2);
- YYERROR;
- }
- rule->r_amap = m->m_id;
+ if (! table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
+ yyerror("invalid use of table \"%s\" as DOMAIN parameter",
+ t->t_name);
+ YYERROR;
}
- $$ = c;
+
+ $$ = t->t_id;
}
+ | LOCAL { $$ = table_findbyname("<localnames>")->t_id; }
+ | ANY { $$ = 0; }
;
-condition_list : condition comma condition_list {
- TAILQ_INSERT_TAIL(conditions, $1, c_entry);
- }
- | condition {
- TAILQ_INSERT_TAIL(conditions, $1, c_entry);
+relay_source : SOURCE tables {
+ struct table *t = table_find($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;
+ }
+ $$ = t->t_name;
}
+ | { $$ = NULL; }
;
-conditions : condition {
- TAILQ_INSERT_TAIL(conditions, $1, c_entry);
+relay_helo : HELO tables {
+ struct table *t = table_find($2);
+ if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
+ yyerror("invalid use of table \"%s\" as "
+ "HELO parameter", t->t_name);
+ YYERROR;
+ }
+ $$ = t->t_name;
}
- | '{' condition_list '}'
+ | { $$ = NULL; }
+ ;
+
+relay_backup : BACKUP STRING { $$ = $2; }
+ | BACKUP { $$ = NULL; }
;
relay_as : AS STRING {
struct mailaddr maddr, *maddrp;
- char *p;
- bzero(&maddr, sizeof (maddr));
-
- p = strrchr($2, '@');
- if (p == NULL) {
- if (strlcpy(maddr.user, $2, sizeof (maddr.user))
- >= sizeof (maddr.user))
- yyerror("user-part too long");
- free($2);
- YYERROR;
- }
- else {
- if (p == $2) {
- /* domain only */
- p++;
- if (strlcpy(maddr.domain, p, sizeof (maddr.domain))
- >= sizeof (maddr.domain)) {
- yyerror("user-part too long");
- free($2);
- YYERROR;
- }
- }
- else {
- *p++ = '\0';
- if (strlcpy(maddr.user, $2, sizeof (maddr.user))
- >= sizeof (maddr.user)) {
- yyerror("user-part too long");
- free($2);
- YYERROR;
- }
- if (strlcpy(maddr.domain, p, sizeof (maddr.domain))
- >= sizeof (maddr.domain)) {
- yyerror("domain-part too long");
- free($2);
- YYERROR;
- }
- }
+ 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 'relay as' value");
- free($2);
+ yyerror("invalid empty parameter to AS");
YYERROR;
}
-
- if (maddr.domain[0] == '\0') {
+ else if (maddr.domain[0] == '\0') {
if (strlcpy(maddr.domain, conf->sc_hostname,
sizeof (maddr.domain))
>= sizeof (maddr.domain)) {
- fatalx("domain too long");
- yyerror("domain-part too long");
- free($2);
+ yyerror("hostname too long for AS parameter: %s",
+ conf->sc_hostname);
YYERROR;
}
}
-
- maddrp = xmemdup(&maddr, sizeof (*maddrp), "parse relay_as: AS");
- free($2);
-
- $$ = maddrp;
+ $$ = xmemdup(&maddr, sizeof (*maddrp), "parse relay_as: AS");
}
| /* empty */ { $$ = NULL; }
;
-action : DELIVER TO MAILDIR {
+action : userbase DELIVER TO MAILDIR {
+ rule->r_users = table_find($1);
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 {
+ | userbase DELIVER TO MAILDIR STRING {
+ rule->r_users = table_find($1);
rule->r_action = A_MAILDIR;
- if (strlcpy(rule->r_value.buffer, $4,
+ if (strlcpy(rule->r_value.buffer, $5,
sizeof(rule->r_value.buffer)) >=
sizeof(rule->r_value.buffer))
fatal("pathname too long");
- free($4);
+ free($5);
}
- | DELIVER TO MBOX {
+ | userbase DELIVER TO MBOX {
+ rule->r_users = table_find($1);
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 MDA STRING {
+ | userbase DELIVER TO MDA STRING {
+ rule->r_users = table_find($1);
rule->r_action = A_MDA;
- if (strlcpy(rule->r_value.buffer, $4,
+ if (strlcpy(rule->r_value.buffer, $5,
sizeof(rule->r_value.buffer))
>= sizeof(rule->r_value.buffer))
fatal("command too long");
- free($4);
+ free($5);
}
- | RELAY relay_as {
+ | RELAY relay_as relay_source relay_helo {
rule->r_action = A_RELAY;
rule->r_as = $2;
- }
- | RELAY BACKUP STRING relay_as {
+ if ($4 != NULL && $3 == NULL) {
+ yyerror("HELO can only be used with SOURCE");
+ YYERROR;
+ }
+ if ($3)
+ strlcpy(rule->r_value.relayhost.sourcetable, $3,
+ sizeof rule->r_value.relayhost.sourcetable);
+ if ($4)
+ strlcpy(rule->r_value.relayhost.helotable, $4,
+ sizeof rule->r_value.relayhost.helotable);
+ }
+ | RELAY relay_backup relay_as relay_source relay_helo {
rule->r_action = A_RELAY;
- rule->r_as = $4;
+ rule->r_as = $3;
rule->r_value.relayhost.flags |= F_BACKUP;
- strlcpy(rule->r_value.relayhost.hostname, $3,
- sizeof (rule->r_value.relayhost.hostname));
- free($3);
+
+ if ($2)
+ strlcpy(rule->r_value.relayhost.hostname, $2,
+ sizeof (rule->r_value.relayhost.hostname));
+ else
+ strlcpy(rule->r_value.relayhost.hostname, env->sc_hostname,
+ sizeof (rule->r_value.relayhost.hostname));
+ free($2);
+
+ if ($5 != NULL && $4 == NULL) {
+ yyerror("HELO can only be used with SOURCE");
+ YYERROR;
+ }
+ if ($4)
+ strlcpy(rule->r_value.relayhost.sourcetable, $4,
+ sizeof rule->r_value.relayhost.sourcetable);
+ if ($5)
+ strlcpy(rule->r_value.relayhost.helotable, $5,
+ sizeof rule->r_value.relayhost.helotable);
}
- | RELAY VIA STRING certname credentials relay_as {
+ | RELAY VIA STRING certificate credentials relay_as relay_source relay_helo {
+ struct table *t;
+
rule->r_action = A_RELAYVIA;
rule->r_as = $6;
@@ -748,7 +792,6 @@ action : DELIVER TO MAILDIR {
yyerror("error: invalid url: %s", $3);
free($3);
free($4);
- free($5);
free($6);
YYERROR;
}
@@ -756,147 +799,134 @@ action : DELIVER TO MAILDIR {
/* no worries, F_AUTH cant be set without SSL */
if (rule->r_value.relayhost.flags & F_AUTH) {
- if ($5 == NULL) {
- yyerror("error: auth without authmap");
+ if (! $5) {
+ yyerror("error: auth without auth table");
free($4);
- free($5);
free($6);
YYERROR;
}
- strlcpy(rule->r_value.relayhost.authmap, $5,
- sizeof(rule->r_value.relayhost.authmap));
+ t = table_find($5);
+ strlcpy(rule->r_value.relayhost.authtable, t->t_name,
+ sizeof(rule->r_value.relayhost.authtable));
}
- free($5);
-
if ($4 != NULL) {
- if (ssl_load_certfile($4, F_CCERT) < 0) {
- yyerror("cannot load certificate: %s",
- $4);
- free($4);
- free($6);
- YYERROR;
- }
if (strlcpy(rule->r_value.relayhost.cert, $4,
sizeof(rule->r_value.relayhost.cert))
>= sizeof(rule->r_value.relayhost.cert))
fatal("certificate path too long");
}
free($4);
+ if ($8 != NULL && $7 == NULL) {
+ yyerror("HELO can only be used with SOURCE");
+ YYERROR;
+ }
+ if ($7)
+ strlcpy(rule->r_value.relayhost.sourcetable, $7,
+ sizeof rule->r_value.relayhost.sourcetable);
+ if ($8)
+ strlcpy(rule->r_value.relayhost.helotable, $8,
+ sizeof rule->r_value.relayhost.helotable);
}
;
-from : FROM mapref {
- $$ = $2;
+from : FROM tables {
+ struct table *t = table_find($2);
+
+ if (! table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) {
+ yyerror("invalid use of table \"%s\" as FROM parameter",
+ t->t_name);
+ YYERROR;
+ }
+
+ $$ = t->t_id;
}
| FROM ANY {
- $$ = map_findbyname("<anyhost>")->m_id;
+ $$ = table_findbyname("<anyhost>")->t_id;
}
| FROM LOCAL {
- $$ = map_findbyname("<localhost>")->m_id;
+ $$ = table_findbyname("<localhost>")->t_id;
}
| /* empty */ {
- $$ = map_findbyname("<localhost>")->m_id;
+ $$ = table_findbyname("<localhost>")->t_id;
}
;
-on : ON STRING {
- if (strlen($2) >= MAX_TAG_SIZE) {
- yyerror("interface, address or tag name too long");
- free($2);
+sender : SENDER tables {
+ struct table *t = table_find($2);
+
+ if (! table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
+ yyerror("invalid use of table \"%s\" as SENDER parameter",
+ t->t_name);
YYERROR;
}
- $$ = $2;
+ $$ = t->t_id;
}
- | /* empty */ { $$ = NULL; }
+ | /* empty */ { $$ = 0; }
;
-rule : ACCEPT on from {
-
+rule : ACCEPT {
rule = xcalloc(1, sizeof(*rule), "parse rule: ACCEPT");
- rule->r_decision = R_ACCEPT;
- rule->r_sources = map_find($3);
-
- conditions = xcalloc(1, sizeof(*conditions),
- "parse rule: ACCEPT");
-
- if ($2)
- (void)strlcpy(rule->r_tag, $2, sizeof(rule->r_tag));
- free($2);
+ } tagged from sender FOR destination usermapping action expire {
- TAILQ_INIT(conditions);
-
- } FOR conditions action tag expire {
- struct rule *subr;
- struct cond *cond;
-
- if ($8)
- (void)strlcpy(rule->r_tag, $8, sizeof(rule->r_tag));
- free($8);
-
- rule->r_qexpire = $9;
-
- while ((cond = TAILQ_FIRST(conditions)) != NULL) {
-
- subr = xmemdup(rule, sizeof(*subr), "parse rule: FOR");
-
- subr->r_condition = *cond;
-
- TAILQ_REMOVE(conditions, cond, c_entry);
- TAILQ_INSERT_TAIL(conf->sc_rules, subr, r_entry);
-
- free(cond);
+ rule->r_decision = R_ACCEPT;
+ rule->r_sources = table_find($4);
+ rule->r_senders = table_find($5);
+ rule->r_destination = table_find($7);
+ rule->r_mapping = table_find($8);
+ if ($3) {
+ 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_qexpire = $10;
- if (rule->r_amap) {
- if (rule->r_action == A_RELAY ||
- rule->r_action == A_RELAYVIA) {
- yyerror("aliases set on a relay rule");
- free(conditions);
- free(rule);
+ if (rule->r_mapping && rule->r_desttype == DEST_VDOM) {
+ enum table_type type;
+
+ switch (rule->r_action) {
+ case A_RELAY:
+ case A_RELAYVIA:
+ type = T_LIST;
+ break;
+ default:
+ type = T_HASH;
+ break;
+ }
+ if (! table_check_type(rule->r_mapping, type)) {
+ yyerror("invalid use of table \"%s\" as VIRTUAL parameter",
+ rule->r_mapping->t_name);
YYERROR;
}
}
- free(conditions);
- free(rule);
- conditions = NULL;
+ TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
+
rule = NULL;
}
- | REJECT on from {
-
+ | REJECT {
rule = xcalloc(1, sizeof(*rule), "parse rule: REJECT");
+ } tagged from sender FOR destination usermapping {
rule->r_decision = R_REJECT;
- rule->r_sources = map_find($3);
-
- conditions = xcalloc(1, sizeof(*conditions),
- "parse rule: REJECT");
-
- if ($2)
- (void)strlcpy(rule->r_tag, $2, sizeof(rule->r_tag));
- free($2);
-
- TAILQ_INIT(conditions);
-
- } FOR conditions {
- struct rule *subr;
- struct cond *cond;
-
- while ((cond = TAILQ_FIRST(conditions)) != NULL) {
-
- subr = xmemdup(rule, sizeof(*subr), "parse rule: FOR");
-
- subr->r_condition = *cond;
-
- TAILQ_REMOVE(conditions, cond, c_entry);
- TAILQ_INSERT_TAIL(conf->sc_rules, subr, r_entry);
-
- free(cond);
+ rule->r_sources = table_find($4);
+ rule->r_sources = table_find($5);
+ rule->r_destination = table_find($7);
+ rule->r_mapping = table_find($8);
+ if ($3) {
+ 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);
}
- free(conditions);
- free(rule);
- conditions = NULL;
+ TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
rule = NULL;
}
;
@@ -939,43 +969,41 @@ lookup(char *s)
{ "auth", AUTH },
{ "auth-optional", AUTH_OPTIONAL },
{ "backup", BACKUP },
+ { "bounce-warn", BOUNCEWARN },
{ "certificate", CERTIFICATE },
{ "compression", COMPRESSION },
- { "db", DB },
{ "deliver", DELIVER },
{ "domain", DOMAIN },
{ "expire", EXPIRE },
- { "file", FILE },
{ "filter", FILTER },
{ "for", FOR },
{ "from", FROM },
- { "hash", HASH },
+ { "helo", HELO },
{ "hostname", HOSTNAME },
{ "include", INCLUDE },
{ "key", KEY },
- { "ldap", LDAP },
- { "list", LIST },
{ "listen", LISTEN },
{ "local", LOCAL },
{ "maildir", MAILDIR },
- { "map", MAP },
+ { "max-message-size", MAXMESSAGESIZE },
{ "mbox", MBOX },
{ "mda", MDA },
{ "on", ON },
- { "plain", FILE },
{ "port", PORT },
{ "queue", QUEUE },
{ "reject", REJECT },
{ "relay", RELAY },
- { "single", SINGLE },
- { "size", SIZE },
+ { "sender", SENDER },
{ "smtps", SMTPS },
{ "source", SOURCE },
{ "ssl", SSL },
+ { "table", TABLE },
{ "tag", TAG },
+ { "tagged", TAGGED },
{ "tls", TLS },
{ "tls-require", TLS_REQUIRE },
{ "to", TO },
+ { "users", USERS },
{ "via", VIA },
{ "virtual", VIRTUAL },
};
@@ -1305,44 +1333,56 @@ popfile(void)
int
parse_config(struct smtpd *x_conf, const char *filename, int opts)
{
- struct sym *sym, *next;
+ struct sym *sym, *next;
+ struct table *t;
+ char hostname[MAXHOSTNAMELEN];
+
+ if (gethostname(hostname, sizeof hostname) == -1) {
+ fprintf(stderr, "invalid hostname: gethostname() failed\n");
+ return (-1);
+ }
conf = x_conf;
bzero(conf, sizeof(*conf));
conf->sc_maxsize = DEFAULT_MAX_BODY_SIZE;
- conf->sc_maps = calloc(1, sizeof(*conf->sc_maps));
+ conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict));
+ conf->sc_tables_tree = calloc(1, sizeof(*conf->sc_tables_tree));
conf->sc_rules = calloc(1, sizeof(*conf->sc_rules));
conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners));
- conf->sc_ssl = calloc(1, sizeof(*conf->sc_ssl));
- conf->sc_filters = calloc(1, sizeof(*conf->sc_filters));
-
- if (conf->sc_maps == NULL ||
- conf->sc_rules == NULL ||
- conf->sc_listeners == NULL ||
- conf->sc_ssl == NULL ||
- conf->sc_filters == NULL) {
+ conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict));
+
+ /* Report mails delayed for more than 4 hours */
+ conf->sc_bounce_warn[0] = 3600 * 4;
+
+ if (conf->sc_tables_dict == NULL ||
+ conf->sc_tables_tree == NULL ||
+ conf->sc_rules == NULL ||
+ conf->sc_listeners == NULL ||
+ conf->sc_ssl_dict == NULL) {
log_warn("warn: cannot allocate memory");
- free(conf->sc_maps);
+ free(conf->sc_tables_dict);
+ free(conf->sc_tables_tree);
free(conf->sc_rules);
free(conf->sc_listeners);
- free(conf->sc_ssl);
- free(conf->sc_filters);
+ free(conf->sc_ssl_dict);
return (-1);
}
errors = 0;
- map = NULL;
+ table = NULL;
rule = NULL;
+ dict_init(&conf->sc_filters);
+
+ dict_init(conf->sc_ssl_dict);
+ dict_init(conf->sc_tables_dict);
+ tree_init(conf->sc_tables_tree);
+
TAILQ_INIT(conf->sc_listeners);
- TAILQ_INIT(conf->sc_maps);
TAILQ_INIT(conf->sc_rules);
- TAILQ_INIT(conf->sc_filters);
- SPLAY_INIT(conf->sc_ssl);
- SPLAY_INIT(&conf->sc_sessions);
conf->sc_qexpire = SMTPD_QUEUE_EXPIRY;
conf->sc_opts = opts;
@@ -1354,10 +1394,17 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
topfile = file;
/*
- * declare special "localhost" and "anyhost" maps
+ * declare special "localhost", "anyhost" and "localnames" tables
*/
set_localaddrs();
+ t = table_create("static", "<localnames>", NULL);
+ t->t_type = T_LIST;
+ table_add(t, "localhost", NULL);
+ table_add(t, hostname, NULL);
+
+ table_create("getpwnam", "<getpwnam>", NULL);
+
/*
* parse configuration
*/
@@ -1583,10 +1630,13 @@ host_dns(const char *s, const char *tag, const char *cert,
int
host(const char *s, const char *tag, const char *cert, struct listenerlist *al,
- int max, in_port_t port, uint8_t flags)
+ int max, in_port_t port, const char *authtable, uint8_t flags,
+ const char *helo)
{
struct listener *h;
+ port = htons(port);
+
h = host_v4(s, port);
/* IPv6 address? */
@@ -1596,12 +1646,19 @@ host(const char *s, const char *tag, const char *cert, struct listenerlist *al,
if (h != NULL) {
h->port = port;
h->flags = flags;
+ if (h->flags & F_SSL)
+ if (cert == NULL)
+ cert = s;
h->ssl = NULL;
h->ssl_cert_name[0] = '\0';
+ if (authtable != NULL)
+ (void)strlcpy(h->authtable, authtable, sizeof(h->authtable));
if (cert != NULL)
(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
if (tag != NULL)
(void)strlcpy(h->tag, tag, sizeof(h->tag));
+ if (helo != NULL)
+ (void)strlcpy(h->helo, helo, sizeof(h->helo));
TAILQ_INSERT_HEAD(al, h, entry);
return (1);
@@ -1612,7 +1669,8 @@ host(const char *s, const char *tag, const char *cert, struct listenerlist *al,
int
interface(const char *s, const char *tag, const char *cert,
- struct listenerlist *al, int max, in_port_t port, uint8_t flags)
+ struct listenerlist *al, int max, in_port_t port, const char *authtable, uint8_t flags,
+ const char *helo)
{
struct ifaddrs *ifap, *p;
struct sockaddr_in *sain;
@@ -1620,6 +1678,8 @@ interface(const char *s, const char *tag, const char *cert,
struct listener *h;
int ret = 0;
+ port = htons(port);
+
if (getifaddrs(&ifap) == -1)
fatal("getifaddrs");
@@ -1655,13 +1715,19 @@ interface(const char *s, const char *tag, const char *cert,
h->fd = -1;
h->port = port;
h->flags = flags;
+ if (h->flags & F_SSL)
+ if (cert == NULL)
+ cert = s;
h->ssl = NULL;
h->ssl_cert_name[0] = '\0';
+ if (authtable != NULL)
+ (void)strlcpy(h->authtable, authtable, sizeof(h->authtable));
if (cert != NULL)
(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
if (tag != NULL)
(void)strlcpy(h->tag, tag, sizeof(h->tag));
-
+ if (helo != NULL)
+ (void)strlcpy(h->helo, helo, sizeof(h->helo));
ret = 1;
TAILQ_INSERT_HEAD(al, h, entry);
}
@@ -1678,18 +1744,18 @@ set_localaddrs(void)
struct sockaddr_storage ss;
struct sockaddr_in *sain;
struct sockaddr_in6 *sin6;
- struct map *m;
+ struct table *t;
- m = map_create(S_NONE, "<anyhost>");
- map_add(m, "local", NULL);
- map_add(m, "0.0.0.0/0", NULL);
- map_add(m, "::/0", NULL);
+ t = table_create("static", "<anyhost>", NULL);
+ table_add(t, "local", NULL);
+ table_add(t, "0.0.0.0/0", NULL);
+ table_add(t, "::/0", NULL);
if (getifaddrs(&ifap) == -1)
fatal("getifaddrs");
- m = map_create(S_NONE, "<localhost>");
- map_add(m, "local", NULL);
+ t = table_create("static", "<localhost>", NULL);
+ table_add(t, "local", NULL);
for (p = ifap; p != NULL; p = p->ifa_next) {
if (p->ifa_addr == NULL)
@@ -1699,14 +1765,14 @@ set_localaddrs(void)
sain = (struct sockaddr_in *)&ss;
*sain = *(struct sockaddr_in *)p->ifa_addr;
sain->sin_len = sizeof(struct sockaddr_in);
- map_add(m, ss_to_text(&ss), NULL);
+ table_add(t, ss_to_text(&ss), NULL);
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&ss;
*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
sin6->sin6_len = sizeof(struct sockaddr_in6);
- map_add(m, ss_to_text(&ss), NULL);
+ table_add(t, ss_to_text(&ss), NULL);
break;
}
}