xref: /openbsd/usr.sbin/smtpd/parse.y (revision cb1bbb86)
1 /*	$OpenBSD: parse.y,v 1.138 2014/03/25 10:28:58 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
5  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
7  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
8  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
9  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
10  *
11  * Permission to use, copy, modify, and distribute this software for any
12  * purpose with or without fee is hereby granted, provided that the above
13  * copyright notice and this permission notice appear in all copies.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  */
23 
24 %{
25 #include <sys/types.h>
26 #include <sys/queue.h>
27 #include <sys/tree.h>
28 #include <sys/socket.h>
29 #include <sys/stat.h>
30 #include <sys/ioctl.h>
31 
32 #include <net/if.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <event.h>
40 #include <ifaddrs.h>
41 #include <imsg.h>
42 #include <inttypes.h>
43 #include <netdb.h>
44 #include <paths.h>
45 #include <pwd.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <syslog.h>
50 #include <unistd.h>
51 #include <util.h>
52 
53 #include <openssl/ssl.h>
54 
55 #include "smtpd.h"
56 #include "ssl.h"
57 #include "log.h"
58 
59 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
60 static struct file {
61 	TAILQ_ENTRY(file)	 entry;
62 	FILE			*stream;
63 	char			*name;
64 	int			 lineno;
65 	int			 errors;
66 } *file, *topfile;
67 struct file	*pushfile(const char *, int);
68 int		 popfile(void);
69 int		 check_file_secrecy(int, const char *);
70 int		 yyparse(void);
71 int		 yylex(void);
72 int		 kw_cmp(const void *, const void *);
73 int		 lookup(char *);
74 int		 lgetc(int);
75 int		 lungetc(int);
76 int		 findeol(void);
77 int		 yyerror(const char *, ...)
78     __attribute__ ((format (printf, 1, 2)));
79 
80 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
81 struct sym {
82 	TAILQ_ENTRY(sym)	 entry;
83 	int			 used;
84 	int			 persist;
85 	char			*nam;
86 	char			*val;
87 };
88 int		 symset(const char *, const char *, int);
89 char		*symget(const char *);
90 
91 struct smtpd		*conf = NULL;
92 static int		 errors = 0;
93 
94 struct filter		*filter = NULL;
95 struct table		*table = NULL;
96 struct rule		*rule = NULL;
97 struct listener		 l;
98 struct mta_limits	*limits;
99 static struct pki	*pki;
100 
101 static struct listen_opts {
102 	char	       *ifx;
103 	int		family;
104 	in_port_t	port;
105 	uint16_t	ssl;
106 	char	       *pki;
107 	uint16_t       	auth;
108 	struct table   *authtable;
109 	char	       *tag;
110 	char	       *hostname;
111 	struct table   *hostnametable;
112 	uint16_t	flags;
113 } listen_opts;
114 
115 static void	create_listener(struct listenerlist *,  struct listen_opts *);
116 static void	config_listener(struct listener *,  struct listen_opts *);
117 
118 struct listener	*host_v4(const char *, in_port_t);
119 struct listener	*host_v6(const char *, in_port_t);
120 int		 host_dns(struct listenerlist *, struct listen_opts *);
121 int		 host(struct listenerlist *, struct listen_opts *);
122 int		 interface(struct listenerlist *, struct listen_opts *);
123 void		 set_localaddrs(void);
124 int		 delaytonum(char *);
125 int		 is_if_in_group(const char *, const char *);
126 
127 static struct filter	*create_filter(const char *, const char *);
128 static struct filter	*create_filter_chain(const char *);
129 static int		 extend_filter_chain(struct filter *, const char *);
130 
131 typedef struct {
132 	union {
133 		int64_t		 number;
134 		struct table	*table;
135 		char		*string;
136 		struct host	*host;
137 		struct mailaddr	*maddr;
138 	} v;
139 	int lineno;
140 } YYSTYPE;
141 
142 %}
143 
144 %token	AS QUEUE COMPRESSION ENCRYPTION MAXMESSAGESIZE MAXMTADEFERRED LISTEN ON ANY PORT EXPIRE
145 %token	TABLE SECURE SMTPS CERTIFICATE DOMAIN BOUNCEWARN LIMIT INET4 INET6
146 %token  RELAY BACKUP VIA DELIVER TO LMTP MAILDIR MBOX HOSTNAME HOSTNAMES
147 %token	ACCEPT REJECT INCLUDE ERROR MDA FROM FOR SOURCE MTA PKI SCHEDULER
148 %token	ARROW AUTH TLS LOCAL VIRTUAL TAG TAGGED ALIAS FILTER FILTERCHAIN KEY CA DHPARAMS
149 %token	AUTH_OPTIONAL TLS_REQUIRE USERBASE SENDER MASK_SOURCE VERIFY FORWARDONLY RECIPIENT
150 %token	<v.string>	STRING
151 %token  <v.number>	NUMBER
152 %type	<v.table>	table
153 %type	<v.number>	size negation
154 %type	<v.table>	tables tablenew tableref alias virtual userbase
155 %type	<v.string>	tagged
156 %%
157 
158 grammar		: /* empty */
159 		| grammar '\n'
160 		| grammar include '\n'
161 		| grammar varset '\n'
162 		| grammar main '\n'
163 		| grammar table '\n'
164 		| grammar rule '\n'
165 		| grammar error '\n'		{ file->errors++; }
166 		;
167 
168 include		: INCLUDE STRING		{
169 			struct file	*nfile;
170 
171 			if ((nfile = pushfile($2, 0)) == NULL) {
172 				yyerror("failed to include file %s", $2);
173 				free($2);
174 				YYERROR;
175 			}
176 			free($2);
177 
178 			file = nfile;
179 			lungetc('\n');
180 		}
181 		;
182 
183 varset		: STRING '=' STRING		{
184 			if (symset($1, $3, 0) == -1)
185 				fatal("cannot store variable");
186 			free($1);
187 			free($3);
188 		}
189 		;
190 
191 comma		: ','
192 		| nl
193 		| /* empty */
194 		;
195 
196 optnl		: '\n' optnl
197 		|
198 		;
199 
200 nl		: '\n' optnl
201 		;
202 
203 size		: NUMBER		{
204 			if ($1 < 0) {
205 				yyerror("invalid size: %" PRId64, $1);
206 				YYERROR;
207 			}
208 			$$ = $1;
209 		}
210 		| STRING			{
211 			long long result;
212 
213 			if (scan_scaled($1, &result) == -1 || result < 0) {
214 				yyerror("invalid size: %s", $1);
215 				free($1);
216 				YYERROR;
217 			}
218 			free($1);
219 			$$ = result;
220 		}
221 		;
222 
223 tagged		: TAGGED negation STRING       		{
224 			if (strlcpy(rule->r_tag, $3, sizeof rule->r_tag)
225 			    >= sizeof rule->r_tag) {
226 				yyerror("tag name too long: %s", $3);
227 				free($3);
228 				YYERROR;
229 			}
230 			free($3);
231 			rule->r_nottag = $2;
232 		}
233 		;
234 
235 bouncedelay	: STRING {
236 			time_t	d;
237 			int	i;
238 
239 			d = delaytonum($1);
240 			if (d < 0) {
241 				yyerror("invalid bounce delay: %s", $1);
242 				free($1);
243 				YYERROR;
244 			}
245 			free($1);
246 			for (i = 0; i < MAX_BOUNCE_WARN; i++) {
247 				if (conf->sc_bounce_warn[i] != 0)
248 					continue;
249 				conf->sc_bounce_warn[i] = d;
250 				break;
251 			}
252 		}
253 
254 bouncedelays	: bouncedelays ',' bouncedelay
255 		| bouncedelay
256 		| /* EMPTY */
257 		;
258 
259 opt_limit_mda	: STRING NUMBER {
260 			if (!strcmp($1, "max-session")) {
261 				conf->sc_mda_max_session = $2;
262 			}
263 			else if (!strcmp($1, "max-session-per-user")) {
264 				conf->sc_mda_max_user_session = $2;
265 			}
266 			else if (!strcmp($1, "task-lowat")) {
267 				conf->sc_mda_task_lowat = $2;
268 			}
269 			else if (!strcmp($1, "task-hiwat")) {
270 				conf->sc_mda_task_hiwat = $2;
271 			}
272 			else if (!strcmp($1, "task-release")) {
273 				conf->sc_mda_task_release = $2;
274 			}
275 			else {
276 				yyerror("invalid scheduler limit keyword: %s", $1);
277 				free($1);
278 				YYERROR;
279 			}
280 			free($1);
281 		}
282 		;
283 
284 limits_mda	: opt_limit_mda limits_mda
285 		| /* empty */
286 		;
287 
288 opt_limit_mta	: INET4 {
289 			limits->family = AF_INET;
290 		}
291 		| INET6 {
292 			limits->family = AF_INET6;
293 		}
294 		| STRING NUMBER {
295 			if (!limit_mta_set(limits, $1, $2)) {
296 				yyerror("invalid mta limit keyword: %s", $1);
297 				free($1);
298 				YYERROR;
299 			}
300 			free($1);
301 		}
302 		;
303 
304 limits_mta	: opt_limit_mta limits_mta
305 		| /* empty */
306 		;
307 
308 opt_limit_scheduler : STRING NUMBER {
309 			if (!strcmp($1, "max-inflight")) {
310 				conf->sc_scheduler_max_inflight = $2;
311 			}
312 			else if (!strcmp($1, "max-evp-batch-size")) {
313 				conf->sc_scheduler_max_evp_batch_size = $2;
314 			}
315 			else if (!strcmp($1, "max-msg-batch-size")) {
316 				conf->sc_scheduler_max_msg_batch_size = $2;
317 			}
318 			else if (!strcmp($1, "max-schedule")) {
319 				conf->sc_scheduler_max_schedule = $2;
320 			}
321 			else {
322 				yyerror("invalid scheduler limit keyword: %s", $1);
323 				free($1);
324 				YYERROR;
325 			}
326 			free($1);
327 		}
328 		;
329 
330 limits_scheduler: opt_limit_scheduler limits_scheduler
331 		| /* empty */
332 		;
333 
334 opt_pki		: CERTIFICATE STRING {
335 			pki->pki_cert_file = $2;
336 		}
337 		| KEY STRING {
338 			pki->pki_key_file = $2;
339 		}
340 		| CA STRING {
341 			pki->pki_ca_file = $2;
342 		}
343 		| DHPARAMS STRING {
344 			pki->pki_dhparams_file = $2;
345 		}
346 		;
347 
348 pki		: opt_pki pki
349 		| /* empty */
350 		;
351 
352 opt_listen     	: INET4			{ listen_opts.family = AF_INET; }
353 		| INET6			{ listen_opts.family = AF_INET6; }
354 		| PORT STRING			{
355 			struct servent	*servent;
356 
357 			servent = getservbyname($2, "tcp");
358 			if (servent == NULL) {
359 				yyerror("invalid port: %s", $2);
360 				free($2);
361 				YYERROR;
362 			}
363 			free($2);
364 			listen_opts.port = ntohs(servent->s_port);
365 		}
366 		| PORT NUMBER			{
367 			if ($2 <= 0 || $2 >= (int)USHRT_MAX) {
368 				yyerror("invalid port: %" PRId64, $2);
369 				YYERROR;
370 			}
371 			listen_opts.port = $2;
372 		}
373 		| SMTPS				{ listen_opts.ssl = F_SMTPS; }
374 		| SMTPS VERIFY 			{ listen_opts.ssl = F_SMTPS|F_TLS_VERIFY; }
375 		| TLS				{ listen_opts.ssl = F_STARTTLS; }
376 		| SECURE       			{ listen_opts.ssl = F_SSL; }
377 		| TLS_REQUIRE			{ listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE; }
378 		| TLS_REQUIRE VERIFY   		{ listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE|F_TLS_VERIFY; }
379 		| PKI STRING			{ listen_opts.pki = $2; }
380 		| AUTH				{ listen_opts.auth = F_AUTH|F_AUTH_REQUIRE; }
381 		| AUTH_OPTIONAL			{ listen_opts.auth = F_AUTH; }
382 		| AUTH tables  			{
383 			listen_opts.authtable = $2;
384 			listen_opts.auth = F_AUTH|F_AUTH_REQUIRE;
385 		}
386 		| AUTH_OPTIONAL tables 		{
387 			listen_opts.authtable = $2;
388 			listen_opts.auth = F_AUTH;
389 		}
390 		| TAG STRING			{
391        			if (strlen($2) >= MAX_TAG_SIZE) {
392        				yyerror("tag name too long");
393 				free($2);
394 				YYERROR;
395 			}
396 			listen_opts.tag = $2;
397 		}
398 		| HOSTNAME STRING	{ listen_opts.hostname = $2; }
399 		| HOSTNAMES tables	{
400 			struct table	*t = $2;
401 			if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
402 				yyerror("invalid use of table \"%s\" as "
403 				    "HOSTNAMES parameter", t->t_name);
404 				YYERROR;
405 			}
406 			listen_opts.hostnametable = t;
407 		}
408 		| MASK_SOURCE	{ listen_opts.flags |= F_MASK_SOURCE; }
409 		;
410 
411 listen		: opt_listen listen
412 		| /* empty */
413 		;
414 
415 opt_relay_common: AS STRING	{
416 			struct mailaddr maddr, *maddrp;
417 
418 			if (! text_to_mailaddr(&maddr, $2)) {
419 				yyerror("invalid parameter to AS: %s", $2);
420 				free($2);
421 				YYERROR;
422 			}
423 			free($2);
424 
425 			if (maddr.user[0] == '\0' && maddr.domain[0] == '\0') {
426 				yyerror("invalid empty parameter to AS");
427 				YYERROR;
428 			}
429 			else if (maddr.domain[0] == '\0') {
430 				if (strlcpy(maddr.domain, conf->sc_hostname,
431 					sizeof (maddr.domain))
432 				    >= sizeof (maddr.domain)) {
433 					yyerror("hostname too long for AS parameter: %s",
434 					    conf->sc_hostname);
435 					YYERROR;
436 				}
437 			}
438 			rule->r_as = xmemdup(&maddr, sizeof (*maddrp), "parse relay_as: AS");
439 		}
440 		| SOURCE tables			{
441 			struct table	*t = $2;
442 			if (! table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) {
443 				yyerror("invalid use of table \"%s\" as "
444 				    "SOURCE parameter", t->t_name);
445 				YYERROR;
446 			}
447 			strlcpy(rule->r_value.relayhost.sourcetable, t->t_name,
448 			    sizeof rule->r_value.relayhost.sourcetable);
449 		}
450 		| HOSTNAME STRING {
451 			strlcat(rule->r_value.relayhost.heloname, $2,
452 			    sizeof rule->r_value.relayhost.heloname);
453 			free($2);
454 		}
455 		| HOSTNAMES tables		{
456 			struct table	*t = $2;
457 			if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
458 				yyerror("invalid use of table \"%s\" as "
459 				    "HOSTNAMES parameter", t->t_name);
460 				YYERROR;
461 			}
462 			strlcpy(rule->r_value.relayhost.helotable, t->t_name,
463 			    sizeof rule->r_value.relayhost.helotable);
464 		}
465 		| PKI STRING {
466 			if (! lowercase(rule->r_value.relayhost.pki_name, $2,
467 				sizeof(rule->r_value.relayhost.pki_name))) {
468 				yyerror("pki name too long: %s", $2);
469 				free($2);
470 				YYERROR;
471 			}
472 			if (dict_get(conf->sc_pki_dict,
473 			    rule->r_value.relayhost.pki_name) == NULL) {
474 				log_warnx("pki name not found: %s", $2);
475 				free($2);
476 				YYERROR;
477 			}
478 			free($2);
479 		}
480 		;
481 
482 opt_relay	: BACKUP STRING			{
483 			rule->r_value.relayhost.flags |= F_BACKUP;
484 			strlcpy(rule->r_value.relayhost.hostname, $2,
485 			    sizeof (rule->r_value.relayhost.hostname));
486 		}
487 		| BACKUP       			{
488 			rule->r_value.relayhost.flags |= F_BACKUP;
489 			strlcpy(rule->r_value.relayhost.hostname,
490 			    conf->sc_hostname,
491 			    sizeof (rule->r_value.relayhost.hostname));
492 		}
493 		| TLS       			{
494 			rule->r_value.relayhost.flags |= F_STARTTLS;
495 		}
496 		| TLS VERIFY			{
497 			rule->r_value.relayhost.flags |= F_STARTTLS|F_TLS_VERIFY;
498 		}
499 		;
500 
501 relay		: opt_relay_common relay
502 		| opt_relay relay
503 		| /* empty */
504 		;
505 
506 opt_relay_via	: AUTH tables {
507 			struct table   *t = $2;
508 
509 			if (! table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) {
510 				yyerror("invalid use of table \"%s\" as AUTH parameter",
511 				    t->t_name);
512 				YYERROR;
513 			}
514 			strlcpy(rule->r_value.relayhost.authtable, t->t_name,
515 			    sizeof(rule->r_value.relayhost.authtable));
516 		}
517 		| VERIFY {
518 			if (!(rule->r_value.relayhost.flags & F_SSL)) {
519 				yyerror("cannot \"verify\" with insecure protocol");
520 				YYERROR;
521 			}
522 			rule->r_value.relayhost.flags |= F_TLS_VERIFY;
523 		}
524 		;
525 
526 relay_via	: opt_relay_common relay_via
527 		| opt_relay_via relay_via
528 		| /* empty */
529 		;
530 
531 main		: BOUNCEWARN {
532 			memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn);
533 		} bouncedelays
534 		| QUEUE COMPRESSION {
535 			conf->sc_queue_flags |= QUEUE_COMPRESSION;
536 		}
537 		| QUEUE ENCRYPTION {
538 			char	*password;
539 
540 			password = getpass("queue key: ");
541 			if (password == NULL) {
542 				yyerror("getpass() error");
543 				YYERROR;
544 			}
545 			conf->sc_queue_key = strdup(password);
546 			memset(password, 0, strlen(password));
547 			if (conf->sc_queue_key == NULL) {
548 				yyerror("memory exhausted");
549 				YYERROR;
550 			}
551 			conf->sc_queue_flags |= QUEUE_ENCRYPTION;
552 
553 		}
554 		| QUEUE ENCRYPTION KEY STRING {
555 			char   *buf;
556 			char   *lbuf;
557 			size_t	len;
558 
559 			if (strcasecmp($4, "stdin") == 0 ||
560 			    strcasecmp($4, "-") == 0) {
561 				lbuf = NULL;
562 				buf = fgetln(stdin, &len);
563 				if (buf[len - 1] == '\n') {
564 					lbuf = calloc(len, 1);
565 					memcpy(lbuf, buf, len-1);
566 				}
567 				else {
568 					lbuf = calloc(len+1, 1);
569 					memcpy(lbuf, buf, len);
570 				}
571 				conf->sc_queue_key = lbuf;
572 			}
573 			else
574 				conf->sc_queue_key = $4;
575 			conf->sc_queue_flags |= QUEUE_ENCRYPTION;
576 		}
577 		| EXPIRE STRING {
578 			conf->sc_qexpire = delaytonum($2);
579 			if (conf->sc_qexpire == -1) {
580 				yyerror("invalid expire delay: %s", $2);
581 				free($2);
582 				YYERROR;
583 			}
584 			free($2);
585 		}
586 		| MAXMESSAGESIZE size {
587 			conf->sc_maxsize = $2;
588 		}
589 		| MAXMTADEFERRED NUMBER  {
590 			conf->sc_mta_max_deferred = $2;
591 		}
592 		| LIMIT MDA limits_mda
593 		| LIMIT MTA FOR DOMAIN STRING {
594 			struct mta_limits	*d;
595 
596 			limits = dict_get(conf->sc_limits_dict, $5);
597 			if (limits == NULL) {
598 				limits = xcalloc(1, sizeof(*limits), "mta_limits");
599 				dict_xset(conf->sc_limits_dict, $5, limits);
600 				d = dict_xget(conf->sc_limits_dict, "default");
601 				memmove(limits, d, sizeof(*limits));
602 			}
603 			free($5);
604 		} limits_mta
605 		| LIMIT MTA {
606 			limits = dict_get(conf->sc_limits_dict, "default");
607 		} limits_mta
608 		| LIMIT SCHEDULER limits_scheduler
609 		| LISTEN {
610 			memset(&l, 0, sizeof l);
611 			memset(&listen_opts, 0, sizeof listen_opts);
612 			listen_opts.family = AF_UNSPEC;
613 		} ON STRING listen {
614 			listen_opts.ifx = $4;
615 			create_listener(conf->sc_listeners, &listen_opts);
616 		}
617 		| FILTER STRING STRING {
618 			if (!create_filter($2, $3)) {
619 				free($2);
620 				free($3);
621 				YYERROR;
622 			}
623 			free($2);
624 			free($3);
625 		}
626 		| FILTERCHAIN STRING {
627 			if ((filter = create_filter_chain($2)) == NULL) {
628 				free($2);
629 				YYERROR;
630 			}
631 		} filter_list
632 		| PKI STRING	{
633 			char buf[MAXHOSTNAMELEN];
634 			xlowercase(buf, $2, sizeof(buf));
635 			free($2);
636 			pki = dict_get(conf->sc_pki_dict, buf);
637 			if (pki == NULL) {
638 				pki = xcalloc(1, sizeof *pki, "parse:pki");
639 				strlcpy(pki->pki_name, buf, sizeof(pki->pki_name));
640 				dict_set(conf->sc_pki_dict, pki->pki_name, pki);
641 			}
642 		} pki
643 		;
644 
645 table		: TABLE STRING STRING	{
646 			char *p, *backend, *config;
647 
648 			p = $3;
649 			if (*p == '/') {
650 				backend = "static";
651 				config = $3;
652 			}
653 			else {
654 				backend = $3;
655 				config = NULL;
656 				for (p = $3; *p && *p != ':'; p++)
657 					;
658 				if (*p == ':') {
659 					*p = '\0';
660 					backend = $3;
661 					config  = p+1;
662 				}
663 			}
664 			if (config != NULL && *config != '/') {
665 				yyerror("invalid backend parameter for table: %s",
666 				    $2);
667 				free($2);
668 				free($3);
669 				YYERROR;
670 			}
671 			table = table_create(backend, $2, NULL, config);
672 			if (!table_config(table)) {
673 				yyerror("invalid configuration file %s for table %s",
674 				    config, table->t_name);
675 				free($2);
676 				free($3);
677 				YYERROR;
678 			}
679 			free($2);
680 			free($3);
681 		}
682 		| TABLE STRING {
683 			table = table_create("static", $2, NULL, NULL);
684 			free($2);
685 		} '{' tableval_list '}' {
686 			table = NULL;
687 		}
688 		;
689 
690 assign		: '=' | ARROW;
691 
692 keyval		: STRING assign STRING		{
693 			table->t_type = T_HASH;
694 			table_add(table, $1, $3);
695 			free($1);
696 			free($3);
697 		}
698 		;
699 
700 keyval_list	: keyval
701 		| keyval comma keyval_list
702 		;
703 
704 stringel	: STRING			{
705 			table->t_type = T_LIST;
706 			table_add(table, $1, NULL);
707 			free($1);
708 		}
709 		;
710 
711 string_list	: stringel
712 		| stringel comma string_list
713 		;
714 
715 filter_list	:
716 		| STRING {
717 			if (!extend_filter_chain(filter, $1)) {
718 				free($1);
719 				YYERROR;
720 			}
721 		} filter_list
722 		;
723 
724 tableval_list	: string_list			{ }
725 		| keyval_list			{ }
726 		;
727 
728 tablenew	: STRING			{
729 			struct table	*t;
730 
731 			t = table_create("static", NULL, NULL, NULL);
732 			t->t_type = T_LIST;
733 			table_add(t, $1, NULL);
734 			free($1);
735 			$$ = t;
736 		}
737 		| '{'				{
738 			table = table_create("static", NULL, NULL, NULL);
739 		} tableval_list '}'		{
740 			$$ = table;
741 		}
742 		;
743 
744 tableref       	: '<' STRING '>'       		{
745 			struct table	*t;
746 
747 			if ((t = table_find($2, NULL)) == NULL) {
748 				yyerror("no such table: %s", $2);
749 				free($2);
750 				YYERROR;
751 			}
752 			free($2);
753 			$$ = t;
754 		}
755 		;
756 
757 tables		: tablenew			{ $$ = $1; }
758 		| tableref			{ $$ = $1; }
759 		;
760 
761 alias		: ALIAS tables			{
762 			struct table   *t = $2;
763 
764 			if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
765 				yyerror("invalid use of table \"%s\" as ALIAS parameter",
766 				    t->t_name);
767 				YYERROR;
768 			}
769 
770 			$$ = t;
771 		}
772 		;
773 
774 virtual		: VIRTUAL tables		{
775 			struct table   *t = $2;
776 
777 			if (! table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
778 				yyerror("invalid use of table \"%s\" as VIRTUAL parameter",
779 				    t->t_name);
780 				YYERROR;
781 			}
782 			$$ = t;
783 		}
784 		;
785 
786 usermapping	: alias		{
787 			if (rule->r_mapping) {
788 				yyerror("alias specified multiple times");
789 				YYERROR;
790 			}
791 			rule->r_desttype = DEST_DOM;
792 			rule->r_mapping = $1;
793 		}
794 		| virtual	{
795 			if (rule->r_mapping) {
796 				yyerror("virtual specified multiple times");
797 				YYERROR;
798 			}
799 			rule->r_desttype = DEST_VDOM;
800 			rule->r_mapping = $1;
801 		}
802 		;
803 
804 userbase	: USERBASE tables	{
805 			struct table   *t = $2;
806 
807 			if (rule->r_userbase) {
808 				yyerror("userbase specified multiple times");
809 				YYERROR;
810 			}
811 			if (! table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) {
812 				yyerror("invalid use of table \"%s\" as USERBASE parameter",
813 				    t->t_name);
814 				YYERROR;
815 			}
816 			rule->r_userbase = t;
817 		}
818 		;
819 
820 deliver_action	: DELIVER TO MAILDIR			{
821 			rule->r_action = A_MAILDIR;
822 			if (strlcpy(rule->r_value.buffer, "~/Maildir",
823 			    sizeof(rule->r_value.buffer)) >=
824 			    sizeof(rule->r_value.buffer))
825 				fatal("pathname too long");
826 		}
827 		| DELIVER TO MAILDIR STRING		{
828 			rule->r_action = A_MAILDIR;
829 			if (strlcpy(rule->r_value.buffer, $4,
830 			    sizeof(rule->r_value.buffer)) >=
831 			    sizeof(rule->r_value.buffer))
832 				fatal("pathname too long");
833 			free($4);
834 		}
835 		| DELIVER TO LMTP STRING		{
836 			rule->r_action = A_LMTP;
837 			if (strchr($4, ':') || $4[0] == '/') {
838 				if (strlcpy(rule->r_value.buffer, $4,
839 					sizeof(rule->r_value.buffer))
840 					>= sizeof(rule->r_value.buffer))
841 					fatal("lmtp destination too long");
842 			} else
843 				fatal("invalid lmtp destination");
844 			free($4);
845 		}
846 		| DELIVER TO MBOX			{
847 			rule->r_action = A_MBOX;
848 			if (strlcpy(rule->r_value.buffer, _PATH_MAILDIR "/%u",
849 			    sizeof(rule->r_value.buffer))
850 			    >= sizeof(rule->r_value.buffer))
851 				fatal("pathname too long");
852 		}
853 		| DELIVER TO MDA STRING	       	{
854 			rule->r_action = A_MDA;
855 			if (strlcpy(rule->r_value.buffer, $4,
856 			    sizeof(rule->r_value.buffer))
857 			    >= sizeof(rule->r_value.buffer))
858 				fatal("command too long");
859 			free($4);
860 		}
861 		;
862 
863 relay_action   	: RELAY relay {
864 			rule->r_action = A_RELAY;
865 		}
866 		| RELAY VIA STRING {
867 			rule->r_action = A_RELAYVIA;
868 			if (! text_to_relayhost(&rule->r_value.relayhost, $3)) {
869 				yyerror("error: invalid url: %s", $3);
870 				free($3);
871 				YYERROR;
872 			}
873 			free($3);
874 		} relay_via {
875 			/* no worries, F_AUTH cant be set without SSL */
876 			if (rule->r_value.relayhost.flags & F_AUTH) {
877 				if (rule->r_value.relayhost.authtable[0] == '\0') {
878 					yyerror("error: auth without auth table");
879 					YYERROR;
880 				}
881 			}
882 		}
883 		;
884 
885 negation	: '!'		{ $$ = 1; }
886 		| /* empty */	{ $$ = 0; }
887 		;
888 
889 from		: FROM negation SOURCE tables       		{
890 			struct table   *t = $4;
891 
892 			if (rule->r_sources) {
893 				yyerror("from specified multiple times");
894 				YYERROR;
895 			}
896 			if (! table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) {
897 				yyerror("invalid use of table \"%s\" as FROM parameter",
898 				    t->t_name);
899 				YYERROR;
900 			}
901 			rule->r_notsources = $2;
902 			rule->r_sources = t;
903 		}
904 		| FROM negation ANY    		{
905 			if (rule->r_sources) {
906 				yyerror("from specified multiple times");
907 				YYERROR;
908 			}
909 			rule->r_sources = table_find("<anyhost>", NULL);
910 			rule->r_notsources = $2;
911 		}
912 		| FROM negation LOCAL  		{
913 			if (rule->r_sources) {
914 				yyerror("from specified multiple times");
915 				YYERROR;
916 			}
917 			rule->r_sources = table_find("<localhost>", NULL);
918 			rule->r_notsources = $2;
919 		}
920 		;
921 
922 for		: FOR negation DOMAIN tables {
923 			struct table   *t = $4;
924 
925 			if (rule->r_destination) {
926 				yyerror("for specified multiple times");
927 				YYERROR;
928 			}
929 			if (! table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
930 				yyerror("invalid use of table \"%s\" as DOMAIN parameter",
931 				    t->t_name);
932 				YYERROR;
933 			}
934 			rule->r_notdestination = $2;
935 			rule->r_destination = t;
936 		}
937 		| FOR negation ANY    		{
938 			if (rule->r_destination) {
939 				yyerror("for specified multiple times");
940 				YYERROR;
941 			}
942 			rule->r_notdestination = $2;
943 			rule->r_destination = table_find("<anydestination>", NULL);
944 		}
945 		| FOR negation LOCAL  		{
946 			if (rule->r_destination) {
947 				yyerror("for specified multiple times");
948 				YYERROR;
949 			}
950 			rule->r_notdestination = $2;
951 			rule->r_destination = table_find("<localnames>", NULL);
952 		}
953 		;
954 
955 sender		: SENDER negation tables			{
956 			struct table   *t = $3;
957 
958 			if (rule->r_senders) {
959 				yyerror("sender specified multiple times");
960 				YYERROR;
961 			}
962 
963 			if (! table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
964 				yyerror("invalid use of table \"%s\" as SENDER parameter",
965 				    t->t_name);
966 				YYERROR;
967 			}
968 			rule->r_notsenders = $2;
969 			rule->r_senders = t;
970 		}
971 		;
972 
973 recipient      	: RECIPIENT negation tables			{
974 			struct table   *t = $3;
975 
976 			if (rule->r_recipients) {
977 				yyerror("recipient specified multiple times");
978 				YYERROR;
979 			}
980 
981 			if (! table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
982 				yyerror("invalid use of table \"%s\" as RECIPIENT parameter",
983 				    t->t_name);
984 				YYERROR;
985 			}
986 			rule->r_notrecipients = $2;
987 			rule->r_recipients = t;
988 		}
989 		;
990 
991 forwardonly	: FORWARDONLY {
992 			if (rule->r_forwardonly) {
993 				yyerror("forward-only specified multiple times");
994 				YYERROR;
995 			}
996 			rule->r_forwardonly = 1;
997 		}
998 		;
999 
1000 expire		: EXPIRE STRING {
1001 			if (rule->r_qexpire != -1) {
1002 				yyerror("expire specified multiple times");
1003 				YYERROR;
1004 			}
1005 			rule->r_qexpire = delaytonum($2);
1006 			if (rule->r_qexpire == -1) {
1007 				yyerror("invalid expire delay: %s", $2);
1008 				free($2);
1009 				YYERROR;
1010 			}
1011 			free($2);
1012 		}
1013 		;
1014 
1015 opt_decision	: sender
1016 		| recipient
1017 		| from
1018 		| for
1019 		| tagged
1020 		;
1021 decision	: opt_decision decision
1022 		|
1023 		;
1024 
1025 opt_lookup	: userbase
1026 		| usermapping
1027 		;
1028 lookup		: opt_lookup lookup
1029 		|
1030 		;
1031 
1032 action		: deliver_action
1033 		| relay_action
1034 		|
1035 		;
1036 
1037 opt_accept	: expire
1038 		| forwardonly
1039 		;
1040 
1041 accept_params	: opt_accept accept_params
1042 		|
1043 		;
1044 
1045 rule		: ACCEPT {
1046 			rule = xcalloc(1, sizeof(*rule), "parse rule: ACCEPT");
1047 			rule->r_action = A_NONE;
1048 			rule->r_decision = R_ACCEPT;
1049 			rule->r_desttype = DEST_DOM;
1050 			rule->r_qexpire = -1;
1051 		} decision lookup action accept_params {
1052 			if (! rule->r_sources)
1053 				rule->r_sources = table_find("<localhost>", NULL);
1054 			if (! rule->r_destination)
1055 			 	rule->r_destination = table_find("<localnames>", NULL);
1056 			if (! rule->r_userbase)
1057 				rule->r_userbase = table_find("<getpwnam>", NULL);
1058 			if (rule->r_qexpire == -1)
1059 				rule->r_qexpire = conf->sc_qexpire;
1060 			if (rule->r_action == A_RELAY || rule->r_action == A_RELAYVIA) {
1061 				if (rule->r_userbase != table_find("<getpwnam>", NULL)) {
1062 					yyerror("userbase may not be used with a relay rule");
1063 					YYERROR;
1064 				}
1065 				if (rule->r_mapping) {
1066 					yyerror("aliases/virtual may not be used with a relay rule");
1067 					YYERROR;
1068 				}
1069 			}
1070 			if (rule->r_forwardonly && rule->r_action != A_NONE) {
1071 				yyerror("forward-only may not be used with a default action");
1072 				YYERROR;
1073 			}
1074 			TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
1075 			rule = NULL;
1076 		}
1077 		| REJECT {
1078 			rule = xcalloc(1, sizeof(*rule), "parse rule: REJECT");
1079 			rule->r_decision = R_REJECT;
1080 			rule->r_desttype = DEST_DOM;
1081 		} decision {
1082 			if (! rule->r_sources)
1083 				rule->r_sources = table_find("<localhost>", NULL);
1084 			if (! rule->r_destination)
1085 				rule->r_destination = table_find("<localnames>", NULL);
1086 			TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
1087 			rule = NULL;
1088 		}
1089 		;
1090 %%
1091 
1092 struct keywords {
1093 	const char	*k_name;
1094 	int		 k_val;
1095 };
1096 
1097 int
1098 yyerror(const char *fmt, ...)
1099 {
1100 	va_list		 ap;
1101 	char		*nfmt;
1102 
1103 	file->errors++;
1104 	va_start(ap, fmt);
1105 	if (asprintf(&nfmt, "%s:%d: %s", file->name, yylval.lineno, fmt) == -1)
1106 		fatalx("yyerror asprintf");
1107 	vlog(LOG_CRIT, nfmt, ap);
1108 	va_end(ap);
1109 	free(nfmt);
1110 	return (0);
1111 }
1112 
1113 int
1114 kw_cmp(const void *k, const void *e)
1115 {
1116 	return (strcmp(k, ((const struct keywords *)e)->k_name));
1117 }
1118 
1119 int
1120 lookup(char *s)
1121 {
1122 	/* this has to be sorted always */
1123 	static const struct keywords keywords[] = {
1124 		{ "accept",		ACCEPT },
1125 		{ "alias",		ALIAS },
1126 		{ "any",		ANY },
1127 		{ "as",			AS },
1128 		{ "auth",		AUTH },
1129 		{ "auth-optional",     	AUTH_OPTIONAL },
1130 		{ "backup",		BACKUP },
1131 		{ "bounce-warn",	BOUNCEWARN },
1132 		{ "ca",			CA },
1133 		{ "certificate",	CERTIFICATE },
1134 		{ "compression",	COMPRESSION },
1135 		{ "deliver",		DELIVER },
1136 		{ "dhparams",		DHPARAMS },
1137 		{ "domain",		DOMAIN },
1138 		{ "encryption",		ENCRYPTION },
1139 		{ "expire",		EXPIRE },
1140 		{ "filter",		FILTER },
1141 		{ "filterchain",	FILTERCHAIN },
1142 		{ "for",		FOR },
1143 		{ "forward-only",      	FORWARDONLY },
1144 		{ "from",		FROM },
1145 		{ "hostname",		HOSTNAME },
1146 		{ "hostnames",		HOSTNAMES },
1147 		{ "include",		INCLUDE },
1148 		{ "inet4",		INET4 },
1149 		{ "inet6",		INET6 },
1150 		{ "key",		KEY },
1151 		{ "limit",		LIMIT },
1152 		{ "listen",		LISTEN },
1153 		{ "lmtp",		LMTP },
1154 		{ "local",		LOCAL },
1155 		{ "maildir",		MAILDIR },
1156 		{ "mask-source",	MASK_SOURCE },
1157 		{ "max-message-size",  	MAXMESSAGESIZE },
1158 		{ "max-mta-deferred",  	MAXMTADEFERRED },
1159 		{ "mbox",		MBOX },
1160 		{ "mda",		MDA },
1161 		{ "mta",		MTA },
1162 		{ "on",			ON },
1163 		{ "pki",		PKI },
1164 		{ "port",		PORT },
1165 		{ "queue",		QUEUE },
1166 		{ "recipient",		RECIPIENT },
1167 		{ "reject",		REJECT },
1168 		{ "relay",		RELAY },
1169 		{ "scheduler",		SCHEDULER },
1170 		{ "secure",		SECURE },
1171 		{ "sender",    		SENDER },
1172 		{ "smtps",		SMTPS },
1173 		{ "source",		SOURCE },
1174 		{ "table",		TABLE },
1175 		{ "tag",		TAG },
1176 		{ "tagged",		TAGGED },
1177 		{ "tls",		TLS },
1178 		{ "tls-require",       	TLS_REQUIRE },
1179 		{ "to",			TO },
1180 		{ "userbase",		USERBASE },
1181 		{ "verify",		VERIFY },
1182 		{ "via",		VIA },
1183 		{ "virtual",		VIRTUAL },
1184 	};
1185 	const struct keywords	*p;
1186 
1187 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
1188 	    sizeof(keywords[0]), kw_cmp);
1189 
1190 	if (p)
1191 		return (p->k_val);
1192 	else
1193 		return (STRING);
1194 }
1195 
1196 #define MAXPUSHBACK	128
1197 
1198 u_char	*parsebuf;
1199 int	 parseindex;
1200 u_char	 pushback_buffer[MAXPUSHBACK];
1201 int	 pushback_index = 0;
1202 
1203 int
1204 lgetc(int quotec)
1205 {
1206 	int		c, next;
1207 
1208 	if (parsebuf) {
1209 		/* Read character from the parsebuffer instead of input. */
1210 		if (parseindex >= 0) {
1211 			c = parsebuf[parseindex++];
1212 			if (c != '\0')
1213 				return (c);
1214 			parsebuf = NULL;
1215 		} else
1216 			parseindex++;
1217 	}
1218 
1219 	if (pushback_index)
1220 		return (pushback_buffer[--pushback_index]);
1221 
1222 	if (quotec) {
1223 		if ((c = getc(file->stream)) == EOF) {
1224 			yyerror("reached end of file while parsing "
1225 			    "quoted string");
1226 			if (file == topfile || popfile() == EOF)
1227 				return (EOF);
1228 			return (quotec);
1229 		}
1230 		return (c);
1231 	}
1232 
1233 	while ((c = getc(file->stream)) == '\\') {
1234 		next = getc(file->stream);
1235 		if (next != '\n') {
1236 			c = next;
1237 			break;
1238 		}
1239 		yylval.lineno = file->lineno;
1240 		file->lineno++;
1241 	}
1242 
1243 	while (c == EOF) {
1244 		if (file == topfile || popfile() == EOF)
1245 			return (EOF);
1246 		c = getc(file->stream);
1247 	}
1248 	return (c);
1249 }
1250 
1251 int
1252 lungetc(int c)
1253 {
1254 	if (c == EOF)
1255 		return (EOF);
1256 	if (parsebuf) {
1257 		parseindex--;
1258 		if (parseindex >= 0)
1259 			return (c);
1260 	}
1261 	if (pushback_index < MAXPUSHBACK-1)
1262 		return (pushback_buffer[pushback_index++] = c);
1263 	else
1264 		return (EOF);
1265 }
1266 
1267 int
1268 findeol(void)
1269 {
1270 	int	c;
1271 
1272 	parsebuf = NULL;
1273 	pushback_index = 0;
1274 
1275 	/* skip to either EOF or the first real EOL */
1276 	while (1) {
1277 		c = lgetc(0);
1278 		if (c == '\n') {
1279 			file->lineno++;
1280 			break;
1281 		}
1282 		if (c == EOF)
1283 			break;
1284 	}
1285 	return (ERROR);
1286 }
1287 
1288 int
1289 yylex(void)
1290 {
1291 	u_char	 buf[8096];
1292 	u_char	*p, *val;
1293 	int	 quotec, next, c;
1294 	int	 token;
1295 
1296 top:
1297 	p = buf;
1298 	while ((c = lgetc(0)) == ' ' || c == '\t')
1299 		; /* nothing */
1300 
1301 	yylval.lineno = file->lineno;
1302 	if (c == '#')
1303 		while ((c = lgetc(0)) != '\n' && c != EOF)
1304 			; /* nothing */
1305 	if (c == '$' && parsebuf == NULL) {
1306 		while (1) {
1307 			if ((c = lgetc(0)) == EOF)
1308 				return (0);
1309 
1310 			if (p + 1 >= buf + sizeof(buf) - 1) {
1311 				yyerror("string too long");
1312 				return (findeol());
1313 			}
1314 			if (isalnum(c) || c == '_') {
1315 				*p++ = c;
1316 				continue;
1317 			}
1318 			*p = '\0';
1319 			lungetc(c);
1320 			break;
1321 		}
1322 		val = symget(buf);
1323 		if (val == NULL) {
1324 			yyerror("macro '%s' not defined", buf);
1325 			return (findeol());
1326 		}
1327 		parsebuf = val;
1328 		parseindex = 0;
1329 		goto top;
1330 	}
1331 
1332 	switch (c) {
1333 	case '\'':
1334 	case '"':
1335 		quotec = c;
1336 		while (1) {
1337 			if ((c = lgetc(quotec)) == EOF)
1338 				return (0);
1339 			if (c == '\n') {
1340 				file->lineno++;
1341 				continue;
1342 			} else if (c == '\\') {
1343 				if ((next = lgetc(quotec)) == EOF)
1344 					return (0);
1345 				if (next == quotec || c == ' ' || c == '\t')
1346 					c = next;
1347 				else if (next == '\n') {
1348 					file->lineno++;
1349 					continue;
1350 				} else
1351 					lungetc(next);
1352 			} else if (c == quotec) {
1353 				*p = '\0';
1354 				break;
1355 			}
1356 			if (p + 1 >= buf + sizeof(buf) - 1) {
1357 				yyerror("string too long");
1358 				return (findeol());
1359 			}
1360 			*p++ = c;
1361 		}
1362 		yylval.v.string = strdup(buf);
1363 		if (yylval.v.string == NULL)
1364 			err(1, "yylex: strdup");
1365 		return (STRING);
1366 	}
1367 
1368 #define allowed_to_end_number(x) \
1369 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1370 
1371 	if (c == '-' || isdigit(c)) {
1372 		do {
1373 			*p++ = c;
1374 			if ((unsigned)(p-buf) >= sizeof(buf)) {
1375 				yyerror("string too long");
1376 				return (findeol());
1377 			}
1378 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1379 		lungetc(c);
1380 		if (p == buf + 1 && buf[0] == '-')
1381 			goto nodigits;
1382 		if (c == EOF || allowed_to_end_number(c)) {
1383 			const char *errstr = NULL;
1384 
1385 			*p = '\0';
1386 			yylval.v.number = strtonum(buf, LLONG_MIN,
1387 			    LLONG_MAX, &errstr);
1388 			if (errstr) {
1389 				yyerror("\"%s\" invalid number: %s",
1390 				    buf, errstr);
1391 				return (findeol());
1392 			}
1393 			return (NUMBER);
1394 		} else {
1395 nodigits:
1396 			while (p > buf + 1)
1397 				lungetc(*--p);
1398 			c = *--p;
1399 			if (c == '-')
1400 				return (c);
1401 		}
1402 	}
1403 
1404 	if (c == '=') {
1405 		if ((c = lgetc(0)) != EOF && c == '>')
1406 			return (ARROW);
1407 		lungetc(c);
1408 		c = '=';
1409 	}
1410 
1411 #define allowed_in_string(x) \
1412 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1413 	x != '{' && x != '}' && x != '<' && x != '>' && \
1414 	x != '!' && x != '=' && x != '#' && \
1415 	x != ','))
1416 
1417 	if (isalnum(c) || c == ':' || c == '_') {
1418 		do {
1419 			*p++ = c;
1420 			if ((unsigned)(p-buf) >= sizeof(buf)) {
1421 				yyerror("string too long");
1422 				return (findeol());
1423 			}
1424 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1425 		lungetc(c);
1426 		*p = '\0';
1427 		if ((token = lookup(buf)) == STRING)
1428 			if ((yylval.v.string = strdup(buf)) == NULL)
1429 				err(1, "yylex: strdup");
1430 		return (token);
1431 	}
1432 	if (c == '\n') {
1433 		yylval.lineno = file->lineno;
1434 		file->lineno++;
1435 	}
1436 	if (c == EOF)
1437 		return (0);
1438 	return (c);
1439 }
1440 
1441 int
1442 check_file_secrecy(int fd, const char *fname)
1443 {
1444 	struct stat	st;
1445 
1446 	if (fstat(fd, &st)) {
1447 		log_warn("warn: cannot stat %s", fname);
1448 		return (-1);
1449 	}
1450 	if (st.st_uid != 0 && st.st_uid != getuid()) {
1451 		log_warnx("warn: %s: owner not root or current user", fname);
1452 		return (-1);
1453 	}
1454 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
1455 		log_warnx("warn: %s: group/world readable/writeable", fname);
1456 		return (-1);
1457 	}
1458 	return (0);
1459 }
1460 
1461 struct file *
1462 pushfile(const char *name, int secret)
1463 {
1464 	struct file	*nfile;
1465 
1466 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
1467 		log_warn("warn: malloc");
1468 		return (NULL);
1469 	}
1470 	if ((nfile->name = strdup(name)) == NULL) {
1471 		log_warn("warn: malloc");
1472 		free(nfile);
1473 		return (NULL);
1474 	}
1475 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1476 		log_warn("warn: %s", nfile->name);
1477 		free(nfile->name);
1478 		free(nfile);
1479 		return (NULL);
1480 	} else if (secret &&
1481 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
1482 		fclose(nfile->stream);
1483 		free(nfile->name);
1484 		free(nfile);
1485 		return (NULL);
1486 	}
1487 	nfile->lineno = 1;
1488 	TAILQ_INSERT_TAIL(&files, nfile, entry);
1489 	return (nfile);
1490 }
1491 
1492 int
1493 popfile(void)
1494 {
1495 	struct file	*prev;
1496 
1497 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1498 		prev->errors += file->errors;
1499 
1500 	TAILQ_REMOVE(&files, file, entry);
1501 	fclose(file->stream);
1502 	free(file->name);
1503 	free(file);
1504 	file = prev;
1505 	return (file ? 0 : EOF);
1506 }
1507 
1508 int
1509 parse_config(struct smtpd *x_conf, const char *filename, int opts)
1510 {
1511 	struct sym     *sym, *next;
1512 	struct table   *t;
1513 	char		hostname[SMTPD_MAXHOSTNAMELEN];
1514 	char		hostname_copy[SMTPD_MAXHOSTNAMELEN];
1515 
1516 	if (! getmailname(hostname, sizeof hostname))
1517 		return (-1);
1518 
1519 	conf = x_conf;
1520 	memset(conf, 0, sizeof(*conf));
1521 
1522 	strlcpy(conf->sc_hostname, hostname, sizeof(conf->sc_hostname));
1523 
1524 	conf->sc_maxsize = DEFAULT_MAX_BODY_SIZE;
1525 
1526 	conf->sc_tables_dict = calloc(1, sizeof(*conf->sc_tables_dict));
1527 	conf->sc_rules = calloc(1, sizeof(*conf->sc_rules));
1528 	conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners));
1529 	conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict));
1530 	conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict));
1531 	conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict));
1532 
1533 	/* Report mails delayed for more than 4 hours */
1534 	conf->sc_bounce_warn[0] = 3600 * 4;
1535 
1536 	if (conf->sc_tables_dict == NULL	||
1537 	    conf->sc_rules == NULL		||
1538 	    conf->sc_listeners == NULL		||
1539 	    conf->sc_pki_dict == NULL		||
1540 	    conf->sc_limits_dict == NULL) {
1541 		log_warn("warn: cannot allocate memory");
1542 		free(conf->sc_tables_dict);
1543 		free(conf->sc_rules);
1544 		free(conf->sc_listeners);
1545 		free(conf->sc_pki_dict);
1546 		free(conf->sc_ssl_dict);
1547 		free(conf->sc_limits_dict);
1548 		return (-1);
1549 	}
1550 
1551 	errors = 0;
1552 
1553 	table = NULL;
1554 	rule = NULL;
1555 
1556 	dict_init(&conf->sc_filters);
1557 
1558 	dict_init(conf->sc_pki_dict);
1559 	dict_init(conf->sc_ssl_dict);
1560 	dict_init(conf->sc_tables_dict);
1561 
1562 	dict_init(conf->sc_limits_dict);
1563 	limits = xcalloc(1, sizeof(*limits), "mta_limits");
1564 	limit_mta_set_defaults(limits);
1565 	dict_xset(conf->sc_limits_dict, "default", limits);
1566 
1567 	TAILQ_INIT(conf->sc_listeners);
1568 	TAILQ_INIT(conf->sc_rules);
1569 
1570 	conf->sc_qexpire = SMTPD_QUEUE_EXPIRY;
1571 	conf->sc_opts = opts;
1572 
1573 	conf->sc_mta_max_deferred = 100;
1574 	conf->sc_scheduler_max_inflight = 5000;
1575 	conf->sc_scheduler_max_schedule = 10;
1576 	conf->sc_scheduler_max_evp_batch_size = 256;
1577 	conf->sc_scheduler_max_msg_batch_size = 1024;
1578 
1579 	conf->sc_mda_max_session = 50;
1580 	conf->sc_mda_max_user_session = 7;
1581 	conf->sc_mda_task_hiwat = 50;
1582 	conf->sc_mda_task_lowat = 30;
1583 	conf->sc_mda_task_release = 10;
1584 
1585 	if ((file = pushfile(filename, 0)) == NULL) {
1586 		purge_config(PURGE_EVERYTHING);
1587 		return (-1);
1588 	}
1589 	topfile = file;
1590 
1591 	/*
1592 	 * declare special "localhost", "anyhost" and "localnames" tables
1593 	 */
1594 	set_localaddrs();
1595 
1596 	t = table_create("static", "<localnames>", NULL, NULL);
1597 	t->t_type = T_LIST;
1598 	table_add(t, "localhost", NULL);
1599 	table_add(t, hostname, NULL);
1600 
1601 	t = table_create("static", "<anydestination>", NULL, NULL);
1602 	t->t_type = T_LIST;
1603 	table_add(t, "*", NULL);
1604 
1605 	/* can't truncate here */
1606 	(void)strlcpy(hostname_copy, hostname, sizeof hostname_copy);
1607 
1608 	hostname_copy[strcspn(hostname_copy, ".")] = '\0';
1609 	if (strcmp(hostname, hostname_copy) != 0)
1610 		table_add(t, hostname_copy, NULL);
1611 
1612 	table_create("getpwnam", "<getpwnam>", NULL, NULL);
1613 
1614 	/*
1615 	 * parse configuration
1616 	 */
1617 	setservent(1);
1618 	yyparse();
1619 	errors = file->errors;
1620 	popfile();
1621 	endservent();
1622 
1623 	/* Free macros and check which have not been used. */
1624 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
1625 		next = TAILQ_NEXT(sym, entry);
1626 		if ((conf->sc_opts & SMTPD_OPT_VERBOSE) && !sym->used)
1627 			fprintf(stderr, "warning: macro '%s' not "
1628 			    "used\n", sym->nam);
1629 		if (!sym->persist) {
1630 			free(sym->nam);
1631 			free(sym->val);
1632 			TAILQ_REMOVE(&symhead, sym, entry);
1633 			free(sym);
1634 		}
1635 	}
1636 
1637 	if (TAILQ_EMPTY(conf->sc_rules)) {
1638 		log_warnx("warn: no rules, nothing to do");
1639 		errors++;
1640 	}
1641 
1642 	if (errors) {
1643 		purge_config(PURGE_EVERYTHING);
1644 		return (-1);
1645 	}
1646 
1647 	return (0);
1648 }
1649 
1650 int
1651 symset(const char *nam, const char *val, int persist)
1652 {
1653 	struct sym	*sym;
1654 
1655 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
1656 	    sym = TAILQ_NEXT(sym, entry))
1657 		;	/* nothing */
1658 
1659 	if (sym != NULL) {
1660 		if (sym->persist == 1)
1661 			return (0);
1662 		else {
1663 			free(sym->nam);
1664 			free(sym->val);
1665 			TAILQ_REMOVE(&symhead, sym, entry);
1666 			free(sym);
1667 		}
1668 	}
1669 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1670 		return (-1);
1671 
1672 	sym->nam = strdup(nam);
1673 	if (sym->nam == NULL) {
1674 		free(sym);
1675 		return (-1);
1676 	}
1677 	sym->val = strdup(val);
1678 	if (sym->val == NULL) {
1679 		free(sym->nam);
1680 		free(sym);
1681 		return (-1);
1682 	}
1683 	sym->used = 0;
1684 	sym->persist = persist;
1685 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1686 	return (0);
1687 }
1688 
1689 int
1690 cmdline_symset(char *s)
1691 {
1692 	char	*sym, *val;
1693 	int	ret;
1694 	size_t	len;
1695 
1696 	if ((val = strrchr(s, '=')) == NULL)
1697 		return (-1);
1698 
1699 	len = strlen(s) - strlen(val) + 1;
1700 	if ((sym = malloc(len)) == NULL)
1701 		errx(1, "cmdline_symset: malloc");
1702 
1703 	(void)strlcpy(sym, s, len);
1704 
1705 	ret = symset(sym, val + 1, 1);
1706 	free(sym);
1707 
1708 	return (ret);
1709 }
1710 
1711 char *
1712 symget(const char *nam)
1713 {
1714 	struct sym	*sym;
1715 
1716 	TAILQ_FOREACH(sym, &symhead, entry)
1717 		if (strcmp(nam, sym->nam) == 0) {
1718 			sym->used = 1;
1719 			return (sym->val);
1720 		}
1721 	return (NULL);
1722 }
1723 
1724 static void
1725 create_listener(struct listenerlist *ll,  struct listen_opts *lo)
1726 {
1727 	uint16_t	flags;
1728 
1729 	if (lo->port != 0 && lo->ssl == F_SSL)
1730 		errx(1, "invalid listen option: tls/smtps on same port");
1731 
1732 	if (lo->auth != 0 && !lo->ssl)
1733 		errx(1, "invalid listen option: auth requires tls/smtps");
1734 
1735 	if (lo->pki && !lo->ssl)
1736 		errx(1, "invalid listen option: pki requires tls/smtps");
1737 
1738 	flags = lo->flags;
1739 
1740 	if (lo->port) {
1741 		lo->flags = lo->ssl|lo->auth|flags;
1742 		lo->port = htons(lo->port);
1743 		if (! interface(ll, lo))
1744 			if (host(ll, lo) <= 0)
1745 				errx(1, "invalid virtual ip or interface: %s", lo->ifx);
1746 	}
1747 	else {
1748 		if (lo->ssl & F_SMTPS) {
1749 			lo->port = htons(465);
1750 			lo->flags = F_SMTPS|lo->auth|flags;
1751 			if (! interface(ll, lo))
1752 				if (host(ll, lo) <= 0)
1753 					errx(1, "invalid virtual ip or interface: %s", lo->ifx);
1754 		}
1755 
1756 		if (! lo->ssl || (lo->ssl & F_STARTTLS)) {
1757 			lo->port = htons(25);
1758 			lo->flags = lo->auth|flags;
1759 			if (lo->ssl & F_STARTTLS)
1760 				lo->flags |= F_STARTTLS;
1761 			if (! interface(ll, lo))
1762 				if (host(ll, lo) <= 0)
1763 					errx(1, "invalid virtual ip or interface: %s", lo->ifx);
1764 		}
1765 	}
1766 }
1767 
1768 static void
1769 config_listener(struct listener *h,  struct listen_opts *lo)
1770 {
1771 	h->fd = -1;
1772 	h->port = lo->port;
1773 	h->flags = lo->flags;
1774 
1775 	if (lo->hostname == NULL)
1776 		lo->hostname = conf->sc_hostname;
1777 
1778 	h->pki_name[0] = '\0';
1779 
1780 	if (lo->authtable != NULL)
1781 		(void)strlcpy(h->authtable, lo->authtable->t_name, sizeof(h->authtable));
1782 	if (lo->pki != NULL) {
1783 		if (! lowercase(h->pki_name, lo->pki, sizeof(h->pki_name))) {
1784 			log_warnx("pki name too long: %s", lo->pki);
1785 			fatalx(NULL);
1786 		}
1787 		if (dict_get(conf->sc_pki_dict, h->pki_name) == NULL) {
1788 			log_warnx("pki name not found: %s", lo->pki);
1789 			fatalx(NULL);
1790 		}
1791 	}
1792 	if (lo->tag != NULL)
1793 		(void)strlcpy(h->tag, lo->tag, sizeof(h->tag));
1794 
1795 	(void)strlcpy(h->hostname, lo->hostname, sizeof(h->hostname));
1796 	if (lo->hostnametable)
1797 		(void)strlcpy(h->hostnametable, lo->hostnametable->t_name, sizeof(h->hostnametable));
1798 
1799 	if (lo->ssl & F_TLS_VERIFY)
1800 		h->flags |= F_TLS_VERIFY;
1801 }
1802 
1803 struct listener *
1804 host_v4(const char *s, in_port_t port)
1805 {
1806 	struct in_addr		 ina;
1807 	struct sockaddr_in	*sain;
1808 	struct listener		*h;
1809 
1810 	memset(&ina, 0, sizeof(ina));
1811 	if (inet_pton(AF_INET, s, &ina) != 1)
1812 		return (NULL);
1813 
1814 	h = xcalloc(1, sizeof(*h), "host_v4");
1815 	sain = (struct sockaddr_in *)&h->ss;
1816 	sain->sin_len = sizeof(struct sockaddr_in);
1817 	sain->sin_family = AF_INET;
1818 	sain->sin_addr.s_addr = ina.s_addr;
1819 	sain->sin_port = port;
1820 
1821 	return (h);
1822 }
1823 
1824 struct listener *
1825 host_v6(const char *s, in_port_t port)
1826 {
1827 	struct in6_addr		 ina6;
1828 	struct sockaddr_in6	*sin6;
1829 	struct listener		*h;
1830 
1831 	memset(&ina6, 0, sizeof(ina6));
1832 	if (inet_pton(AF_INET6, s, &ina6) != 1)
1833 		return (NULL);
1834 
1835 	h = xcalloc(1, sizeof(*h), "host_v6");
1836 	sin6 = (struct sockaddr_in6 *)&h->ss;
1837 	sin6->sin6_len = sizeof(struct sockaddr_in6);
1838 	sin6->sin6_family = AF_INET6;
1839 	sin6->sin6_port = port;
1840 	memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
1841 
1842 	return (h);
1843 }
1844 
1845 int
1846 host_dns(struct listenerlist *al, struct listen_opts *lo)
1847 {
1848 	struct addrinfo		 hints, *res0, *res;
1849 	int			 error, cnt = 0;
1850 	struct sockaddr_in	*sain;
1851 	struct sockaddr_in6	*sin6;
1852 	struct listener		*h;
1853 
1854 	memset(&hints, 0, sizeof(hints));
1855 	hints.ai_family = PF_UNSPEC;
1856 	hints.ai_socktype = SOCK_STREAM;
1857 	error = getaddrinfo(lo->ifx, NULL, &hints, &res0);
1858 	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
1859 		return (0);
1860 	if (error) {
1861 		log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx,
1862 		    gai_strerror(error));
1863 		return (-1);
1864 	}
1865 
1866 	for (res = res0; res; res = res->ai_next) {
1867 		if (res->ai_family != AF_INET &&
1868 		    res->ai_family != AF_INET6)
1869 			continue;
1870 		h = xcalloc(1, sizeof(*h), "host_dns");
1871 
1872 		h->ss.ss_family = res->ai_family;
1873 		if (res->ai_family == AF_INET) {
1874 			sain = (struct sockaddr_in *)&h->ss;
1875 			sain->sin_len = sizeof(struct sockaddr_in);
1876 			sain->sin_addr.s_addr = ((struct sockaddr_in *)
1877 			    res->ai_addr)->sin_addr.s_addr;
1878 			sain->sin_port = lo->port;
1879 		} else {
1880 			sin6 = (struct sockaddr_in6 *)&h->ss;
1881 			sin6->sin6_len = sizeof(struct sockaddr_in6);
1882 			memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
1883 			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
1884 			sin6->sin6_port = lo->port;
1885 		}
1886 
1887 		config_listener(h, lo);
1888 
1889 		TAILQ_INSERT_HEAD(al, h, entry);
1890 		cnt++;
1891 	}
1892 
1893 	freeaddrinfo(res0);
1894 	return (cnt);
1895 }
1896 
1897 int
1898 host(struct listenerlist *al, struct listen_opts *lo)
1899 {
1900 	struct listener *h;
1901 
1902 	h = host_v4(lo->ifx, lo->port);
1903 
1904 	/* IPv6 address? */
1905 	if (h == NULL)
1906 		h = host_v6(lo->ifx, lo->port);
1907 
1908 	if (h != NULL) {
1909 		config_listener(h, lo);
1910 		TAILQ_INSERT_HEAD(al, h, entry);
1911 		return (1);
1912 	}
1913 
1914 	return (host_dns(al, lo));
1915 }
1916 
1917 int
1918 interface(struct listenerlist *al, struct listen_opts *lo)
1919 {
1920 	struct ifaddrs *ifap, *p;
1921 	struct sockaddr_in	*sain;
1922 	struct sockaddr_in6	*sin6;
1923 	struct listener		*h;
1924 	int			ret = 0;
1925 
1926 	if (getifaddrs(&ifap) == -1)
1927 		fatal("getifaddrs");
1928 
1929 	for (p = ifap; p != NULL; p = p->ifa_next) {
1930 		if (p->ifa_addr == NULL)
1931 			continue;
1932 		if (strcmp(p->ifa_name, lo->ifx) != 0 &&
1933 		    ! is_if_in_group(p->ifa_name, lo->ifx))
1934 			continue;
1935 		if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family)
1936 			continue;
1937 
1938 		h = xcalloc(1, sizeof(*h), "interface");
1939 
1940 		switch (p->ifa_addr->sa_family) {
1941 		case AF_INET:
1942 			sain = (struct sockaddr_in *)&h->ss;
1943 			*sain = *(struct sockaddr_in *)p->ifa_addr;
1944 			sain->sin_len = sizeof(struct sockaddr_in);
1945 			sain->sin_port = lo->port;
1946 			break;
1947 
1948 		case AF_INET6:
1949 			sin6 = (struct sockaddr_in6 *)&h->ss;
1950 			*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
1951 			sin6->sin6_len = sizeof(struct sockaddr_in6);
1952 			sin6->sin6_port = lo->port;
1953 			break;
1954 
1955 		default:
1956 			free(h);
1957 			continue;
1958 		}
1959 
1960 		config_listener(h, lo);
1961 		ret = 1;
1962 		TAILQ_INSERT_HEAD(al, h, entry);
1963 	}
1964 
1965 	freeifaddrs(ifap);
1966 
1967 	return ret;
1968 }
1969 
1970 void
1971 set_localaddrs(void)
1972 {
1973 	struct ifaddrs *ifap, *p;
1974 	struct sockaddr_storage ss;
1975 	struct sockaddr_in	*sain;
1976 	struct sockaddr_in6	*sin6;
1977 	struct table		*t;
1978 
1979 	t = table_create("static", "<anyhost>", NULL, NULL);
1980 	table_add(t, "local", NULL);
1981 	table_add(t, "0.0.0.0/0", NULL);
1982 	table_add(t, "::/0", NULL);
1983 
1984 	if (getifaddrs(&ifap) == -1)
1985 		fatal("getifaddrs");
1986 
1987 	t = table_create("static", "<localhost>", NULL, NULL);
1988 	table_add(t, "local", NULL);
1989 
1990 	for (p = ifap; p != NULL; p = p->ifa_next) {
1991 		if (p->ifa_addr == NULL)
1992 			continue;
1993 		switch (p->ifa_addr->sa_family) {
1994 		case AF_INET:
1995 			sain = (struct sockaddr_in *)&ss;
1996 			*sain = *(struct sockaddr_in *)p->ifa_addr;
1997 			sain->sin_len = sizeof(struct sockaddr_in);
1998 			table_add(t, ss_to_text(&ss), NULL);
1999 			break;
2000 
2001 		case AF_INET6:
2002 			sin6 = (struct sockaddr_in6 *)&ss;
2003 			*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
2004 			sin6->sin6_len = sizeof(struct sockaddr_in6);
2005 			table_add(t, ss_to_text(&ss), NULL);
2006 			break;
2007 		}
2008 	}
2009 
2010 	freeifaddrs(ifap);
2011 }
2012 
2013 int
2014 delaytonum(char *str)
2015 {
2016 	unsigned int     factor;
2017 	size_t           len;
2018 	const char      *errstr = NULL;
2019 	int              delay;
2020 
2021 	/* we need at least 1 digit and 1 unit */
2022 	len = strlen(str);
2023 	if (len < 2)
2024 		goto bad;
2025 
2026 	switch(str[len - 1]) {
2027 
2028 	case 's':
2029 		factor = 1;
2030 		break;
2031 
2032 	case 'm':
2033 		factor = 60;
2034 		break;
2035 
2036 	case 'h':
2037 		factor = 60 * 60;
2038 		break;
2039 
2040 	case 'd':
2041 		factor = 24 * 60 * 60;
2042 		break;
2043 
2044 	default:
2045 		goto bad;
2046 	}
2047 
2048 	str[len - 1] = '\0';
2049 	delay = strtonum(str, 1, INT_MAX / factor, &errstr);
2050 	if (errstr)
2051 		goto bad;
2052 
2053 	return (delay * factor);
2054 
2055 bad:
2056 	return (-1);
2057 }
2058 
2059 int
2060 is_if_in_group(const char *ifname, const char *groupname)
2061 {
2062         unsigned int		 len;
2063         struct ifgroupreq        ifgr;
2064         struct ifg_req          *ifg;
2065 	int			 s;
2066 	int			 ret = 0;
2067 
2068 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
2069 		err(1, "socket");
2070 
2071         memset(&ifgr, 0, sizeof(ifgr));
2072         strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ);
2073         if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) {
2074                 if (errno == EINVAL || errno == ENOTTY)
2075 			goto end;
2076 		err(1, "SIOCGIFGROUP");
2077         }
2078 
2079         len = ifgr.ifgr_len;
2080         ifgr.ifgr_groups =
2081             (struct ifg_req *)xcalloc(len/sizeof(struct ifg_req),
2082 		sizeof(struct ifg_req), "is_if_in_group");
2083         if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1)
2084                 err(1, "SIOCGIFGROUP");
2085 
2086         ifg = ifgr.ifgr_groups;
2087         for (; ifg && len >= sizeof(struct ifg_req); ifg++) {
2088                 len -= sizeof(struct ifg_req);
2089 		if (strcmp(ifg->ifgrq_group, groupname) == 0) {
2090 			ret = 1;
2091 			break;
2092 		}
2093         }
2094         free(ifgr.ifgr_groups);
2095 
2096 end:
2097 	close(s);
2098 	return ret;
2099 }
2100 
2101 struct filter *
2102 create_filter(const char *name, const char *path)
2103 {
2104 	struct filter	*f;
2105 
2106 	if (dict_get(&conf->sc_filters, name)) {
2107 		yyerror("filter \"%s\" already defined", name);
2108 		return (NULL);
2109 	}
2110 
2111 	f = xcalloc(1, sizeof(*f), "create_filter");
2112 	strlcpy(f->name, name, sizeof(f->name));
2113 	strlcpy(f->path, path, sizeof(f->path));
2114 
2115 	dict_xset(&conf->sc_filters, name, f);
2116 
2117 	return (f);
2118 }
2119 
2120 static struct filter *
2121 create_filter_chain(const char *name)
2122 {
2123 	struct filter	*f;
2124 
2125 	if (dict_get(&conf->sc_filters, name)) {
2126 		yyerror("filter \"%s\" already defined", name);
2127 		return (NULL);
2128 	}
2129 	f = xcalloc(1, sizeof(*f), "create_filter_chain");
2130 	strlcpy(f->name, name, sizeof(f->name));
2131 	f->chain = 1;
2132 
2133 	dict_xset(&conf->sc_filters, name, f);
2134 
2135 	return (f);
2136 }
2137 
2138 static int
2139 extend_filter_chain(struct filter *f, const char *name)
2140 {
2141 	int	i;
2142 
2143 	if (!f->chain) {
2144 		yyerror("filter \"%s\" is not a chain", f->name);
2145 		return (0);
2146 	}
2147 
2148 	if (dict_get(&conf->sc_filters, name) == NULL) {
2149 		yyerror("undefined filter \"%s\"", name);
2150 		return (0);
2151 	}
2152 	if (dict_get(&conf->sc_filters, name) == f) {
2153 		yyerror("filter chain cannot contain itself");
2154 		return (0);
2155 	}
2156 
2157 	for (i = 0; i < MAX_FILTER_PER_CHAIN; i++) {
2158 		if (f->filters[i][0] == '\0') {
2159 			strlcpy(f->filters[i], name, sizeof(f->filters[i]));
2160 			return (1);
2161 		}
2162 	}
2163 	yyerror("filter chain \"%s\" is full", f->name);
2164 	return (0);
2165 }
2166