xref: /openbsd/usr.sbin/smtpd/parse.y (revision 770825c6)
1 /*	$OpenBSD: parse.y,v 1.280 2020/09/23 18:01:26 martijn 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 #include <sys/un.h>
32 
33 #include <net/if.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <event.h>
41 #include <ifaddrs.h>
42 #include <imsg.h>
43 #include <inttypes.h>
44 #include <limits.h>
45 #include <netdb.h>
46 #include <pwd.h>
47 #include <resolv.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <subagentx.h>
52 #include <syslog.h>
53 #include <unistd.h>
54 #include <util.h>
55 
56 #include <openssl/ssl.h>
57 
58 #include "smtpd.h"
59 #include "ssl.h"
60 #include "log.h"
61 
62 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
63 static struct file {
64 	TAILQ_ENTRY(file)	 entry;
65 	FILE			*stream;
66 	char			*name;
67 	size_t			 ungetpos;
68 	size_t			 ungetsize;
69 	u_char			*ungetbuf;
70 	int			 eof_reached;
71 	int			 lineno;
72 	int			 errors;
73 } *file, *topfile;
74 struct file	*pushfile(const char *, int);
75 int		 popfile(void);
76 int		 check_file_secrecy(int, const char *);
77 int		 yyparse(void);
78 int		 yylex(void);
79 int		 kw_cmp(const void *, const void *);
80 int		 lookup(char *);
81 int		 igetc(void);
82 int		 lgetc(int);
83 void		 lungetc(int);
84 int		 findeol(void);
85 int		 yyerror(const char *, ...)
86     __attribute__((__format__ (printf, 1, 2)))
87     __attribute__((__nonnull__ (1)));
88 
89 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
90 struct sym {
91 	TAILQ_ENTRY(sym)	 entry;
92 	int			 used;
93 	int			 persist;
94 	char			*nam;
95 	char			*val;
96 };
97 int		 symset(const char *, const char *, int);
98 char		*symget(const char *);
99 
100 struct smtpd		*conf = NULL;
101 static int		 errors = 0;
102 
103 struct table		*table = NULL;
104 struct mta_limits	*limits;
105 static struct pki	*pki;
106 static struct ca	*sca;
107 
108 struct dispatcher	*dispatcher;
109 struct rule		*rule;
110 struct filter_proc	*processor;
111 struct filter_config	*filter_config;
112 static uint32_t		 last_dynchain_id = 1;
113 
114 enum listen_options {
115 	LO_FAMILY	= 0x000001,
116 	LO_PORT		= 0x000002,
117 	LO_SSL		= 0x000004,
118 	LO_FILTER      	= 0x000008,
119 	LO_PKI      	= 0x000010,
120 	LO_AUTH      	= 0x000020,
121 	LO_TAG      	= 0x000040,
122 	LO_HOSTNAME   	= 0x000080,
123 	LO_HOSTNAMES   	= 0x000100,
124 	LO_MASKSOURCE  	= 0x000200,
125 	LO_NODSN	= 0x000400,
126 	LO_SENDERS	= 0x000800,
127 	LO_RECEIVEDAUTH = 0x001000,
128 	LO_MASQUERADE	= 0x002000,
129 	LO_CA		= 0x004000,
130 	LO_PROXY       	= 0x008000,
131 };
132 
133 static struct listen_opts {
134 	char	       *ifx;
135 	int		family;
136 	in_port_t	port;
137 	uint16_t	ssl;
138 	char	       *filtername;
139 	char	       *pki;
140 	char	       *ca;
141 	uint16_t       	auth;
142 	struct table   *authtable;
143 	char	       *tag;
144 	char	       *hostname;
145 	struct table   *hostnametable;
146 	struct table   *sendertable;
147 	uint16_t	flags;
148 
149 	uint32_t       	options;
150 } listen_opts;
151 
152 static void	create_sock_listener(struct listen_opts *);
153 static void	create_if_listener(struct listen_opts *);
154 static void	config_listener(struct listener *, struct listen_opts *);
155 static int	host_v4(struct listen_opts *);
156 static int	host_v6(struct listen_opts *);
157 static int	host_dns(struct listen_opts *);
158 static int	interface(struct listen_opts *);
159 
160 int		 delaytonum(char *);
161 int		 is_if_in_group(const char *, const char *);
162 
163 static int config_lo_mask_source(struct listen_opts *);
164 
165 typedef struct {
166 	union {
167 		int64_t		 number;
168 		struct table	*table;
169 		char		*string;
170 		struct host	*host;
171 		struct mailaddr	*maddr;
172 	} v;
173 	int lineno;
174 } YYSTYPE;
175 
176 %}
177 
178 %token	ACTION ADMD AGENTX ALIAS ANY APPLINDEX ARROW AUTH AUTH_OPTIONAL
179 %token	BACKUP BOUNCE BYPASS
180 %token	CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT CONTEXT
181 %token	DATA DATA_LINE DHE DISCONNECT DOMAIN
182 %token	EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY
183 %token	FCRDNS FILTER FOR FORWARD_ONLY FROM
184 %token	GROUP
185 %token	HELO HELO_SRC HOST HOSTNAME HOSTNAMES
186 %token	INCLUDE INET4 INET6
187 %token	JUNK
188 %token	KEY
189 %token	LIMIT LISTEN LMTP LOCAL
190 %token	MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX
191 %token	NO_DSN NO_VERIFY NOOP
192 %token	ON
193 %token	PATH PHASE PKI PORT PROC PROC_EXEC PROXY_V2
194 %token	QUEUE QUIT
195 %token	RCPT_TO RDNS RECIPIENT RECEIVEDAUTH REGEX RELAY REJECT REPORT REWRITE RSET
196 %token	SCHEDULER SENDER SENDERS SMTP SMTP_IN SMTP_OUT SMTPS SOCKET SRC SRS SUB_ADDR_DELIM
197 %token	TABLE TAG TAGGED TLS TLS_REQUIRE TTL
198 %token	USER USERBASE
199 %token	VERIFY VIRTUAL
200 %token	WARN_INTERVAL WRAPPER
201 
202 %token	<v.string>	STRING
203 %token  <v.number>	NUMBER
204 %type	<v.table>	table
205 %type	<v.number>	size negation
206 %type	<v.table>	tables tablenew tableref
207 %%
208 
209 grammar		: /* empty */
210 		| grammar '\n'
211 		| grammar include '\n'
212 		| grammar varset '\n'
213 		| grammar bounce '\n'
214 		| grammar admd '\n'
215 		| grammar ca '\n'
216 		| grammar mda '\n'
217 		| grammar mta '\n'
218 		| grammar pki '\n'
219 		| grammar proc '\n'
220 		| grammar queue '\n'
221 		| grammar scheduler '\n'
222 		| grammar smtp '\n'
223 		| grammar srs '\n'
224 		| grammar listen '\n'
225 		| grammar table '\n'
226 		| grammar dispatcher '\n'
227 		| grammar match '\n'
228 		| grammar filter '\n'
229 		| grammar agentx '\n'
230 		| grammar error '\n'		{ file->errors++; }
231 		;
232 
233 include		: INCLUDE STRING		{
234 			struct file	*nfile;
235 
236 			if ((nfile = pushfile($2, 0)) == NULL) {
237 				yyerror("failed to include file %s", $2);
238 				free($2);
239 				YYERROR;
240 			}
241 			free($2);
242 
243 			file = nfile;
244 			lungetc('\n');
245 		}
246 		;
247 
248 varset		: STRING '=' STRING		{
249 			char *s = $1;
250 			while (*s++) {
251 				if (isspace((unsigned char)*s)) {
252 					yyerror("macro name cannot contain "
253 					    "whitespace");
254 					free($1);
255 					free($3);
256 					YYERROR;
257 				}
258 			}
259 			if (symset($1, $3, 0) == -1)
260 				fatal("cannot store variable");
261 			free($1);
262 			free($3);
263 		}
264 		;
265 
266 comma		: ','
267 		| nl
268 		| /* empty */
269 		;
270 
271 optnl		: '\n' optnl
272 		|
273 		;
274 
275 nl		: '\n' optnl
276 		;
277 
278 negation	: '!'		{ $$ = 1; }
279 		| /* empty */	{ $$ = 0; }
280 		;
281 
282 assign		: '=' | ARROW;
283 
284 
285 keyval		: STRING assign STRING		{
286 			table_add(table, $1, $3);
287 			free($1);
288 			free($3);
289 		}
290 		;
291 
292 keyval_list	: keyval
293 		| keyval comma keyval_list
294 		;
295 
296 stringel	: STRING			{
297 			table_add(table, $1, NULL);
298 			free($1);
299 		}
300 		;
301 
302 string_list	: stringel
303 		| stringel comma string_list
304 		;
305 
306 tableval_list	: string_list			{ }
307 		| keyval_list			{ }
308 		;
309 
310 bounce:
311 BOUNCE WARN_INTERVAL {
312 	memset(conf->sc_bounce_warn, 0, sizeof conf->sc_bounce_warn);
313 } bouncedelays
314 ;
315 
316 
317 admd:
318 ADMD STRING {
319 	size_t i;
320 
321 	for (i = 0; $2[i] != '\0'; i++) {
322 		if (!isprint($2[i])) {
323 			yyerror("not a valid admd");
324 			free($2);
325 			YYERROR;
326 		}
327 	}
328 	conf->sc_admd = $2;
329 };
330 
331 
332 ca:
333 CA STRING {
334 	char buf[HOST_NAME_MAX+1];
335 
336 	/* if not catchall, check that it is a valid domain */
337 	if (strcmp($2, "*") != 0) {
338 		if (!res_hnok($2)) {
339 			yyerror("not a valid domain name: %s", $2);
340 			free($2);
341 			YYERROR;
342 		}
343 	}
344 	xlowercase(buf, $2, sizeof(buf));
345 	free($2);
346 	sca = dict_get(conf->sc_ca_dict, buf);
347 	if (sca == NULL) {
348 		sca = xcalloc(1, sizeof *sca);
349 		(void)strlcpy(sca->ca_name, buf, sizeof(sca->ca_name));
350 		dict_set(conf->sc_ca_dict, sca->ca_name, sca);
351 	}
352 } ca_params
353 ;
354 
355 
356 ca_params_opt:
357 CERT STRING {
358 	sca->ca_cert_file = $2;
359 }
360 ;
361 
362 ca_params:
363 ca_params_opt
364 ;
365 
366 
367 mda:
368 MDA LIMIT limits_mda
369 | MDA WRAPPER STRING STRING {
370 	if (dict_get(conf->sc_mda_wrappers, $3)) {
371 		yyerror("mda wrapper already declared with that name: %s", $3);
372 		YYERROR;
373 	}
374 	dict_set(conf->sc_mda_wrappers, $3, $4);
375 }
376 ;
377 
378 
379 mta:
380 MTA MAX_DEFERRED NUMBER  {
381 	conf->sc_mta_max_deferred = $3;
382 }
383 | MTA LIMIT FOR DOMAIN STRING {
384 	struct mta_limits	*d;
385 
386 	limits = dict_get(conf->sc_limits_dict, $5);
387 	if (limits == NULL) {
388 		limits = xcalloc(1, sizeof(*limits));
389 		dict_xset(conf->sc_limits_dict, $5, limits);
390 		d = dict_xget(conf->sc_limits_dict, "default");
391 		memmove(limits, d, sizeof(*limits));
392 	}
393 	free($5);
394 } limits_mta
395 | MTA LIMIT {
396 	limits = dict_get(conf->sc_limits_dict, "default");
397 } limits_mta
398 ;
399 
400 
401 pki:
402 PKI STRING {
403 	char buf[HOST_NAME_MAX+1];
404 
405 	/* if not catchall, check that it is a valid domain */
406 	if (strcmp($2, "*") != 0) {
407 		if (!res_hnok($2)) {
408 			yyerror("not a valid domain name: %s", $2);
409 			free($2);
410 			YYERROR;
411 		}
412 	}
413 	xlowercase(buf, $2, sizeof(buf));
414 	free($2);
415 	pki = dict_get(conf->sc_pki_dict, buf);
416 	if (pki == NULL) {
417 		pki = xcalloc(1, sizeof *pki);
418 		(void)strlcpy(pki->pki_name, buf, sizeof(pki->pki_name));
419 		dict_set(conf->sc_pki_dict, pki->pki_name, pki);
420 	}
421 } pki_params
422 ;
423 
424 pki_params_opt:
425 CERT STRING {
426 	pki->pki_cert_file = $2;
427 }
428 | KEY STRING {
429 	pki->pki_key_file = $2;
430 }
431 | DHE STRING {
432 	if (strcasecmp($2, "none") == 0)
433 		pki->pki_dhe = 0;
434 	else if (strcasecmp($2, "auto") == 0)
435 		pki->pki_dhe = 1;
436 	else if (strcasecmp($2, "legacy") == 0)
437 		pki->pki_dhe = 2;
438 	else {
439 		yyerror("invalid DHE keyword: %s", $2);
440 		free($2);
441 		YYERROR;
442 	}
443 	free($2);
444 }
445 ;
446 
447 
448 pki_params:
449 pki_params_opt pki_params
450 | /* empty */
451 ;
452 
453 
454 proc:
455 PROC STRING STRING {
456 	if (dict_get(conf->sc_filter_processes_dict, $2)) {
457 		yyerror("processor already exists with that name: %s", $2);
458 		free($2);
459 		free($3);
460 		YYERROR;
461 	}
462 	processor = xcalloc(1, sizeof *processor);
463 	processor->command = $3;
464 } proc_params {
465 	dict_set(conf->sc_filter_processes_dict, $2, processor);
466 	processor = NULL;
467 }
468 ;
469 
470 
471 proc_params_opt:
472 USER STRING {
473 	if (processor->user) {
474 		yyerror("user already specified for this processor");
475 		free($2);
476 		YYERROR;
477 	}
478 	processor->user = $2;
479 }
480 | GROUP STRING {
481 	if (processor->group) {
482 		yyerror("group already specified for this processor");
483 		free($2);
484 		YYERROR;
485 	}
486 	processor->group = $2;
487 }
488 | CHROOT STRING {
489 	if (processor->chroot) {
490 		yyerror("chroot already specified for this processor");
491 		free($2);
492 		YYERROR;
493 	}
494 	processor->chroot = $2;
495 }
496 ;
497 
498 proc_params:
499 proc_params_opt proc_params
500 | /* empty */
501 ;
502 
503 
504 queue:
505 QUEUE COMPRESSION {
506 	conf->sc_queue_flags |= QUEUE_COMPRESSION;
507 }
508 | QUEUE ENCRYPTION {
509 	conf->sc_queue_flags |= QUEUE_ENCRYPTION;
510 }
511 | QUEUE ENCRYPTION STRING {
512 	if (strcasecmp($3, "stdin") == 0 || strcasecmp($3, "-") == 0) {
513 		conf->sc_queue_key = "stdin";
514 		free($3);
515 	}
516 	else
517 		conf->sc_queue_key = $3;
518 	conf->sc_queue_flags |= QUEUE_ENCRYPTION;
519 }
520 | QUEUE TTL STRING {
521 	conf->sc_ttl = delaytonum($3);
522 	if (conf->sc_ttl == -1) {
523 		yyerror("invalid ttl delay: %s", $3);
524 		free($3);
525 		YYERROR;
526 	}
527 	free($3);
528 }
529 ;
530 
531 
532 scheduler:
533 SCHEDULER LIMIT limits_scheduler
534 ;
535 
536 
537 smtp:
538 SMTP LIMIT limits_smtp
539 | SMTP CIPHERS STRING {
540 	conf->sc_tls_ciphers = $3;
541 }
542 | SMTP MAX_MESSAGE_SIZE size {
543 	conf->sc_maxsize = $3;
544 }
545 | SMTP SUB_ADDR_DELIM STRING {
546 	if (strlen($3) != 1) {
547 		yyerror("subaddressing-delimiter must be one character");
548 		free($3);
549 		YYERROR;
550 	}
551 	if (isspace((unsigned char)*$3) || !isprint((unsigned char)*$3) || *$3 == '@') {
552 		yyerror("sub-addr-delim uses invalid character");
553 		free($3);
554 		YYERROR;
555 	}
556 	conf->sc_subaddressing_delim = $3;
557 }
558 ;
559 
560 srs:
561 SRS KEY STRING {
562 	conf->sc_srs_key = $3;
563 }
564 | SRS KEY BACKUP STRING {
565 	conf->sc_srs_key_backup = $4;
566 }
567 | SRS TTL STRING {
568 	conf->sc_srs_ttl = delaytonum($3);
569 	if (conf->sc_srs_ttl == -1) {
570 		yyerror("ttl delay \"%s\" is invalid", $3);
571 		free($3);
572 		YYERROR;
573 	}
574 
575 	conf->sc_srs_ttl /= 86400;
576 	if (conf->sc_srs_ttl == 0) {
577 		yyerror("ttl delay \"%s\" is too short", $3);
578 		free($3);
579 		YYERROR;
580 	}
581 	free($3);
582 }
583 ;
584 
585 
586 dispatcher_local_option:
587 USER STRING {
588 	if (dispatcher->u.local.is_mbox) {
589 		yyerror("user may not be specified for this dispatcher");
590 		YYERROR;
591 	}
592 
593 	if (dispatcher->u.local.forward_only) {
594 		yyerror("user may not be specified for forward-only");
595 		YYERROR;
596 	}
597 
598 	if (dispatcher->u.local.expand_only) {
599 		yyerror("user may not be specified for expand-only");
600 		YYERROR;
601 	}
602 
603 	if (dispatcher->u.local.user) {
604 		yyerror("user already specified for this dispatcher");
605 		YYERROR;
606 	}
607 
608 	dispatcher->u.local.user = $2;
609 }
610 | ALIAS tables {
611 	struct table   *t = $2;
612 
613 	if (dispatcher->u.local.table_alias) {
614 		yyerror("alias mapping already specified for this dispatcher");
615 		YYERROR;
616 	}
617 
618 	if (dispatcher->u.local.table_virtual) {
619 		yyerror("virtual mapping already specified for this dispatcher");
620 		YYERROR;
621 	}
622 
623 	if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
624 		yyerror("table \"%s\" may not be used for alias lookups",
625 		    t->t_name);
626 		YYERROR;
627 	}
628 
629 	dispatcher->u.local.table_alias = strdup(t->t_name);
630 }
631 | VIRTUAL tables {
632 	struct table   *t = $2;
633 
634 	if (dispatcher->u.local.table_virtual) {
635 		yyerror("virtual mapping already specified for this dispatcher");
636 		YYERROR;
637 	}
638 
639 	if (dispatcher->u.local.table_alias) {
640 		yyerror("alias mapping already specified for this dispatcher");
641 		YYERROR;
642 	}
643 
644 	if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ALIAS)) {
645 		yyerror("table \"%s\" may not be used for virtual lookups",
646 		    t->t_name);
647 		YYERROR;
648 	}
649 
650 	dispatcher->u.local.table_virtual = strdup(t->t_name);
651 }
652 | USERBASE tables {
653 	struct table   *t = $2;
654 
655 	if (dispatcher->u.local.table_userbase) {
656 		yyerror("userbase mapping already specified for this dispatcher");
657 		YYERROR;
658 	}
659 
660 	if (!table_check_use(t, T_DYNAMIC|T_HASH, K_USERINFO)) {
661 		yyerror("table \"%s\" may not be used for userbase lookups",
662 		    t->t_name);
663 		YYERROR;
664 	}
665 
666 	dispatcher->u.local.table_userbase = strdup(t->t_name);
667 }
668 | WRAPPER STRING {
669 	if (! dict_get(conf->sc_mda_wrappers, $2)) {
670 		yyerror("no mda wrapper with that name: %s", $2);
671 		YYERROR;
672 	}
673 	dispatcher->u.local.mda_wrapper = $2;
674 }
675 ;
676 
677 dispatcher_local_options:
678 dispatcher_local_option dispatcher_local_options
679 | /* empty */
680 ;
681 
682 dispatcher_local:
683 MBOX {
684 	dispatcher->u.local.is_mbox = 1;
685 	asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.local -f %%{mbox.from} -- %%{user.username}");
686 } dispatcher_local_options
687 | MAILDIR {
688 	asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir");
689 } dispatcher_local_options
690 | MAILDIR JUNK {
691 	asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir -j");
692 } dispatcher_local_options
693 | MAILDIR STRING {
694 	if (strncmp($2, "~/", 2) == 0)
695 		asprintf(&dispatcher->u.local.command,
696 		    "/usr/libexec/mail.maildir \"%%{user.directory}/%s\"", $2+2);
697 	else
698 		asprintf(&dispatcher->u.local.command,
699 		    "/usr/libexec/mail.maildir \"%s\"", $2);
700 } dispatcher_local_options
701 | MAILDIR STRING JUNK {
702 	if (strncmp($2, "~/", 2) == 0)
703 		asprintf(&dispatcher->u.local.command,
704 		    "/usr/libexec/mail.maildir -j \"%%{user.directory}/%s\"", $2+2);
705 	else
706 		asprintf(&dispatcher->u.local.command,
707 		    "/usr/libexec/mail.maildir -j \"%s\"", $2);
708 } dispatcher_local_options
709 | LMTP STRING {
710 	asprintf(&dispatcher->u.local.command,
711 	    "/usr/libexec/mail.lmtp -d %s -u", $2);
712 	dispatcher->u.local.user = SMTPD_USER;
713 } dispatcher_local_options
714 | LMTP STRING RCPT_TO {
715 	asprintf(&dispatcher->u.local.command,
716 	    "/usr/libexec/mail.lmtp -d %s -r", $2);
717 	dispatcher->u.local.user = SMTPD_USER;
718 } dispatcher_local_options
719 | MDA STRING {
720 	asprintf(&dispatcher->u.local.command,
721 	    "/usr/libexec/mail.mda \"%s\"", $2);
722 } dispatcher_local_options
723 | FORWARD_ONLY {
724 	dispatcher->u.local.forward_only = 1;
725 } dispatcher_local_options
726 | EXPAND_ONLY {
727 	dispatcher->u.local.expand_only = 1;
728 } dispatcher_local_options
729 
730 ;
731 
732 dispatcher_remote_option:
733 HELO STRING {
734 	if (dispatcher->u.remote.helo) {
735 		yyerror("helo already specified for this dispatcher");
736 		YYERROR;
737 	}
738 
739 	dispatcher->u.remote.helo = $2;
740 }
741 | HELO_SRC tables {
742 	struct table   *t = $2;
743 
744 	if (dispatcher->u.remote.helo_source) {
745 		yyerror("helo-source mapping already specified for this dispatcher");
746 		YYERROR;
747 	}
748 	if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
749 		yyerror("table \"%s\" may not be used for helo-source lookups",
750 		    t->t_name);
751 		YYERROR;
752 	}
753 
754 	dispatcher->u.remote.helo_source = strdup(t->t_name);
755 }
756 | PKI STRING {
757 	if (dispatcher->u.remote.pki) {
758 		yyerror("pki already specified for this dispatcher");
759 		YYERROR;
760 	}
761 
762 	dispatcher->u.remote.pki = $2;
763 }
764 | CA STRING {
765 	if (dispatcher->u.remote.ca) {
766 		yyerror("ca already specified for this dispatcher");
767 		YYERROR;
768 	}
769 
770 	dispatcher->u.remote.ca = $2;
771 }
772 | SRC tables {
773 	struct table   *t = $2;
774 
775 	if (dispatcher->u.remote.source) {
776 		yyerror("source mapping already specified for this dispatcher");
777 		YYERROR;
778 	}
779 
780 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_SOURCE)) {
781 		yyerror("table \"%s\" may not be used for source lookups",
782 		    t->t_name);
783 		YYERROR;
784 	}
785 
786 	dispatcher->u.remote.source = strdup(t->t_name);
787 }
788 | MAIL_FROM STRING {
789 	if (dispatcher->u.remote.mail_from) {
790 		yyerror("mail-from already specified for this dispatcher");
791 		YYERROR;
792 	}
793 
794 	dispatcher->u.remote.mail_from = $2;
795 }
796 | BACKUP MX STRING {
797 	if (dispatcher->u.remote.backup) {
798 		yyerror("backup already specified for this dispatcher");
799 		YYERROR;
800 	}
801 	if (dispatcher->u.remote.smarthost) {
802 		yyerror("backup and host are mutually exclusive");
803 		YYERROR;
804 	}
805 
806 	dispatcher->u.remote.backup = 1;
807 	dispatcher->u.remote.backupmx = $3;
808 }
809 | BACKUP {
810 	if (dispatcher->u.remote.backup) {
811 		yyerror("backup already specified for this dispatcher");
812 		YYERROR;
813 	}
814 	if (dispatcher->u.remote.smarthost) {
815 		yyerror("backup and host are mutually exclusive");
816 		YYERROR;
817 	}
818 
819 	dispatcher->u.remote.backup = 1;
820 }
821 | HOST tables {
822 	struct table   *t = $2;
823 
824 	if (dispatcher->u.remote.smarthost) {
825 		yyerror("host mapping already specified for this dispatcher");
826 		YYERROR;
827 	}
828 	if (dispatcher->u.remote.backup) {
829 		yyerror("backup and host are mutually exclusive");
830 		YYERROR;
831 	}
832 
833 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_RELAYHOST)) {
834 		yyerror("table \"%s\" may not be used for host lookups",
835 		    t->t_name);
836 		YYERROR;
837 	}
838 
839 	dispatcher->u.remote.smarthost = strdup(t->t_name);
840 }
841 | DOMAIN tables {
842 	struct table   *t = $2;
843 
844 	if (dispatcher->u.remote.smarthost) {
845 		yyerror("host mapping already specified for this dispatcher");
846 		YYERROR;
847 	}
848 	if (dispatcher->u.remote.backup) {
849 		yyerror("backup and domain are mutually exclusive");
850 		YYERROR;
851 	}
852 
853 	if (!table_check_use(t, T_DYNAMIC|T_HASH, K_RELAYHOST)) {
854 		yyerror("table \"%s\" may not be used for host lookups",
855 		    t->t_name);
856 		YYERROR;
857 	}
858 
859 	dispatcher->u.remote.smarthost = strdup(t->t_name);
860 	dispatcher->u.remote.smarthost_domain = 1;
861 }
862 | TLS {
863 	if (dispatcher->u.remote.tls_required == 1) {
864 		yyerror("tls already specified for this dispatcher");
865 		YYERROR;
866 	}
867 
868 	dispatcher->u.remote.tls_required = 1;
869 }
870 | TLS NO_VERIFY {
871 	if (dispatcher->u.remote.tls_required == 1) {
872 		yyerror("tls already specified for this dispatcher");
873 		YYERROR;
874 	}
875 
876 	dispatcher->u.remote.tls_required = 1;
877 	dispatcher->u.remote.tls_noverify = 1;
878 }
879 | AUTH tables {
880 	struct table   *t = $2;
881 
882 	if (dispatcher->u.remote.smarthost == NULL) {
883 		yyerror("auth may not be specified without host on a dispatcher");
884 		YYERROR;
885 	}
886 
887 	if (dispatcher->u.remote.auth) {
888 		yyerror("auth mapping already specified for this dispatcher");
889 		YYERROR;
890 	}
891 
892 	if (!table_check_use(t, T_DYNAMIC|T_HASH, K_CREDENTIALS)) {
893 		yyerror("table \"%s\" may not be used for auth lookups",
894 		    t->t_name);
895 		YYERROR;
896 	}
897 
898 	dispatcher->u.remote.auth = strdup(t->t_name);
899 }
900 | FILTER STRING {
901 	struct filter_config *fc;
902 
903 	if (dispatcher->u.remote.filtername) {
904 		yyerror("filter already specified for this dispatcher");
905 		YYERROR;
906 	}
907 
908 	if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
909 		yyerror("no filter exist with that name: %s", $2);
910 		free($2);
911 		YYERROR;
912 	}
913 	fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT;
914 	dispatcher->u.remote.filtername = $2;
915 }
916 | FILTER {
917 	char	buffer[128];
918 	char	*filtername;
919 
920 	if (dispatcher->u.remote.filtername) {
921 		yyerror("filter already specified for this dispatcher");
922 		YYERROR;
923 	}
924 
925 	do {
926 		(void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++);
927 	} while (dict_check(conf->sc_filters_dict, buffer));
928 
929 	filtername = xstrdup(buffer);
930 	filter_config = xcalloc(1, sizeof *filter_config);
931 	filter_config->filter_type = FILTER_TYPE_CHAIN;
932 	filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT;
933 	dict_init(&filter_config->chain_procs);
934 	dispatcher->u.remote.filtername = filtername;
935 } '{' filter_list '}' {
936 	dict_set(conf->sc_filters_dict, dispatcher->u.remote.filtername, filter_config);
937 	filter_config = NULL;
938 }
939 | SRS {
940 	if (conf->sc_srs_key == NULL) {
941 		yyerror("an srs key is required for srs to be specified in an action");
942 		YYERROR;
943 	}
944 	if (dispatcher->u.remote.srs == 1) {
945 		yyerror("srs already specified for this dispatcher");
946 		YYERROR;
947 	}
948 
949 	dispatcher->u.remote.srs = 1;
950 }
951 ;
952 
953 dispatcher_remote_options:
954 dispatcher_remote_option dispatcher_remote_options
955 | /* empty */
956 ;
957 
958 dispatcher_remote :
959 RELAY dispatcher_remote_options
960 ;
961 
962 dispatcher_type:
963 dispatcher_local {
964 	dispatcher->type = DISPATCHER_LOCAL;
965 }
966 | dispatcher_remote {
967 	dispatcher->type = DISPATCHER_REMOTE;
968 }
969 ;
970 
971 dispatcher_option:
972 TTL STRING {
973 	if (dispatcher->ttl) {
974 		yyerror("ttl already specified for this dispatcher");
975 		YYERROR;
976 	}
977 
978 	dispatcher->ttl = delaytonum($2);
979 	if (dispatcher->ttl == -1) {
980 		yyerror("ttl delay \"%s\" is invalid", $2);
981 		free($2);
982 		YYERROR;
983 	}
984 	free($2);
985 }
986 ;
987 
988 dispatcher_options:
989 dispatcher_option dispatcher_options
990 | /* empty */
991 ;
992 
993 dispatcher:
994 ACTION STRING {
995 	if (dict_get(conf->sc_dispatchers, $2)) {
996 		yyerror("dispatcher already declared with that name: %s", $2);
997 		YYERROR;
998 	}
999 	dispatcher = xcalloc(1, sizeof *dispatcher);
1000 } dispatcher_type dispatcher_options {
1001 	if (dispatcher->type == DISPATCHER_LOCAL)
1002 		if (dispatcher->u.local.table_userbase == NULL)
1003 			dispatcher->u.local.table_userbase = "<getpwnam>";
1004 	dict_set(conf->sc_dispatchers, $2, dispatcher);
1005 	dispatcher = NULL;
1006 }
1007 ;
1008 
1009 match_option:
1010 negation TAG tables {
1011 	struct table   *t = $3;
1012 
1013 	if (rule->flag_tag) {
1014 		yyerror("tag already specified for this rule");
1015 		YYERROR;
1016 	}
1017 
1018 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING)) {
1019 		yyerror("table \"%s\" may not be used for tag lookups",
1020 		    t->t_name);
1021 		YYERROR;
1022 	}
1023 
1024 	rule->flag_tag = $1 ? -1 : 1;
1025 	rule->table_tag = strdup(t->t_name);
1026 }
1027 |
1028 negation TAG REGEX tables {
1029 	struct table   *t = $4;
1030 
1031 	if (rule->flag_tag) {
1032 		yyerror("tag already specified for this rule");
1033 		YYERROR;
1034 	}
1035 
1036 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1037 		yyerror("table \"%s\" may not be used for tag lookups",
1038 		    t->t_name);
1039 		YYERROR;
1040 	}
1041 
1042 	rule->flag_tag = $1 ? -1 : 1;
1043 	rule->flag_tag_regex = 1;
1044 	rule->table_tag = strdup(t->t_name);
1045 }
1046 
1047 | negation HELO tables {
1048 	struct table   *t = $3;
1049 
1050 	if (rule->flag_smtp_helo) {
1051 		yyerror("helo already specified for this rule");
1052 		YYERROR;
1053 	}
1054 
1055 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
1056 		yyerror("table \"%s\" may not be used for helo lookups",
1057 		    t->t_name);
1058 		YYERROR;
1059 	}
1060 
1061 	rule->flag_smtp_helo = $1 ? -1 : 1;
1062 	rule->table_smtp_helo = strdup(t->t_name);
1063 }
1064 | negation HELO REGEX tables {
1065 	struct table   *t = $4;
1066 
1067 	if (rule->flag_smtp_helo) {
1068 		yyerror("helo already specified for this rule");
1069 		YYERROR;
1070 	}
1071 
1072 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1073 		yyerror("table \"%s\" may not be used for helo lookups",
1074 		    t->t_name);
1075 		YYERROR;
1076 	}
1077 
1078 	rule->flag_smtp_helo = $1 ? -1 : 1;
1079 	rule->flag_smtp_helo_regex = 1;
1080 	rule->table_smtp_helo = strdup(t->t_name);
1081 }
1082 | negation TLS {
1083 	if (rule->flag_smtp_starttls) {
1084 		yyerror("tls already specified for this rule");
1085 		YYERROR;
1086 	}
1087 	rule->flag_smtp_starttls = $1 ? -1 : 1;
1088 }
1089 | negation AUTH {
1090 	if (rule->flag_smtp_auth) {
1091 		yyerror("auth already specified for this rule");
1092 		YYERROR;
1093 	}
1094 	rule->flag_smtp_auth = $1 ? -1 : 1;
1095 }
1096 | negation AUTH tables {
1097 	struct table   *t = $3;
1098 
1099 	if (rule->flag_smtp_auth) {
1100 		yyerror("auth already specified for this rule");
1101 		YYERROR;
1102 	}
1103 
1104        	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) {
1105 		yyerror("table \"%s\" may not be used for auth lookups",
1106 		    t->t_name);
1107 		YYERROR;
1108 	}
1109 
1110 	rule->flag_smtp_auth = $1 ? -1 : 1;
1111 	rule->table_smtp_auth = strdup(t->t_name);
1112 }
1113 | negation AUTH REGEX tables {
1114 	struct table   *t = $4;
1115 
1116 	if (rule->flag_smtp_auth) {
1117 		yyerror("auth already specified for this rule");
1118 		YYERROR;
1119 	}
1120 
1121 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1122 		yyerror("table \"%s\" may not be used for auth lookups",
1123 		    t->t_name);
1124 		YYERROR;
1125 	}
1126 
1127 	rule->flag_smtp_auth = $1 ? -1 : 1;
1128 	rule->flag_smtp_auth_regex = 1;
1129 	rule->table_smtp_auth = strdup(t->t_name);
1130 }
1131 | negation MAIL_FROM tables {
1132 	struct table   *t = $3;
1133 
1134 	if (rule->flag_smtp_mail_from) {
1135 		yyerror("mail-from already specified for this rule");
1136 		YYERROR;
1137 	}
1138 
1139 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
1140 		yyerror("table \"%s\" may not be used for mail-from lookups",
1141 		    t->t_name);
1142 		YYERROR;
1143 	}
1144 
1145 	rule->flag_smtp_mail_from = $1 ? -1 : 1;
1146 	rule->table_smtp_mail_from = strdup(t->t_name);
1147 }
1148 | negation MAIL_FROM REGEX tables {
1149 	struct table   *t = $4;
1150 
1151 	if (rule->flag_smtp_mail_from) {
1152 		yyerror("mail-from already specified for this rule");
1153 		YYERROR;
1154 	}
1155 
1156 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1157 		yyerror("table \"%s\" may not be used for mail-from lookups",
1158 		    t->t_name);
1159 		YYERROR;
1160 	}
1161 
1162 	rule->flag_smtp_mail_from = $1 ? -1 : 1;
1163 	rule->flag_smtp_mail_from_regex = 1;
1164 	rule->table_smtp_mail_from = strdup(t->t_name);
1165 }
1166 | negation RCPT_TO tables {
1167 	struct table   *t = $3;
1168 
1169 	if (rule->flag_smtp_rcpt_to) {
1170 		yyerror("rcpt-to already specified for this rule");
1171 		YYERROR;
1172 	}
1173 
1174 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
1175 		yyerror("table \"%s\" may not be used for rcpt-to lookups",
1176 		    t->t_name);
1177 		YYERROR;
1178 	}
1179 
1180 	rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
1181 	rule->table_smtp_rcpt_to = strdup(t->t_name);
1182 }
1183 | negation RCPT_TO REGEX tables {
1184 	struct table   *t = $4;
1185 
1186 	if (rule->flag_smtp_rcpt_to) {
1187 		yyerror("rcpt-to already specified for this rule");
1188 		YYERROR;
1189 	}
1190 
1191 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1192 		yyerror("table \"%s\" may not be used for rcpt-to lookups",
1193 		    t->t_name);
1194 		YYERROR;
1195 	}
1196 
1197 	rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
1198 	rule->flag_smtp_rcpt_to_regex = 1;
1199 	rule->table_smtp_rcpt_to = strdup(t->t_name);
1200 }
1201 
1202 | negation FROM SOCKET {
1203 	if (rule->flag_from) {
1204 		yyerror("from already specified for this rule");
1205 		YYERROR;
1206 	}
1207 	rule->flag_from = $1 ? -1 : 1;
1208 	rule->flag_from_socket = 1;
1209 }
1210 | negation FROM LOCAL {
1211 	struct table	*t = table_find(conf, "<localhost>");
1212 
1213 	if (rule->flag_from) {
1214 		yyerror("from already specified for this rule");
1215 		YYERROR;
1216 	}
1217 	rule->flag_from = $1 ? -1 : 1;
1218 	rule->table_from = strdup(t->t_name);
1219 }
1220 | negation FROM ANY {
1221 	struct table	*t = table_find(conf, "<anyhost>");
1222 
1223 	if (rule->flag_from) {
1224 		yyerror("from already specified for this rule");
1225 		YYERROR;
1226 	}
1227 	rule->flag_from = $1 ? -1 : 1;
1228 	rule->table_from = strdup(t->t_name);
1229 }
1230 | negation FROM SRC tables {
1231 	struct table   *t = $4;
1232 
1233 	if (rule->flag_from) {
1234 		yyerror("from already specified for this rule");
1235 		YYERROR;
1236 	}
1237 
1238 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_NETADDR)) {
1239 		yyerror("table \"%s\" may not be used for from lookups",
1240 		    t->t_name);
1241 		YYERROR;
1242 	}
1243 
1244 	rule->flag_from = $1 ? -1 : 1;
1245 	rule->table_from = strdup(t->t_name);
1246 }
1247 | negation FROM SRC REGEX tables {
1248 	struct table   *t = $5;
1249 
1250 	if (rule->flag_from) {
1251 		yyerror("from already specified for this rule");
1252 		YYERROR;
1253 	}
1254 
1255 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1256 		yyerror("table \"%s\" may not be used for from lookups",
1257 		    t->t_name);
1258 		YYERROR;
1259 	}
1260 
1261 	rule->flag_from = $1 ? -1 : 1;
1262 	rule->flag_from_regex = 1;
1263 	rule->table_from = strdup(t->t_name);
1264 }
1265 | negation FROM RDNS {
1266 	if (rule->flag_from) {
1267 		yyerror("from already specified for this rule");
1268 		YYERROR;
1269 	}
1270 	rule->flag_from = $1 ? -1 : 1;
1271 	rule->flag_from_rdns = 1;
1272 }
1273 | negation FROM RDNS tables {
1274 	struct table   *t = $4;
1275 
1276 	if (rule->flag_from) {
1277 		yyerror("from already specified for this rule");
1278 		YYERROR;
1279 	}
1280 
1281 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
1282 		yyerror("table \"%s\" may not be used for rdns lookups",
1283 		    t->t_name);
1284 		YYERROR;
1285 	}
1286 
1287 	rule->flag_from = $1 ? -1 : 1;
1288 	rule->flag_from_rdns = 1;
1289 	rule->table_from = strdup(t->t_name);
1290 }
1291 | negation FROM RDNS REGEX tables {
1292 	struct table   *t = $5;
1293 
1294 	if (rule->flag_from) {
1295 		yyerror("from already specified for this rule");
1296 		YYERROR;
1297 	}
1298 
1299 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
1300 		yyerror("table \"%s\" may not be used for rdns lookups",
1301 		    t->t_name);
1302 		YYERROR;
1303 	}
1304 
1305 	rule->flag_from = $1 ? -1 : 1;
1306 	rule->flag_from_regex = 1;
1307 	rule->flag_from_rdns = 1;
1308 	rule->table_from = strdup(t->t_name);
1309 }
1310 
1311 | negation FROM AUTH {
1312 	struct table	*anyhost = table_find(conf, "<anyhost>");
1313 
1314 	if (rule->flag_from) {
1315 		yyerror("from already specified for this rule");
1316 		YYERROR;
1317 	}
1318 
1319 	rule->flag_from = 1;
1320 	rule->table_from = strdup(anyhost->t_name);
1321 	rule->flag_smtp_auth = $1 ? -1 : 1;
1322 }
1323 | negation FROM AUTH tables {
1324 	struct table	*anyhost = table_find(conf, "<anyhost>");
1325 	struct table	*t = $4;
1326 
1327 	if (rule->flag_from) {
1328 		yyerror("from already specified for this rule");
1329 		YYERROR;
1330 	}
1331 
1332        	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) {
1333 		yyerror("table \"%s\" may not be used for from lookups",
1334 		    t->t_name);
1335 		YYERROR;
1336 	}
1337 
1338 	rule->flag_from = 1;
1339 	rule->table_from = strdup(anyhost->t_name);
1340 	rule->flag_smtp_auth = $1 ? -1 : 1;
1341 	rule->table_smtp_auth = strdup(t->t_name);
1342 }
1343 | negation FROM AUTH REGEX tables {
1344 	struct table	*anyhost = table_find(conf, "<anyhost>");
1345 	struct table	*t = $5;
1346 
1347 	if (rule->flag_from) {
1348 		yyerror("from already specified for this rule");
1349 		YYERROR;
1350 	}
1351 
1352 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1353         	yyerror("table \"%s\" may not be used for from lookups",
1354 		    t->t_name);
1355 		YYERROR;
1356 	}
1357 
1358 	rule->flag_from = 1;
1359 	rule->table_from = strdup(anyhost->t_name);
1360 	rule->flag_smtp_auth = $1 ? -1 : 1;
1361 	rule->flag_smtp_auth_regex = 1;
1362 	rule->table_smtp_auth = strdup(t->t_name);
1363 }
1364 
1365 | negation FROM MAIL_FROM tables {
1366 	struct table	*anyhost = table_find(conf, "<anyhost>");
1367 	struct table	*t = $4;
1368 
1369 	if (rule->flag_from) {
1370 		yyerror("from already specified for this rule");
1371 		YYERROR;
1372 	}
1373 
1374 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
1375 		yyerror("table \"%s\" may not be used for from lookups",
1376 		    t->t_name);
1377 		YYERROR;
1378 	}
1379 
1380 	rule->flag_from = 1;
1381 	rule->table_from = strdup(anyhost->t_name);
1382 	rule->flag_smtp_mail_from = $1 ? -1 : 1;
1383 	rule->table_smtp_mail_from = strdup(t->t_name);
1384 }
1385 | negation FROM MAIL_FROM REGEX tables {
1386 	struct table	*anyhost = table_find(conf, "<anyhost>");
1387 	struct table	*t = $5;
1388 
1389 	if (rule->flag_from) {
1390 		yyerror("from already specified for this rule");
1391 		YYERROR;
1392 	}
1393 
1394 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1395 		yyerror("table \"%s\" may not be used for from lookups",
1396 		    t->t_name);
1397 		YYERROR;
1398 	}
1399 
1400 	rule->flag_from = 1;
1401 	rule->table_from = strdup(anyhost->t_name);
1402 	rule->flag_smtp_mail_from = $1 ? -1 : 1;
1403 	rule->flag_smtp_mail_from_regex = 1;
1404 	rule->table_smtp_mail_from = strdup(t->t_name);
1405 }
1406 
1407 | negation FOR LOCAL {
1408 	struct table   *t = table_find(conf, "<localnames>");
1409 
1410 	if (rule->flag_for) {
1411 		yyerror("for already specified for this rule");
1412 		YYERROR;
1413 	}
1414 	rule->flag_for = $1 ? -1 : 1;
1415 	rule->table_for = strdup(t->t_name);
1416 }
1417 | negation FOR ANY {
1418 	struct table   *t = table_find(conf, "<anydestination>");
1419 
1420 	if (rule->flag_for) {
1421 		yyerror("for already specified for this rule");
1422 		YYERROR;
1423 	}
1424 	rule->flag_for = $1 ? -1 : 1;
1425 	rule->table_for = strdup(t->t_name);
1426 }
1427 | negation FOR DOMAIN tables {
1428 	struct table   *t = $4;
1429 
1430 	if (rule->flag_for) {
1431 		yyerror("for already specified for this rule");
1432 		YYERROR;
1433 	}
1434 
1435 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_DOMAIN)) {
1436 		yyerror("table \"%s\" may not be used for 'for' lookups",
1437 		    t->t_name);
1438 		YYERROR;
1439 	}
1440 
1441 	rule->flag_for = $1 ? -1 : 1;
1442 	rule->table_for = strdup(t->t_name);
1443 }
1444 | negation FOR DOMAIN REGEX tables {
1445 	struct table   *t = $5;
1446 
1447 	if (rule->flag_for) {
1448 		yyerror("for already specified for this rule");
1449 		YYERROR;
1450 	}
1451 
1452 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1453 		yyerror("table \"%s\" may not be used for 'for' lookups",
1454 		    t->t_name);
1455 		YYERROR;
1456 	}
1457 
1458 	rule->flag_for = $1 ? -1 : 1;
1459 	rule->flag_for_regex = 1;
1460 	rule->table_for = strdup(t->t_name);
1461 }
1462 | negation FOR RCPT_TO tables {
1463 	struct table	*anyhost = table_find(conf, "<anydestination>");
1464 	struct table	*t = $4;
1465 
1466 	if (rule->flag_for) {
1467 		yyerror("for already specified for this rule");
1468 		YYERROR;
1469 	}
1470 
1471 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
1472 		yyerror("table \"%s\" may not be used for for lookups",
1473 		    t->t_name);
1474 		YYERROR;
1475 	}
1476 
1477 	rule->flag_for = 1;
1478 	rule->table_for = strdup(anyhost->t_name);
1479 	rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
1480 	rule->table_smtp_rcpt_to = strdup(t->t_name);
1481 }
1482 | negation FOR RCPT_TO REGEX tables {
1483 	struct table	*anyhost = table_find(conf, "<anydestination>");
1484 	struct table	*t = $5;
1485 
1486 	if (rule->flag_for) {
1487 		yyerror("for already specified for this rule");
1488 		YYERROR;
1489 	}
1490 
1491 	if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
1492 		yyerror("table \"%s\" may not be used for for lookups",
1493 		    t->t_name);
1494 		YYERROR;
1495 	}
1496 
1497 	rule->flag_for = 1;
1498 	rule->table_for = strdup(anyhost->t_name);
1499 	rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
1500 	rule->flag_smtp_rcpt_to_regex = 1;
1501 	rule->table_smtp_rcpt_to = strdup(t->t_name);
1502 }
1503 ;
1504 
1505 match_options:
1506 match_option match_options
1507 | /* empty */
1508 ;
1509 
1510 match_dispatcher:
1511 STRING {
1512 	if (dict_get(conf->sc_dispatchers, $1) == NULL) {
1513 		yyerror("no such dispatcher: %s", $1);
1514 		YYERROR;
1515 	}
1516 	rule->dispatcher = $1;
1517 }
1518 ;
1519 
1520 action:
1521 REJECT {
1522 	rule->reject = 1;
1523 }
1524 | ACTION match_dispatcher
1525 ;
1526 
1527 match:
1528 MATCH {
1529 	rule = xcalloc(1, sizeof *rule);
1530 } match_options action {
1531 	if (!rule->flag_from) {
1532 		rule->table_from = strdup("<localhost>");
1533 		rule->flag_from = 1;
1534 	}
1535 	if (!rule->flag_for) {
1536 		rule->table_for = strdup("<localnames>");
1537 		rule->flag_for = 1;
1538 	}
1539 	TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
1540 	rule = NULL;
1541 }
1542 ;
1543 
1544 filter_action_builtin:
1545 filter_action_builtin_nojunk
1546 | JUNK {
1547 	filter_config->junk = 1;
1548 }
1549 | BYPASS {
1550 	filter_config->bypass = 1;
1551 }
1552 ;
1553 
1554 filter_action_builtin_nojunk:
1555 REJECT STRING {
1556 	filter_config->reject = $2;
1557 }
1558 | DISCONNECT STRING {
1559 	filter_config->disconnect = $2;
1560 }
1561 | REWRITE STRING {
1562 	filter_config->rewrite = $2;
1563 }
1564 | REPORT STRING {
1565 	filter_config->report = $2;
1566 }
1567 ;
1568 
1569 filter_phase_check_fcrdns:
1570 negation FCRDNS {
1571 	filter_config->not_fcrdns = $1 ? -1 : 1;
1572 	filter_config->fcrdns = 1;
1573 }
1574 ;
1575 
1576 filter_phase_check_rdns:
1577 negation RDNS {
1578 	filter_config->not_rdns = $1 ? -1 : 1;
1579 	filter_config->rdns = 1;
1580 }
1581 ;
1582 
1583 filter_phase_check_rdns_table:
1584 negation RDNS tables {
1585 	filter_config->not_rdns_table = $1 ? -1 : 1;
1586 	filter_config->rdns_table = $3;
1587 }
1588 ;
1589 filter_phase_check_rdns_regex:
1590 negation RDNS REGEX tables {
1591 	filter_config->not_rdns_regex = $1 ? -1 : 1;
1592 	filter_config->rdns_regex = $4;
1593 }
1594 ;
1595 
1596 filter_phase_check_src_table:
1597 negation SRC tables {
1598 	filter_config->not_src_table = $1 ? -1 : 1;
1599 	filter_config->src_table = $3;
1600 }
1601 ;
1602 filter_phase_check_src_regex:
1603 negation SRC REGEX tables {
1604 	filter_config->not_src_regex = $1 ? -1 : 1;
1605 	filter_config->src_regex = $4;
1606 }
1607 ;
1608 
1609 filter_phase_check_helo_table:
1610 negation HELO tables {
1611 	filter_config->not_helo_table = $1 ? -1 : 1;
1612 	filter_config->helo_table = $3;
1613 }
1614 ;
1615 filter_phase_check_helo_regex:
1616 negation HELO REGEX tables {
1617 	filter_config->not_helo_regex = $1 ? -1 : 1;
1618 	filter_config->helo_regex = $4;
1619 }
1620 ;
1621 
1622 filter_phase_check_auth:
1623 negation AUTH {
1624 	filter_config->not_auth = $1 ? -1 : 1;
1625 	filter_config->auth = 1;
1626 }
1627 ;
1628 filter_phase_check_auth_table:
1629 negation AUTH tables {
1630 	filter_config->not_auth_table = $1 ? -1 : 1;
1631 	filter_config->auth_table = $3;
1632 }
1633 ;
1634 filter_phase_check_auth_regex:
1635 negation AUTH REGEX tables {
1636 	filter_config->not_auth_regex = $1 ? -1 : 1;
1637 	filter_config->auth_regex = $4;
1638 }
1639 ;
1640 
1641 filter_phase_check_mail_from_table:
1642 negation MAIL_FROM tables {
1643 	filter_config->not_mail_from_table = $1 ? -1 : 1;
1644 	filter_config->mail_from_table = $3;
1645 }
1646 ;
1647 filter_phase_check_mail_from_regex:
1648 negation MAIL_FROM REGEX tables {
1649 	filter_config->not_mail_from_regex = $1 ? -1 : 1;
1650 	filter_config->mail_from_regex = $4;
1651 }
1652 ;
1653 
1654 filter_phase_check_rcpt_to_table:
1655 negation RCPT_TO tables {
1656 	filter_config->not_rcpt_to_table = $1 ? -1 : 1;
1657 	filter_config->rcpt_to_table = $3;
1658 }
1659 ;
1660 filter_phase_check_rcpt_to_regex:
1661 negation RCPT_TO REGEX tables {
1662 	filter_config->not_rcpt_to_regex = $1 ? -1 : 1;
1663 	filter_config->rcpt_to_regex = $4;
1664 }
1665 ;
1666 
1667 filter_phase_global_options:
1668 filter_phase_check_fcrdns |
1669 filter_phase_check_rdns |
1670 filter_phase_check_rdns_regex |
1671 filter_phase_check_rdns_table |
1672 filter_phase_check_src_regex |
1673 filter_phase_check_src_table;
1674 
1675 filter_phase_connect_options:
1676 filter_phase_global_options;
1677 
1678 filter_phase_helo_options:
1679 filter_phase_check_helo_table |
1680 filter_phase_check_helo_regex |
1681 filter_phase_global_options;
1682 
1683 filter_phase_auth_options:
1684 filter_phase_check_helo_table |
1685 filter_phase_check_helo_regex |
1686 filter_phase_check_auth |
1687 filter_phase_check_auth_table |
1688 filter_phase_check_auth_regex |
1689 filter_phase_global_options;
1690 
1691 filter_phase_mail_from_options:
1692 filter_phase_check_helo_table |
1693 filter_phase_check_helo_regex |
1694 filter_phase_check_auth |
1695 filter_phase_check_auth_table |
1696 filter_phase_check_auth_regex |
1697 filter_phase_check_mail_from_table |
1698 filter_phase_check_mail_from_regex |
1699 filter_phase_global_options;
1700 
1701 filter_phase_rcpt_to_options:
1702 filter_phase_check_helo_table |
1703 filter_phase_check_helo_regex |
1704 filter_phase_check_auth |
1705 filter_phase_check_auth_table |
1706 filter_phase_check_auth_regex |
1707 filter_phase_check_mail_from_table |
1708 filter_phase_check_mail_from_regex |
1709 filter_phase_check_rcpt_to_table |
1710 filter_phase_check_rcpt_to_regex |
1711 filter_phase_global_options;
1712 
1713 filter_phase_data_options:
1714 filter_phase_check_helo_table |
1715 filter_phase_check_helo_regex |
1716 filter_phase_check_auth |
1717 filter_phase_check_auth_table |
1718 filter_phase_check_auth_regex |
1719 filter_phase_check_mail_from_table |
1720 filter_phase_check_mail_from_regex |
1721 filter_phase_global_options;
1722 
1723 /*
1724 filter_phase_quit_options:
1725 filter_phase_check_helo_table |
1726 filter_phase_check_helo_regex |
1727 filter_phase_global_options;
1728 
1729 filter_phase_rset_options:
1730 filter_phase_check_helo_table |
1731 filter_phase_check_helo_regex |
1732 filter_phase_global_options;
1733 
1734 filter_phase_noop_options:
1735 filter_phase_check_helo_table |
1736 filter_phase_check_helo_regex |
1737 filter_phase_global_options;
1738 */
1739 
1740 filter_phase_commit_options:
1741 filter_phase_check_helo_table |
1742 filter_phase_check_helo_regex |
1743 filter_phase_check_auth |
1744 filter_phase_check_auth_table |
1745 filter_phase_check_auth_regex |
1746 filter_phase_check_mail_from_table |
1747 filter_phase_check_mail_from_regex |
1748 filter_phase_global_options;
1749 
1750 
1751 filter_phase_connect:
1752 CONNECT {
1753 	filter_config->phase = FILTER_CONNECT;
1754 } MATCH filter_phase_connect_options filter_action_builtin
1755 ;
1756 
1757 
1758 filter_phase_helo:
1759 HELO {
1760 	filter_config->phase = FILTER_HELO;
1761 } MATCH filter_phase_helo_options filter_action_builtin
1762 ;
1763 
1764 filter_phase_ehlo:
1765 EHLO {
1766 	filter_config->phase = FILTER_EHLO;
1767 } MATCH filter_phase_helo_options filter_action_builtin
1768 ;
1769 
1770 filter_phase_auth:
1771 AUTH {
1772 } MATCH filter_phase_auth_options filter_action_builtin
1773 ;
1774 
1775 filter_phase_mail_from:
1776 MAIL_FROM {
1777 	filter_config->phase = FILTER_MAIL_FROM;
1778 } MATCH filter_phase_mail_from_options filter_action_builtin
1779 ;
1780 
1781 filter_phase_rcpt_to:
1782 RCPT_TO {
1783 	filter_config->phase = FILTER_RCPT_TO;
1784 } MATCH filter_phase_rcpt_to_options filter_action_builtin
1785 ;
1786 
1787 filter_phase_data:
1788 DATA {
1789 	filter_config->phase = FILTER_DATA;
1790 } MATCH filter_phase_data_options filter_action_builtin
1791 ;
1792 
1793 /*
1794 filter_phase_data_line:
1795 DATA_LINE {
1796 	filter_config->phase = FILTER_DATA_LINE;
1797 } MATCH filter_action_builtin
1798 ;
1799 
1800 filter_phase_quit:
1801 QUIT {
1802 	filter_config->phase = FILTER_QUIT;
1803 } filter_phase_quit_options filter_action_builtin
1804 ;
1805 
1806 filter_phase_rset:
1807 RSET {
1808 	filter_config->phase = FILTER_RSET;
1809 } MATCH filter_phase_rset_options filter_action_builtin
1810 ;
1811 
1812 filter_phase_noop:
1813 NOOP {
1814 	filter_config->phase = FILTER_NOOP;
1815 } MATCH filter_phase_noop_options filter_action_builtin
1816 ;
1817 */
1818 
1819 filter_phase_commit:
1820 COMMIT {
1821 	filter_config->phase = FILTER_COMMIT;
1822 } MATCH filter_phase_commit_options filter_action_builtin_nojunk
1823 ;
1824 
1825 
1826 
1827 filter_phase:
1828 filter_phase_connect
1829 | filter_phase_helo
1830 | filter_phase_ehlo
1831 | filter_phase_auth
1832 | filter_phase_mail_from
1833 | filter_phase_rcpt_to
1834 | filter_phase_data
1835 /*| filter_phase_data_line*/
1836 /*| filter_phase_quit*/
1837 /*| filter_phase_noop*/
1838 /*| filter_phase_rset*/
1839 | filter_phase_commit
1840 ;
1841 
1842 
1843 filterel:
1844 STRING	{
1845 	struct filter_config   *fr;
1846 	struct filter_proc     *fp;
1847 	size_t			i;
1848 
1849 	if ((fr = dict_get(conf->sc_filters_dict, $1)) == NULL) {
1850 		yyerror("no filter exist with that name: %s", $1);
1851 		free($1);
1852 		YYERROR;
1853 	}
1854 	if (fr->filter_type == FILTER_TYPE_CHAIN) {
1855 		yyerror("no filter chain allowed within a filter chain: %s", $1);
1856 		free($1);
1857 		YYERROR;
1858 	}
1859 
1860 	for (i = 0; i < filter_config->chain_size; i++) {
1861 		if (strcmp(filter_config->chain[i], $1) == 0) {
1862 			yyerror("no filter allowed twice within a filter chain: %s", $1);
1863 			free($1);
1864 			YYERROR;
1865 		}
1866 	}
1867 
1868 	if (fr->proc) {
1869 		if ((fp = dict_get(&filter_config->chain_procs, fr->proc))) {
1870 			yyerror("no proc allowed twice within a filter chain: %s", fr->proc);
1871 			free($1);
1872 			YYERROR;
1873 		}
1874 		dict_set(&filter_config->chain_procs, fr->proc, NULL);
1875 	}
1876 
1877 	fr->filter_subsystem |= filter_config->filter_subsystem;
1878 	filter_config->chain_size += 1;
1879 	filter_config->chain = reallocarray(filter_config->chain, filter_config->chain_size, sizeof(char *));
1880 	if (filter_config->chain == NULL)
1881 		err(1, NULL);
1882 	filter_config->chain[filter_config->chain_size - 1] = $1;
1883 }
1884 ;
1885 
1886 filter_list:
1887 filterel
1888 | filterel comma filter_list
1889 ;
1890 
1891 filter:
1892 FILTER STRING PROC STRING {
1893 	struct filter_proc *fp;
1894 
1895 	if (dict_get(conf->sc_filters_dict, $2)) {
1896 		yyerror("filter already exists with that name: %s", $2);
1897 		free($2);
1898 		free($4);
1899 		YYERROR;
1900 	}
1901 	if ((fp = dict_get(conf->sc_filter_processes_dict, $4)) == NULL) {
1902 		yyerror("no processor exist with that name: %s", $4);
1903 		free($4);
1904 		YYERROR;
1905 	}
1906 
1907 	filter_config = xcalloc(1, sizeof *filter_config);
1908 	filter_config->filter_type = FILTER_TYPE_PROC;
1909 	filter_config->name = $2;
1910 	filter_config->proc = $4;
1911 	dict_set(conf->sc_filters_dict, $2, filter_config);
1912 	filter_config = NULL;
1913 }
1914 |
1915 FILTER STRING PROC_EXEC STRING {
1916 	if (dict_get(conf->sc_filters_dict, $2)) {
1917 		yyerror("filter already exists with that name: %s", $2);
1918 		free($2);
1919 		free($4);
1920 		YYERROR;
1921 	}
1922 
1923 	processor = xcalloc(1, sizeof *processor);
1924 	processor->command = $4;
1925 
1926 	filter_config = xcalloc(1, sizeof *filter_config);
1927 	filter_config->filter_type = FILTER_TYPE_PROC;
1928 	filter_config->name = $2;
1929 	filter_config->proc = xstrdup($2);
1930 	dict_set(conf->sc_filters_dict, $2, filter_config);
1931 } proc_params {
1932 	dict_set(conf->sc_filter_processes_dict, filter_config->proc, processor);
1933 	processor = NULL;
1934 	filter_config = NULL;
1935 }
1936 |
1937 FILTER STRING PHASE {
1938 	if (dict_get(conf->sc_filters_dict, $2)) {
1939 		yyerror("filter already exists with that name: %s", $2);
1940 		free($2);
1941 		YYERROR;
1942 	}
1943 	filter_config = xcalloc(1, sizeof *filter_config);
1944 	filter_config->name = $2;
1945 	filter_config->filter_type = FILTER_TYPE_BUILTIN;
1946 	dict_set(conf->sc_filters_dict, $2, filter_config);
1947 } filter_phase {
1948 	filter_config = NULL;
1949 }
1950 |
1951 FILTER STRING CHAIN {
1952 	if (dict_get(conf->sc_filters_dict, $2)) {
1953 		yyerror("filter already exists with that name: %s", $2);
1954 		free($2);
1955 		YYERROR;
1956 	}
1957 	filter_config = xcalloc(1, sizeof *filter_config);
1958 	filter_config->filter_type = FILTER_TYPE_CHAIN;
1959 	dict_init(&filter_config->chain_procs);
1960 } '{' filter_list '}' {
1961 	dict_set(conf->sc_filters_dict, $2, filter_config);
1962 	filter_config = NULL;
1963 }
1964 ;
1965 
1966 agentxopt	:
1967 CONTEXT STRING {
1968 	if (conf->sc_agentx->context != NULL) {
1969 		yyerror("redefinition of agentx context");
1970 		free($2);
1971 		YYERROR;
1972 	}
1973 	conf->sc_agentx->context = $2;
1974 }
1975 | PATH STRING {
1976 	if (strlen($2) >= sizeof(((struct sockaddr_un *)0)->sun_path)) {
1977 		yyerror("agentx path too large");
1978 		free($2);
1979 		YYERROR;
1980 	}
1981 	if ($2[0] != '/') {
1982 		yyerror("agentx path must be absolute");
1983 		free($2);
1984 		YYERROR;
1985 	}
1986 	if (conf->sc_agentx->path != NULL) {
1987 		yyerror("redefinition of agentx path");
1988 		free($2);
1989 		YYERROR;
1990 	}
1991 	conf->sc_agentx->path = $2;
1992 }
1993 | APPLINDEX STRING {
1994 	if (conf->sc_agentx->applIndexType != AGENTX_INDEX_TYPE_UNDEFINED) {
1995 		yyerror("redefinition of agentx applIndex");
1996 		YYERROR;
1997 	}
1998 
1999 	if (strcasecmp($2, "any") == 0)
2000 		conf->sc_agentx->applIndexType = AGENTX_INDEX_TYPE_ANY;
2001 	else if (strcasecmp($2, "new") == 0)
2002 		conf->sc_agentx->applIndexType = AGENTX_INDEX_TYPE_NEW;
2003 	else {
2004 		yyerror("invalid applIndex");
2005 		YYERROR;
2006 	}
2007 }
2008 | APPLINDEX NUMBER {
2009 	if (conf->sc_agentx->applIndexType != AGENTX_INDEX_TYPE_UNDEFINED) {
2010 		yyerror("redefinition of agentx applIndex");
2011 		YYERROR;
2012 	}
2013 	if ($2 > UINT32_MAX) {
2014 		yyerror("agentx applIndex too large");
2015 		YYERROR;
2016 	}
2017 	if ($2 < 1) {
2018 		yyerror("agentx applIndex too small");
2019 		YYERROR;
2020 	}
2021 
2022 	conf->sc_agentx->applIndexType = AGENTX_INDEX_TYPE_VALUE;
2023 	conf->sc_agentx->applIndex = (uint32_t) $2;
2024 }
2025 ;
2026 
2027 agentxopts	: /* empty */
2028 		| agentxopts agentxopt
2029 		;
2030 
2031 agentx:
2032 AGENTX {
2033 	conf->sc_agentx = xcalloc(1, sizeof(*conf->sc_agentx));
2034 } agentxopts {
2035 	if (conf->sc_agentx->path == NULL)
2036 		conf->sc_agentx->path = SUBAGENTX_AGENTX_MASTER;
2037 	if (conf->sc_agentx->applIndexType == AGENTX_INDEX_TYPE_UNDEFINED)
2038 		conf->sc_agentx->applIndexType = AGENTX_INDEX_TYPE_ANY;
2039 }
2040 ;
2041 
2042 size		: NUMBER		{
2043 			if ($1 < 0) {
2044 				yyerror("invalid size: %" PRId64, $1);
2045 				YYERROR;
2046 			}
2047 			$$ = $1;
2048 		}
2049 		| STRING			{
2050 			long long result;
2051 
2052 			if (scan_scaled($1, &result) == -1 || result < 0) {
2053 				yyerror("invalid size: %s", $1);
2054 				free($1);
2055 				YYERROR;
2056 			}
2057 			free($1);
2058 			$$ = result;
2059 		}
2060 		;
2061 
2062 bouncedelay	: STRING {
2063 			time_t	d;
2064 			int	i;
2065 
2066 			d = delaytonum($1);
2067 			if (d < 0) {
2068 				yyerror("invalid bounce delay: %s", $1);
2069 				free($1);
2070 				YYERROR;
2071 			}
2072 			free($1);
2073 			for (i = 0; i < MAX_BOUNCE_WARN; i++) {
2074 				if (conf->sc_bounce_warn[i] != 0)
2075 					continue;
2076 				conf->sc_bounce_warn[i] = d;
2077 				break;
2078 			}
2079 		}
2080 		;
2081 
2082 bouncedelays	: bouncedelays ',' bouncedelay
2083 		| bouncedelay
2084 		;
2085 
2086 opt_limit_mda	: STRING NUMBER {
2087 			if (!strcmp($1, "max-session")) {
2088 				conf->sc_mda_max_session = $2;
2089 			}
2090 			else if (!strcmp($1, "max-session-per-user")) {
2091 				conf->sc_mda_max_user_session = $2;
2092 			}
2093 			else if (!strcmp($1, "task-lowat")) {
2094 				conf->sc_mda_task_lowat = $2;
2095 			}
2096 			else if (!strcmp($1, "task-hiwat")) {
2097 				conf->sc_mda_task_hiwat = $2;
2098 			}
2099 			else if (!strcmp($1, "task-release")) {
2100 				conf->sc_mda_task_release = $2;
2101 			}
2102 			else {
2103 				yyerror("invalid scheduler limit keyword: %s", $1);
2104 				free($1);
2105 				YYERROR;
2106 			}
2107 			free($1);
2108 		}
2109 		;
2110 
2111 limits_smtp	: opt_limit_smtp limits_smtp
2112 		| /* empty */
2113 		;
2114 
2115 opt_limit_smtp : STRING NUMBER {
2116 			if (!strcmp($1, "max-rcpt")) {
2117 				conf->sc_session_max_rcpt = $2;
2118 			}
2119 			else if (!strcmp($1, "max-mails")) {
2120 				conf->sc_session_max_mails = $2;
2121 			}
2122 			else {
2123 				yyerror("invalid session limit keyword: %s", $1);
2124 				free($1);
2125 				YYERROR;
2126 			}
2127 			free($1);
2128 		}
2129 		;
2130 
2131 limits_mda	: opt_limit_mda limits_mda
2132 		| /* empty */
2133 		;
2134 
2135 opt_limit_mta	: INET4 {
2136 			limits->family = AF_INET;
2137 		}
2138 		| INET6 {
2139 			limits->family = AF_INET6;
2140 		}
2141 		| STRING NUMBER {
2142 			if (!limit_mta_set(limits, $1, $2)) {
2143 				yyerror("invalid mta limit keyword: %s", $1);
2144 				free($1);
2145 				YYERROR;
2146 			}
2147 			free($1);
2148 		}
2149 		;
2150 
2151 limits_mta	: opt_limit_mta limits_mta
2152 		| /* empty */
2153 		;
2154 
2155 opt_limit_scheduler : STRING NUMBER {
2156 			if (!strcmp($1, "max-inflight")) {
2157 				conf->sc_scheduler_max_inflight = $2;
2158 			}
2159 			else if (!strcmp($1, "max-evp-batch-size")) {
2160 				conf->sc_scheduler_max_evp_batch_size = $2;
2161 			}
2162 			else if (!strcmp($1, "max-msg-batch-size")) {
2163 				conf->sc_scheduler_max_msg_batch_size = $2;
2164 			}
2165 			else if (!strcmp($1, "max-schedule")) {
2166 				conf->sc_scheduler_max_schedule = $2;
2167 			}
2168 			else {
2169 				yyerror("invalid scheduler limit keyword: %s", $1);
2170 				free($1);
2171 				YYERROR;
2172 			}
2173 			free($1);
2174 		}
2175 		;
2176 
2177 limits_scheduler: opt_limit_scheduler limits_scheduler
2178 		| /* empty */
2179 		;
2180 
2181 
2182 opt_sock_listen : FILTER STRING {
2183 			struct filter_config *fc;
2184 
2185 			if (listen_opts.options & LO_FILTER) {
2186 				yyerror("filter already specified");
2187 				free($2);
2188 				YYERROR;
2189 			}
2190 			if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
2191 				yyerror("no filter exist with that name: %s", $2);
2192 				free($2);
2193 				YYERROR;
2194 			}
2195 			fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
2196 			listen_opts.options |= LO_FILTER;
2197 			listen_opts.filtername = $2;
2198 		}
2199 		| FILTER {
2200 			char	buffer[128];
2201 
2202 			if (listen_opts.options & LO_FILTER) {
2203 				yyerror("filter already specified");
2204 				YYERROR;
2205 			}
2206 
2207 			do {
2208 				(void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++);
2209 			} while (dict_check(conf->sc_filters_dict, buffer));
2210 
2211 			listen_opts.options |= LO_FILTER;
2212 			listen_opts.filtername = xstrdup(buffer);
2213 			filter_config = xcalloc(1, sizeof *filter_config);
2214 			filter_config->filter_type = FILTER_TYPE_CHAIN;
2215 			filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
2216 			dict_init(&filter_config->chain_procs);
2217 		} '{' filter_list '}' {
2218 			dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config);
2219 			filter_config = NULL;
2220 		}
2221 		| MASK_SRC {
2222 			if (config_lo_mask_source(&listen_opts)) {
2223 				YYERROR;
2224 			}
2225 		}
2226 		| TAG STRING			{
2227 			if (listen_opts.options & LO_TAG) {
2228 				yyerror("tag already specified");
2229 				YYERROR;
2230 			}
2231 			listen_opts.options |= LO_TAG;
2232 
2233 			if (strlen($2) >= SMTPD_TAG_SIZE) {
2234 				yyerror("tag name too long");
2235 				free($2);
2236 				YYERROR;
2237 			}
2238 			listen_opts.tag = $2;
2239 		}
2240 		;
2241 
2242 opt_if_listen : INET4 {
2243 			if (listen_opts.options & LO_FAMILY) {
2244 				yyerror("address family already specified");
2245 				YYERROR;
2246 			}
2247 			listen_opts.options |= LO_FAMILY;
2248 			listen_opts.family = AF_INET;
2249 		}
2250 		| INET6			{
2251 			if (listen_opts.options & LO_FAMILY) {
2252 				yyerror("address family already specified");
2253 				YYERROR;
2254 			}
2255 			listen_opts.options |= LO_FAMILY;
2256 			listen_opts.family = AF_INET6;
2257 		}
2258 		| PORT STRING			{
2259 			struct servent	*servent;
2260 
2261 			if (listen_opts.options & LO_PORT) {
2262 				yyerror("port already specified");
2263 				YYERROR;
2264 			}
2265 			listen_opts.options |= LO_PORT;
2266 
2267 			servent = getservbyname($2, "tcp");
2268 			if (servent == NULL) {
2269 				yyerror("invalid port: %s", $2);
2270 				free($2);
2271 				YYERROR;
2272 			}
2273 			free($2);
2274 			listen_opts.port = ntohs(servent->s_port);
2275 		}
2276 		| PORT SMTP			{
2277 			struct servent *servent;
2278 
2279 			if (listen_opts.options & LO_PORT) {
2280 				yyerror("port already specified");
2281 				YYERROR;
2282 			}
2283 			listen_opts.options |= LO_PORT;
2284 
2285 			servent = getservbyname("smtp", "tcp");
2286 			if (servent == NULL) {
2287 				yyerror("invalid port: smtp");
2288 				YYERROR;
2289 			}
2290 			listen_opts.port = ntohs(servent->s_port);
2291 		}
2292 		| PORT SMTPS			{
2293 			struct servent *servent;
2294 
2295 			if (listen_opts.options & LO_PORT) {
2296 				yyerror("port already specified");
2297 				YYERROR;
2298 			}
2299 			listen_opts.options |= LO_PORT;
2300 
2301 			servent = getservbyname("smtps", "tcp");
2302 			if (servent == NULL) {
2303 				yyerror("invalid port: smtps");
2304 				YYERROR;
2305 			}
2306 			listen_opts.port = ntohs(servent->s_port);
2307 		}
2308 		| PORT NUMBER			{
2309 			if (listen_opts.options & LO_PORT) {
2310 				yyerror("port already specified");
2311 				YYERROR;
2312 			}
2313 			listen_opts.options |= LO_PORT;
2314 
2315 			if ($2 <= 0 || $2 > (int)USHRT_MAX) {
2316 				yyerror("invalid port: %" PRId64, $2);
2317 				YYERROR;
2318 			}
2319 			listen_opts.port = $2;
2320 		}
2321 		| FILTER STRING			{
2322 			struct filter_config *fc;
2323 
2324 			if (listen_opts.options & LO_FILTER) {
2325 				yyerror("filter already specified");
2326 				YYERROR;
2327 			}
2328 			if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
2329 				yyerror("no filter exist with that name: %s", $2);
2330 				free($2);
2331 				YYERROR;
2332 			}
2333 			fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
2334 			listen_opts.options |= LO_FILTER;
2335 			listen_opts.filtername = $2;
2336 		}
2337 		| FILTER {
2338 			char	buffer[128];
2339 
2340 			if (listen_opts.options & LO_FILTER) {
2341 				yyerror("filter already specified");
2342 				YYERROR;
2343 			}
2344 
2345 			do {
2346 				(void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++);
2347 			} while (dict_check(conf->sc_filters_dict, buffer));
2348 
2349 			listen_opts.options |= LO_FILTER;
2350 			listen_opts.filtername = xstrdup(buffer);
2351 			filter_config = xcalloc(1, sizeof *filter_config);
2352 			filter_config->filter_type = FILTER_TYPE_CHAIN;
2353 			filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
2354 			dict_init(&filter_config->chain_procs);
2355 		} '{' filter_list '}' {
2356 			dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config);
2357 			filter_config = NULL;
2358 		}
2359 		| SMTPS				{
2360 			if (listen_opts.options & LO_SSL) {
2361 				yyerror("TLS mode already specified");
2362 				YYERROR;
2363 			}
2364 			listen_opts.options |= LO_SSL;
2365 			listen_opts.ssl = F_SMTPS;
2366 		}
2367 		| SMTPS VERIFY 			{
2368 			if (listen_opts.options & LO_SSL) {
2369 				yyerror("TLS mode already specified");
2370 				YYERROR;
2371 			}
2372 			listen_opts.options |= LO_SSL;
2373 			listen_opts.ssl = F_SMTPS|F_TLS_VERIFY;
2374 		}
2375 		| TLS				{
2376 			if (listen_opts.options & LO_SSL) {
2377 				yyerror("TLS mode already specified");
2378 				YYERROR;
2379 			}
2380 			listen_opts.options |= LO_SSL;
2381 			listen_opts.ssl = F_STARTTLS;
2382 		}
2383 		| TLS_REQUIRE			{
2384 			if (listen_opts.options & LO_SSL) {
2385 				yyerror("TLS mode already specified");
2386 				YYERROR;
2387 			}
2388 			listen_opts.options |= LO_SSL;
2389 			listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE;
2390 		}
2391 		| TLS_REQUIRE VERIFY   		{
2392 			if (listen_opts.options & LO_SSL) {
2393 				yyerror("TLS mode already specified");
2394 				YYERROR;
2395 			}
2396 			listen_opts.options |= LO_SSL;
2397 			listen_opts.ssl = F_STARTTLS|F_STARTTLS_REQUIRE|F_TLS_VERIFY;
2398 		}
2399 		| PKI STRING			{
2400 			if (listen_opts.options & LO_PKI) {
2401 				yyerror("pki already specified");
2402 				YYERROR;
2403 			}
2404 			listen_opts.options |= LO_PKI;
2405 			listen_opts.pki = $2;
2406 		}
2407 		| CA STRING			{
2408 			if (listen_opts.options & LO_CA) {
2409 				yyerror("ca already specified");
2410 				YYERROR;
2411 			}
2412 			listen_opts.options |= LO_CA;
2413 			listen_opts.ca = $2;
2414 		}
2415 		| AUTH				{
2416 			if (listen_opts.options & LO_AUTH) {
2417 				yyerror("auth already specified");
2418 				YYERROR;
2419 			}
2420 			listen_opts.options |= LO_AUTH;
2421 			listen_opts.auth = F_AUTH|F_AUTH_REQUIRE;
2422 		}
2423 		| AUTH_OPTIONAL			{
2424 			if (listen_opts.options & LO_AUTH) {
2425 				yyerror("auth already specified");
2426 				YYERROR;
2427 			}
2428 			listen_opts.options |= LO_AUTH;
2429 			listen_opts.auth = F_AUTH;
2430 		}
2431 		| AUTH tables  			{
2432 			if (listen_opts.options & LO_AUTH) {
2433 				yyerror("auth already specified");
2434 				YYERROR;
2435 			}
2436 			listen_opts.options |= LO_AUTH;
2437 			listen_opts.authtable = $2;
2438 			listen_opts.auth = F_AUTH|F_AUTH_REQUIRE;
2439 		}
2440 		| AUTH_OPTIONAL tables 		{
2441 			if (listen_opts.options & LO_AUTH) {
2442 				yyerror("auth already specified");
2443 				YYERROR;
2444 			}
2445 			listen_opts.options |= LO_AUTH;
2446 			listen_opts.authtable = $2;
2447 			listen_opts.auth = F_AUTH;
2448 		}
2449 		| TAG STRING			{
2450 			if (listen_opts.options & LO_TAG) {
2451 				yyerror("tag already specified");
2452 				YYERROR;
2453 			}
2454 			listen_opts.options |= LO_TAG;
2455 
2456 			if (strlen($2) >= SMTPD_TAG_SIZE) {
2457        				yyerror("tag name too long");
2458 				free($2);
2459 				YYERROR;
2460 			}
2461 			listen_opts.tag = $2;
2462 		}
2463 		| HOSTNAME STRING	{
2464 			if (listen_opts.options & LO_HOSTNAME) {
2465 				yyerror("hostname already specified");
2466 				YYERROR;
2467 			}
2468 			listen_opts.options |= LO_HOSTNAME;
2469 
2470 			listen_opts.hostname = $2;
2471 		}
2472 		| HOSTNAMES tables	{
2473 			struct table	*t = $2;
2474 
2475 			if (listen_opts.options & LO_HOSTNAMES) {
2476 				yyerror("hostnames already specified");
2477 				YYERROR;
2478 			}
2479 			listen_opts.options |= LO_HOSTNAMES;
2480 
2481 			if (!table_check_use(t, T_DYNAMIC|T_HASH, K_ADDRNAME)) {
2482 				yyerror("invalid use of table \"%s\" as "
2483 				    "HOSTNAMES parameter", t->t_name);
2484 				YYERROR;
2485 			}
2486 			listen_opts.hostnametable = t;
2487 		}
2488 		| MASK_SRC	{
2489 			if (config_lo_mask_source(&listen_opts)) {
2490 				YYERROR;
2491 			}
2492 		}
2493 		| RECEIVEDAUTH	{
2494 			if (listen_opts.options & LO_RECEIVEDAUTH) {
2495 				yyerror("received-auth already specified");
2496 				YYERROR;
2497 			}
2498 			listen_opts.options |= LO_RECEIVEDAUTH;
2499 			listen_opts.flags |= F_RECEIVEDAUTH;
2500 		}
2501 		| NO_DSN	{
2502 			if (listen_opts.options & LO_NODSN) {
2503 				yyerror("no-dsn already specified");
2504 				YYERROR;
2505 			}
2506 			listen_opts.options |= LO_NODSN;
2507 			listen_opts.flags &= ~F_EXT_DSN;
2508 		}
2509 		| PROXY_V2	{
2510 			if (listen_opts.options & LO_PROXY) {
2511 				yyerror("proxy-v2 already specified");
2512 				YYERROR;
2513 			}
2514 			listen_opts.options |= LO_PROXY;
2515 			listen_opts.flags |= F_PROXY;
2516 		}
2517 		| SENDERS tables	{
2518 			struct table	*t = $2;
2519 
2520 			if (listen_opts.options & LO_SENDERS) {
2521 				yyerror("senders already specified");
2522 				YYERROR;
2523 			}
2524 			listen_opts.options |= LO_SENDERS;
2525 
2526 			if (!table_check_use(t, T_DYNAMIC|T_HASH, K_MAILADDRMAP)) {
2527 				yyerror("invalid use of table \"%s\" as "
2528 				    "SENDERS parameter", t->t_name);
2529 				YYERROR;
2530 			}
2531 			listen_opts.sendertable = t;
2532 		}
2533 		| SENDERS tables MASQUERADE	{
2534 			struct table	*t = $2;
2535 
2536 			if (listen_opts.options & LO_SENDERS) {
2537 				yyerror("senders already specified");
2538 				YYERROR;
2539 			}
2540 			listen_opts.options |= LO_SENDERS|LO_MASQUERADE;
2541 
2542 			if (!table_check_use(t, T_DYNAMIC|T_HASH, K_MAILADDRMAP)) {
2543 				yyerror("invalid use of table \"%s\" as "
2544 				    "SENDERS parameter", t->t_name);
2545 				YYERROR;
2546 			}
2547 			listen_opts.sendertable = t;
2548 		}
2549 		;
2550 
2551 listener_type	: socket_listener
2552 		| if_listener
2553 		;
2554 
2555 socket_listener	: SOCKET sock_listen {
2556 			if (conf->sc_sock_listener) {
2557 				yyerror("socket listener already configured");
2558 				YYERROR;
2559 			}
2560 			create_sock_listener(&listen_opts);
2561 		}
2562 		;
2563 
2564 if_listener	: STRING if_listen {
2565 			listen_opts.ifx = $1;
2566 			create_if_listener(&listen_opts);
2567 		}
2568 		;
2569 
2570 sock_listen	: opt_sock_listen sock_listen
2571 		| /* empty */
2572 		;
2573 
2574 if_listen	: opt_if_listen if_listen
2575 		| /* empty */
2576 		;
2577 
2578 
2579 listen		: LISTEN {
2580 			memset(&listen_opts, 0, sizeof listen_opts);
2581 			listen_opts.family = AF_UNSPEC;
2582 			listen_opts.flags |= F_EXT_DSN;
2583 		} ON listener_type
2584 		;
2585 
2586 table		: TABLE STRING STRING	{
2587 			char *p, *backend, *config;
2588 
2589 			p = $3;
2590 			if (*p == '/') {
2591 				backend = "static";
2592 				config = $3;
2593 			}
2594 			else {
2595 				backend = $3;
2596 				config = NULL;
2597 				for (p = $3; *p && *p != ':'; p++)
2598 					;
2599 				if (*p == ':') {
2600 					*p = '\0';
2601 					backend = $3;
2602 					config  = p+1;
2603 				}
2604 			}
2605 			if (config != NULL && *config != '/') {
2606 				yyerror("invalid backend parameter for table: %s",
2607 				    $2);
2608 				free($2);
2609 				free($3);
2610 				YYERROR;
2611 			}
2612 			table = table_create(conf, backend, $2, config);
2613 			if (!table_config(table)) {
2614 				yyerror("invalid configuration file %s for table %s",
2615 				    config, table->t_name);
2616 				free($2);
2617 				free($3);
2618 				YYERROR;
2619 			}
2620 			table = NULL;
2621 			free($2);
2622 			free($3);
2623 		}
2624 		| TABLE STRING {
2625 			table = table_create(conf, "static", $2, NULL);
2626 			free($2);
2627 		} '{' tableval_list '}' {
2628 			table = NULL;
2629 		}
2630 		;
2631 
2632 tablenew	: STRING			{
2633 			struct table	*t;
2634 
2635 			t = table_create(conf, "static", NULL, NULL);
2636 			table_add(t, $1, NULL);
2637 			free($1);
2638 			$$ = t;
2639 		}
2640 		| '{'				{
2641 			table = table_create(conf, "static", NULL, NULL);
2642 		} tableval_list '}'		{
2643 			$$ = table;
2644 			table = NULL;
2645 		}
2646 		;
2647 
2648 tableref       	: '<' STRING '>'       		{
2649 			struct table	*t;
2650 
2651 			if ((t = table_find(conf, $2)) == NULL) {
2652 				yyerror("no such table: %s", $2);
2653 				free($2);
2654 				YYERROR;
2655 			}
2656 			free($2);
2657 			$$ = t;
2658 		}
2659 		;
2660 
2661 tables		: tablenew			{ $$ = $1; }
2662 		| tableref			{ $$ = $1; }
2663 		;
2664 
2665 
2666 %%
2667 
2668 struct keywords {
2669 	const char	*k_name;
2670 	int		 k_val;
2671 };
2672 
2673 int
2674 yyerror(const char *fmt, ...)
2675 {
2676 	va_list		 ap;
2677 	char		*msg;
2678 
2679 	file->errors++;
2680 	va_start(ap, fmt);
2681 	if (vasprintf(&msg, fmt, ap) == -1)
2682 		fatalx("yyerror vasprintf");
2683 	va_end(ap);
2684 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
2685 	free(msg);
2686 	return (0);
2687 }
2688 
2689 int
2690 kw_cmp(const void *k, const void *e)
2691 {
2692 	return (strcmp(k, ((const struct keywords *)e)->k_name));
2693 }
2694 
2695 int
2696 lookup(char *s)
2697 {
2698 	/* this has to be sorted always */
2699 	static const struct keywords keywords[] = {
2700 		{ "action",		ACTION },
2701 		{ "admd",		ADMD },
2702 		{ "agentx",		AGENTX },
2703 		{ "alias",		ALIAS },
2704 		{ "any",		ANY },
2705 		{ "applIndex",		APPLINDEX },
2706 		{ "auth",		AUTH },
2707 		{ "auth-optional",     	AUTH_OPTIONAL },
2708 		{ "backup",		BACKUP },
2709 		{ "bounce",		BOUNCE },
2710 		{ "bypass",		BYPASS },
2711 		{ "ca",			CA },
2712 		{ "cert",		CERT },
2713 		{ "chain",		CHAIN },
2714 		{ "chroot",		CHROOT },
2715 		{ "ciphers",		CIPHERS },
2716 		{ "commit",		COMMIT },
2717 		{ "compression",	COMPRESSION },
2718 		{ "connect",		CONNECT },
2719 		{ "context",		CONTEXT },
2720 		{ "data",		DATA },
2721 		{ "data-line",		DATA_LINE },
2722 		{ "dhe",		DHE },
2723 		{ "disconnect",		DISCONNECT },
2724 		{ "domain",		DOMAIN },
2725 		{ "ehlo",		EHLO },
2726 		{ "encryption",		ENCRYPTION },
2727 		{ "expand-only",      	EXPAND_ONLY },
2728 		{ "fcrdns",		FCRDNS },
2729 		{ "filter",		FILTER },
2730 		{ "for",		FOR },
2731 		{ "forward-only",      	FORWARD_ONLY },
2732 		{ "from",		FROM },
2733 		{ "group",		GROUP },
2734 		{ "helo",		HELO },
2735 		{ "helo-src",       	HELO_SRC },
2736 		{ "host",		HOST },
2737 		{ "hostname",		HOSTNAME },
2738 		{ "hostnames",		HOSTNAMES },
2739 		{ "include",		INCLUDE },
2740 		{ "inet4",		INET4 },
2741 		{ "inet6",		INET6 },
2742 		{ "junk",		JUNK },
2743 		{ "key",		KEY },
2744 		{ "limit",		LIMIT },
2745 		{ "listen",		LISTEN },
2746 		{ "lmtp",		LMTP },
2747 		{ "local",		LOCAL },
2748 		{ "mail-from",		MAIL_FROM },
2749 		{ "maildir",		MAILDIR },
2750 		{ "mask-src",		MASK_SRC },
2751 		{ "masquerade",		MASQUERADE },
2752 		{ "match",		MATCH },
2753 		{ "max-deferred",  	MAX_DEFERRED },
2754 		{ "max-message-size",  	MAX_MESSAGE_SIZE },
2755 		{ "mbox",		MBOX },
2756 		{ "mda",		MDA },
2757 		{ "mta",		MTA },
2758 		{ "mx",			MX },
2759 		{ "no-dsn",		NO_DSN },
2760 		{ "no-verify",		NO_VERIFY },
2761 		{ "noop",		NOOP },
2762 		{ "on",			ON },
2763 		{ "path",		PATH },
2764 		{ "phase",		PHASE },
2765 		{ "pki",		PKI },
2766 		{ "port",		PORT },
2767 		{ "proc",		PROC },
2768 		{ "proc-exec",		PROC_EXEC },
2769 		{ "proxy-v2",		PROXY_V2 },
2770 		{ "queue",		QUEUE },
2771 		{ "quit",		QUIT },
2772 		{ "rcpt-to",		RCPT_TO },
2773 		{ "rdns",		RDNS },
2774 		{ "received-auth",     	RECEIVEDAUTH },
2775 		{ "recipient",		RECIPIENT },
2776 		{ "regex",		REGEX },
2777 		{ "reject",		REJECT },
2778 		{ "relay",		RELAY },
2779 		{ "report",		REPORT },
2780 		{ "rewrite",		REWRITE },
2781 		{ "rset",		RSET },
2782 		{ "scheduler",		SCHEDULER },
2783 		{ "senders",   		SENDERS },
2784 		{ "smtp",		SMTP },
2785 		{ "smtp-in",		SMTP_IN },
2786 		{ "smtp-out",		SMTP_OUT },
2787 		{ "smtps",		SMTPS },
2788 		{ "socket",		SOCKET },
2789 		{ "src",		SRC },
2790 		{ "srs",		SRS },
2791 		{ "sub-addr-delim",	SUB_ADDR_DELIM },
2792 		{ "table",		TABLE },
2793 		{ "tag",		TAG },
2794 		{ "tagged",		TAGGED },
2795 		{ "tls",		TLS },
2796 		{ "tls-require",       	TLS_REQUIRE },
2797 		{ "ttl",		TTL },
2798 		{ "user",		USER },
2799 		{ "userbase",		USERBASE },
2800 		{ "verify",		VERIFY },
2801 		{ "virtual",		VIRTUAL },
2802 		{ "warn-interval",	WARN_INTERVAL },
2803 		{ "wrapper",		WRAPPER },
2804 	};
2805 	const struct keywords	*p;
2806 
2807 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
2808 	    sizeof(keywords[0]), kw_cmp);
2809 
2810 	if (p)
2811 		return (p->k_val);
2812 	else
2813 		return (STRING);
2814 }
2815 
2816 #define START_EXPAND	1
2817 #define DONE_EXPAND	2
2818 
2819 static int	expanding;
2820 
2821 int
2822 igetc(void)
2823 {
2824 	int	c;
2825 
2826 	while (1) {
2827 		if (file->ungetpos > 0)
2828 			c = file->ungetbuf[--file->ungetpos];
2829 		else
2830 			c = getc(file->stream);
2831 
2832 		if (c == START_EXPAND)
2833 			expanding = 1;
2834 		else if (c == DONE_EXPAND)
2835 			expanding = 0;
2836 		else
2837 			break;
2838 	}
2839 	return (c);
2840 }
2841 
2842 int
2843 lgetc(int quotec)
2844 {
2845 	int		c, next;
2846 
2847 	if (quotec) {
2848 		if ((c = igetc()) == EOF) {
2849 			yyerror("reached end of file while parsing "
2850 			    "quoted string");
2851 			if (file == topfile || popfile() == EOF)
2852 				return (EOF);
2853 			return (quotec);
2854 		}
2855 		return (c);
2856 	}
2857 
2858 	while ((c = igetc()) == '\\') {
2859 		next = igetc();
2860 		if (next != '\n') {
2861 			c = next;
2862 			break;
2863 		}
2864 		yylval.lineno = file->lineno;
2865 		file->lineno++;
2866 	}
2867 
2868 	if (c == EOF) {
2869 		/*
2870 		 * Fake EOL when hit EOF for the first time. This gets line
2871 		 * count right if last line in included file is syntactically
2872 		 * invalid and has no newline.
2873 		 */
2874 		if (file->eof_reached == 0) {
2875 			file->eof_reached = 1;
2876 			return ('\n');
2877 		}
2878 		while (c == EOF) {
2879 			if (file == topfile || popfile() == EOF)
2880 				return (EOF);
2881 			c = igetc();
2882 		}
2883 	}
2884 	return (c);
2885 }
2886 
2887 void
2888 lungetc(int c)
2889 {
2890 	if (c == EOF)
2891 		return;
2892 
2893 	if (file->ungetpos >= file->ungetsize) {
2894 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
2895 		if (p == NULL)
2896 			err(1, "%s", __func__);
2897 		file->ungetbuf = p;
2898 		file->ungetsize *= 2;
2899 	}
2900 	file->ungetbuf[file->ungetpos++] = c;
2901 }
2902 
2903 int
2904 findeol(void)
2905 {
2906 	int	c;
2907 
2908 	/* skip to either EOF or the first real EOL */
2909 	while (1) {
2910 		c = lgetc(0);
2911 		if (c == '\n') {
2912 			file->lineno++;
2913 			break;
2914 		}
2915 		if (c == EOF)
2916 			break;
2917 	}
2918 	return (ERROR);
2919 }
2920 
2921 int
2922 yylex(void)
2923 {
2924 	unsigned char	 buf[8096];
2925 	unsigned char	*p, *val;
2926 	int		 quotec, next, c;
2927 	int		 token;
2928 
2929 top:
2930 	p = buf;
2931 	while ((c = lgetc(0)) == ' ' || c == '\t')
2932 		; /* nothing */
2933 
2934 	yylval.lineno = file->lineno;
2935 	if (c == '#')
2936 		while ((c = lgetc(0)) != '\n' && c != EOF)
2937 			; /* nothing */
2938 	if (c == '$' && !expanding) {
2939 		while (1) {
2940 			if ((c = lgetc(0)) == EOF)
2941 				return (0);
2942 
2943 			if (p + 1 >= buf + sizeof(buf) - 1) {
2944 				yyerror("string too long");
2945 				return (findeol());
2946 			}
2947 			if (isalnum(c) || c == '_') {
2948 				*p++ = c;
2949 				continue;
2950 			}
2951 			*p = '\0';
2952 			lungetc(c);
2953 			break;
2954 		}
2955 		val = symget(buf);
2956 		if (val == NULL) {
2957 			yyerror("macro '%s' not defined", buf);
2958 			return (findeol());
2959 		}
2960 		p = val + strlen(val) - 1;
2961 		lungetc(DONE_EXPAND);
2962 		while (p >= val) {
2963 			lungetc(*p);
2964 			p--;
2965 		}
2966 		lungetc(START_EXPAND);
2967 		goto top;
2968 	}
2969 
2970 	switch (c) {
2971 	case '\'':
2972 	case '"':
2973 		quotec = c;
2974 		while (1) {
2975 			if ((c = lgetc(quotec)) == EOF)
2976 				return (0);
2977 			if (c == '\n') {
2978 				file->lineno++;
2979 				continue;
2980 			} else if (c == '\\') {
2981 				if ((next = lgetc(quotec)) == EOF)
2982 					return (0);
2983 				if (next == quotec || next == ' ' ||
2984 				    next == '\t')
2985 					c = next;
2986 				else if (next == '\n') {
2987 					file->lineno++;
2988 					continue;
2989 				} else
2990 					lungetc(next);
2991 			} else if (c == quotec) {
2992 				*p = '\0';
2993 				break;
2994 			} else if (c == '\0') {
2995 				yyerror("syntax error");
2996 				return (findeol());
2997 			}
2998 			if (p + 1 >= buf + sizeof(buf) - 1) {
2999 				yyerror("string too long");
3000 				return (findeol());
3001 			}
3002 			*p++ = c;
3003 		}
3004 		yylval.v.string = strdup(buf);
3005 		if (yylval.v.string == NULL)
3006 			err(1, "%s", __func__);
3007 		return (STRING);
3008 	}
3009 
3010 #define allowed_to_end_number(x) \
3011 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
3012 
3013 	if (c == '-' || isdigit(c)) {
3014 		do {
3015 			*p++ = c;
3016 			if ((size_t)(p-buf) >= sizeof(buf)) {
3017 				yyerror("string too long");
3018 				return (findeol());
3019 			}
3020 		} while ((c = lgetc(0)) != EOF && isdigit(c));
3021 		lungetc(c);
3022 		if (p == buf + 1 && buf[0] == '-')
3023 			goto nodigits;
3024 		if (c == EOF || allowed_to_end_number(c)) {
3025 			const char *errstr = NULL;
3026 
3027 			*p = '\0';
3028 			yylval.v.number = strtonum(buf, LLONG_MIN,
3029 			    LLONG_MAX, &errstr);
3030 			if (errstr) {
3031 				yyerror("\"%s\" invalid number: %s",
3032 				    buf, errstr);
3033 				return (findeol());
3034 			}
3035 			return (NUMBER);
3036 		} else {
3037 nodigits:
3038 			while (p > buf + 1)
3039 				lungetc(*--p);
3040 			c = *--p;
3041 			if (c == '-')
3042 				return (c);
3043 		}
3044 	}
3045 
3046 	if (c == '=') {
3047 		if ((c = lgetc(0)) != EOF && c == '>')
3048 			return (ARROW);
3049 		lungetc(c);
3050 		c = '=';
3051 	}
3052 
3053 #define allowed_in_string(x) \
3054 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
3055 	x != '{' && x != '}' && x != '<' && x != '>' && \
3056 	x != '!' && x != '=' && x != '#' && \
3057 	x != ','))
3058 
3059 	if (isalnum(c) || c == ':' || c == '_') {
3060 		do {
3061 			*p++ = c;
3062 			if ((size_t)(p-buf) >= sizeof(buf)) {
3063 				yyerror("string too long");
3064 				return (findeol());
3065 			}
3066 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
3067 		lungetc(c);
3068 		*p = '\0';
3069 		if ((token = lookup(buf)) == STRING)
3070 			if ((yylval.v.string = strdup(buf)) == NULL)
3071 				err(1, "%s", __func__);
3072 		return (token);
3073 	}
3074 	if (c == '\n') {
3075 		yylval.lineno = file->lineno;
3076 		file->lineno++;
3077 	}
3078 	if (c == EOF)
3079 		return (0);
3080 	return (c);
3081 }
3082 
3083 int
3084 check_file_secrecy(int fd, const char *fname)
3085 {
3086 	struct stat	st;
3087 
3088 	if (fstat(fd, &st)) {
3089 		log_warn("warn: cannot stat %s", fname);
3090 		return (-1);
3091 	}
3092 	if (st.st_uid != 0 && st.st_uid != getuid()) {
3093 		log_warnx("warn: %s: owner not root or current user", fname);
3094 		return (-1);
3095 	}
3096 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
3097 		log_warnx("warn: %s: group/world readable/writeable", fname);
3098 		return (-1);
3099 	}
3100 	return (0);
3101 }
3102 
3103 struct file *
3104 pushfile(const char *name, int secret)
3105 {
3106 	struct file	*nfile;
3107 
3108 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
3109 		log_warn("%s", __func__);
3110 		return (NULL);
3111 	}
3112 	if ((nfile->name = strdup(name)) == NULL) {
3113 		log_warn("%s", __func__);
3114 		free(nfile);
3115 		return (NULL);
3116 	}
3117 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
3118 		log_warn("%s: %s", __func__, nfile->name);
3119 		free(nfile->name);
3120 		free(nfile);
3121 		return (NULL);
3122 	} else if (secret &&
3123 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
3124 		fclose(nfile->stream);
3125 		free(nfile->name);
3126 		free(nfile);
3127 		return (NULL);
3128 	}
3129 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
3130 	nfile->ungetsize = 16;
3131 	nfile->ungetbuf = malloc(nfile->ungetsize);
3132 	if (nfile->ungetbuf == NULL) {
3133 		log_warn("%s", __func__);
3134 		fclose(nfile->stream);
3135 		free(nfile->name);
3136 		free(nfile);
3137 		return (NULL);
3138 	}
3139 	TAILQ_INSERT_TAIL(&files, nfile, entry);
3140 	return (nfile);
3141 }
3142 
3143 int
3144 popfile(void)
3145 {
3146 	struct file	*prev;
3147 
3148 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
3149 		prev->errors += file->errors;
3150 
3151 	TAILQ_REMOVE(&files, file, entry);
3152 	fclose(file->stream);
3153 	free(file->name);
3154 	free(file->ungetbuf);
3155 	free(file);
3156 	file = prev;
3157 	return (file ? 0 : EOF);
3158 }
3159 
3160 int
3161 parse_config(struct smtpd *x_conf, const char *filename, int opts)
3162 {
3163 	struct sym     *sym, *next;
3164 
3165 	conf = x_conf;
3166 	errors = 0;
3167 
3168 	if ((file = pushfile(filename, 0)) == NULL) {
3169 		purge_config(PURGE_EVERYTHING);
3170 		return (-1);
3171 	}
3172 	topfile = file;
3173 
3174 	/*
3175 	 * parse configuration
3176 	 */
3177 	setservent(1);
3178 	yyparse();
3179 	errors = file->errors;
3180 	popfile();
3181 	endservent();
3182 
3183 	/* If the socket listener was not configured, create a default one. */
3184 	if (!conf->sc_sock_listener) {
3185 		memset(&listen_opts, 0, sizeof listen_opts);
3186 		create_sock_listener(&listen_opts);
3187 	}
3188 
3189 	/* Free macros and check which have not been used. */
3190 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
3191 		if ((conf->sc_opts & SMTPD_OPT_VERBOSE) && !sym->used)
3192 			fprintf(stderr, "warning: macro '%s' not "
3193 			    "used\n", sym->nam);
3194 		if (!sym->persist) {
3195 			free(sym->nam);
3196 			free(sym->val);
3197 			TAILQ_REMOVE(&symhead, sym, entry);
3198 			free(sym);
3199 		}
3200 	}
3201 
3202 	if (TAILQ_EMPTY(conf->sc_rules)) {
3203 		log_warnx("warn: no rules, nothing to do");
3204 		errors++;
3205 	}
3206 
3207 	if (errors) {
3208 		purge_config(PURGE_EVERYTHING);
3209 		return (-1);
3210 	}
3211 
3212 	return (0);
3213 }
3214 
3215 int
3216 symset(const char *nam, const char *val, int persist)
3217 {
3218 	struct sym	*sym;
3219 
3220 	TAILQ_FOREACH(sym, &symhead, entry) {
3221 		if (strcmp(nam, sym->nam) == 0)
3222 			break;
3223 	}
3224 
3225 	if (sym != NULL) {
3226 		if (sym->persist == 1)
3227 			return (0);
3228 		else {
3229 			free(sym->nam);
3230 			free(sym->val);
3231 			TAILQ_REMOVE(&symhead, sym, entry);
3232 			free(sym);
3233 		}
3234 	}
3235 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
3236 		return (-1);
3237 
3238 	sym->nam = strdup(nam);
3239 	if (sym->nam == NULL) {
3240 		free(sym);
3241 		return (-1);
3242 	}
3243 	sym->val = strdup(val);
3244 	if (sym->val == NULL) {
3245 		free(sym->nam);
3246 		free(sym);
3247 		return (-1);
3248 	}
3249 	sym->used = 0;
3250 	sym->persist = persist;
3251 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
3252 	return (0);
3253 }
3254 
3255 int
3256 cmdline_symset(char *s)
3257 {
3258 	char	*sym, *val;
3259 	int	ret;
3260 
3261 	if ((val = strrchr(s, '=')) == NULL)
3262 		return (-1);
3263 	sym = strndup(s, val - s);
3264 	if (sym == NULL)
3265 		errx(1, "%s: strndup", __func__);
3266 	ret = symset(sym, val + 1, 1);
3267 	free(sym);
3268 
3269 	return (ret);
3270 }
3271 
3272 char *
3273 symget(const char *nam)
3274 {
3275 	struct sym	*sym;
3276 
3277 	TAILQ_FOREACH(sym, &symhead, entry) {
3278 		if (strcmp(nam, sym->nam) == 0) {
3279 			sym->used = 1;
3280 			return (sym->val);
3281 		}
3282 	}
3283 	return (NULL);
3284 }
3285 
3286 static void
3287 create_sock_listener(struct listen_opts *lo)
3288 {
3289 	struct listener *l = xcalloc(1, sizeof(*l));
3290 	lo->hostname = conf->sc_hostname;
3291 	l->ss.ss_family = AF_LOCAL;
3292 	l->ss.ss_len = sizeof(struct sockaddr *);
3293 	l->local = 1;
3294 	conf->sc_sock_listener = l;
3295 	config_listener(l, lo);
3296 }
3297 
3298 static void
3299 create_if_listener(struct listen_opts *lo)
3300 {
3301 	uint16_t	flags;
3302 
3303 	if (lo->port != 0 && lo->ssl == F_SSL)
3304 		errx(1, "invalid listen option: tls/smtps on same port");
3305 
3306 	if (lo->auth != 0 && !lo->ssl)
3307 		errx(1, "invalid listen option: auth requires tls/smtps");
3308 
3309 	if (lo->pki && !lo->ssl)
3310 		errx(1, "invalid listen option: pki requires tls/smtps");
3311 
3312 	flags = lo->flags;
3313 
3314 	if (lo->port) {
3315 		lo->flags = lo->ssl|lo->auth|flags;
3316 		lo->port = htons(lo->port);
3317 	}
3318 	else {
3319 		if (lo->ssl & F_SMTPS) {
3320 			lo->port = htons(465);
3321 			lo->flags = F_SMTPS|lo->auth|flags;
3322 		}
3323 
3324 		if (!lo->ssl || (lo->ssl & F_STARTTLS)) {
3325 			lo->port = htons(25);
3326 			lo->flags = lo->auth|flags;
3327 			if (lo->ssl & F_STARTTLS)
3328 				lo->flags |= F_STARTTLS;
3329 		}
3330 	}
3331 
3332 	if (interface(lo))
3333 		return;
3334 	if (host_v4(lo))
3335 		return;
3336 	if (host_v6(lo))
3337 		return;
3338 	if (host_dns(lo))
3339 		return;
3340 
3341 	errx(1, "invalid virtual ip or interface: %s", lo->ifx);
3342 }
3343 
3344 static void
3345 config_listener(struct listener *h,  struct listen_opts *lo)
3346 {
3347 	h->fd = -1;
3348 	h->port = lo->port;
3349 	h->flags = lo->flags;
3350 
3351 	if (lo->hostname == NULL)
3352 		lo->hostname = conf->sc_hostname;
3353 
3354 	if (lo->options & LO_FILTER) {
3355 		h->flags |= F_FILTERED;
3356 		(void)strlcpy(h->filter_name,
3357 		    lo->filtername,
3358 		    sizeof(h->filter_name));
3359 	}
3360 
3361 	h->pki_name[0] = '\0';
3362 
3363 	if (lo->authtable != NULL)
3364 		(void)strlcpy(h->authtable, lo->authtable->t_name, sizeof(h->authtable));
3365 	if (lo->pki != NULL) {
3366 		if (!lowercase(h->pki_name, lo->pki, sizeof(h->pki_name))) {
3367 			log_warnx("pki name too long: %s", lo->pki);
3368 			fatalx(NULL);
3369 		}
3370 		if (dict_get(conf->sc_pki_dict, h->pki_name) == NULL) {
3371 			log_warnx("pki name not found: %s", lo->pki);
3372 			fatalx(NULL);
3373 		}
3374 	}
3375 
3376 	if (lo->ca != NULL) {
3377 		if (!lowercase(h->ca_name, lo->ca, sizeof(h->ca_name))) {
3378 			log_warnx("ca name too long: %s", lo->ca);
3379 			fatalx(NULL);
3380 		}
3381 		if (dict_get(conf->sc_ca_dict, h->ca_name) == NULL) {
3382 			log_warnx("ca name not found: %s", lo->ca);
3383 			fatalx(NULL);
3384 		}
3385 	}
3386 	if (lo->tag != NULL)
3387 		(void)strlcpy(h->tag, lo->tag, sizeof(h->tag));
3388 
3389 	(void)strlcpy(h->hostname, lo->hostname, sizeof(h->hostname));
3390 	if (lo->hostnametable)
3391 		(void)strlcpy(h->hostnametable, lo->hostnametable->t_name, sizeof(h->hostnametable));
3392 	if (lo->sendertable) {
3393 		(void)strlcpy(h->sendertable, lo->sendertable->t_name, sizeof(h->sendertable));
3394 		if (lo->options & LO_MASQUERADE)
3395 			h->flags |= F_MASQUERADE;
3396 	}
3397 
3398 	if (lo->ssl & F_TLS_VERIFY)
3399 		h->flags |= F_TLS_VERIFY;
3400 
3401 	if (lo->ssl & F_STARTTLS_REQUIRE)
3402 		h->flags |= F_STARTTLS_REQUIRE;
3403 
3404 	if (h != conf->sc_sock_listener)
3405 		TAILQ_INSERT_TAIL(conf->sc_listeners, h, entry);
3406 }
3407 
3408 static int
3409 host_v4(struct listen_opts *lo)
3410 {
3411 	struct in_addr		 ina;
3412 	struct sockaddr_in	*sain;
3413 	struct listener		*h;
3414 
3415 	if (lo->family != AF_UNSPEC && lo->family != AF_INET)
3416 		return (0);
3417 
3418 	memset(&ina, 0, sizeof(ina));
3419 	if (inet_pton(AF_INET, lo->ifx, &ina) != 1)
3420 		return (0);
3421 
3422 	h = xcalloc(1, sizeof(*h));
3423 	sain = (struct sockaddr_in *)&h->ss;
3424 	sain->sin_len = sizeof(struct sockaddr_in);
3425 	sain->sin_family = AF_INET;
3426 	sain->sin_addr.s_addr = ina.s_addr;
3427 	sain->sin_port = lo->port;
3428 
3429 	if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
3430 		h->local = 1;
3431 	config_listener(h,  lo);
3432 
3433 	return (1);
3434 }
3435 
3436 static int
3437 host_v6(struct listen_opts *lo)
3438 {
3439 	struct in6_addr		 ina6;
3440 	struct sockaddr_in6	*sin6;
3441 	struct listener		*h;
3442 
3443 	if (lo->family != AF_UNSPEC && lo->family != AF_INET6)
3444 		return (0);
3445 
3446 	memset(&ina6, 0, sizeof(ina6));
3447 	if (inet_pton(AF_INET6, lo->ifx, &ina6) != 1)
3448 		return (0);
3449 
3450 	h = xcalloc(1, sizeof(*h));
3451 	sin6 = (struct sockaddr_in6 *)&h->ss;
3452 	sin6->sin6_len = sizeof(struct sockaddr_in6);
3453 	sin6->sin6_family = AF_INET6;
3454 	sin6->sin6_port = lo->port;
3455 	memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
3456 
3457 	if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
3458 		h->local = 1;
3459 	config_listener(h,  lo);
3460 
3461 	return (1);
3462 }
3463 
3464 static int
3465 host_dns(struct listen_opts *lo)
3466 {
3467 	struct addrinfo		 hints, *res0, *res;
3468 	int			 error, cnt = 0;
3469 	struct sockaddr_in	*sain;
3470 	struct sockaddr_in6	*sin6;
3471 	struct listener		*h;
3472 
3473 	memset(&hints, 0, sizeof(hints));
3474 	hints.ai_family = lo->family;
3475 	hints.ai_socktype = SOCK_STREAM;
3476 	hints.ai_flags = AI_ADDRCONFIG;
3477 	error = getaddrinfo(lo->ifx, NULL, &hints, &res0);
3478 	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
3479 		return (0);
3480 	if (error) {
3481 		log_warnx("warn: host_dns: could not parse \"%s\": %s", lo->ifx,
3482 		    gai_strerror(error));
3483 		return (-1);
3484 	}
3485 
3486 	for (res = res0; res; res = res->ai_next) {
3487 		if (res->ai_family != AF_INET &&
3488 		    res->ai_family != AF_INET6)
3489 			continue;
3490 		h = xcalloc(1, sizeof(*h));
3491 
3492 		h->ss.ss_family = res->ai_family;
3493 		if (res->ai_family == AF_INET) {
3494 			sain = (struct sockaddr_in *)&h->ss;
3495 			sain->sin_len = sizeof(struct sockaddr_in);
3496 			sain->sin_addr.s_addr = ((struct sockaddr_in *)
3497 			    res->ai_addr)->sin_addr.s_addr;
3498 			sain->sin_port = lo->port;
3499 			if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
3500 				h->local = 1;
3501 		} else {
3502 			sin6 = (struct sockaddr_in6 *)&h->ss;
3503 			sin6->sin6_len = sizeof(struct sockaddr_in6);
3504 			memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
3505 			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
3506 			sin6->sin6_port = lo->port;
3507 			if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
3508 				h->local = 1;
3509 		}
3510 
3511 		config_listener(h, lo);
3512 
3513 		cnt++;
3514 	}
3515 
3516 	freeaddrinfo(res0);
3517 	return (cnt);
3518 }
3519 
3520 static int
3521 interface(struct listen_opts *lo)
3522 {
3523 	struct ifaddrs *ifap, *p;
3524 	struct sockaddr_in	*sain;
3525 	struct sockaddr_in6	*sin6;
3526 	struct listener		*h;
3527 	int			ret = 0;
3528 
3529 	if (getifaddrs(&ifap) == -1)
3530 		fatal("getifaddrs");
3531 
3532 	for (p = ifap; p != NULL; p = p->ifa_next) {
3533 		if (p->ifa_addr == NULL)
3534 			continue;
3535 		if (strcmp(p->ifa_name, lo->ifx) != 0 &&
3536 		    !is_if_in_group(p->ifa_name, lo->ifx))
3537 			continue;
3538 		if (lo->family != AF_UNSPEC && lo->family != p->ifa_addr->sa_family)
3539 			continue;
3540 
3541 		h = xcalloc(1, sizeof(*h));
3542 
3543 		switch (p->ifa_addr->sa_family) {
3544 		case AF_INET:
3545 			sain = (struct sockaddr_in *)&h->ss;
3546 			*sain = *(struct sockaddr_in *)p->ifa_addr;
3547 			sain->sin_len = sizeof(struct sockaddr_in);
3548 			sain->sin_port = lo->port;
3549 			if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK))
3550 				h->local = 1;
3551 			break;
3552 
3553 		case AF_INET6:
3554 			sin6 = (struct sockaddr_in6 *)&h->ss;
3555 			*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
3556 			sin6->sin6_len = sizeof(struct sockaddr_in6);
3557 			sin6->sin6_port = lo->port;
3558 			if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
3559 				h->local = 1;
3560 			break;
3561 
3562 		default:
3563 			free(h);
3564 			continue;
3565 		}
3566 
3567 		config_listener(h, lo);
3568 		ret = 1;
3569 	}
3570 
3571 	freeifaddrs(ifap);
3572 
3573 	return ret;
3574 }
3575 
3576 int
3577 delaytonum(char *str)
3578 {
3579 	unsigned int     factor;
3580 	size_t           len;
3581 	const char      *errstr = NULL;
3582 	int              delay;
3583 
3584 	/* we need at least 1 digit and 1 unit */
3585 	len = strlen(str);
3586 	if (len < 2)
3587 		goto bad;
3588 
3589 	switch(str[len - 1]) {
3590 
3591 	case 's':
3592 		factor = 1;
3593 		break;
3594 
3595 	case 'm':
3596 		factor = 60;
3597 		break;
3598 
3599 	case 'h':
3600 		factor = 60 * 60;
3601 		break;
3602 
3603 	case 'd':
3604 		factor = 24 * 60 * 60;
3605 		break;
3606 
3607 	default:
3608 		goto bad;
3609 	}
3610 
3611 	str[len - 1] = '\0';
3612 	delay = strtonum(str, 1, INT_MAX / factor, &errstr);
3613 	if (errstr)
3614 		goto bad;
3615 
3616 	return (delay * factor);
3617 
3618 bad:
3619 	return (-1);
3620 }
3621 
3622 int
3623 is_if_in_group(const char *ifname, const char *groupname)
3624 {
3625         unsigned int		 len;
3626         struct ifgroupreq        ifgr;
3627         struct ifg_req          *ifg;
3628 	int			 s;
3629 	int			 ret = 0;
3630 
3631 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
3632 		err(1, "socket");
3633 
3634         memset(&ifgr, 0, sizeof(ifgr));
3635         if (strlcpy(ifgr.ifgr_name, ifname, IFNAMSIZ) >= IFNAMSIZ)
3636 		errx(1, "interface name too large");
3637 
3638         if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1) {
3639                 if (errno == EINVAL || errno == ENOTTY)
3640 			goto end;
3641 		err(1, "SIOCGIFGROUP");
3642         }
3643 
3644         len = ifgr.ifgr_len;
3645         ifgr.ifgr_groups = xcalloc(len/sizeof(struct ifg_req),
3646 		sizeof(struct ifg_req));
3647         if (ioctl(s, SIOCGIFGROUP, (caddr_t)&ifgr) == -1)
3648                 err(1, "SIOCGIFGROUP");
3649 
3650         ifg = ifgr.ifgr_groups;
3651         for (; ifg && len >= sizeof(struct ifg_req); ifg++) {
3652                 len -= sizeof(struct ifg_req);
3653 		if (strcmp(ifg->ifgrq_group, groupname) == 0) {
3654 			ret = 1;
3655 			break;
3656 		}
3657         }
3658         free(ifgr.ifgr_groups);
3659 
3660 end:
3661 	close(s);
3662 	return ret;
3663 }
3664 
3665 static int
3666 config_lo_mask_source(struct listen_opts *lo) {
3667 	if (lo->options & LO_MASKSOURCE) {
3668 		yyerror("mask-source already specified");
3669 		return -1;
3670 	}
3671 	lo->options |= LO_MASKSOURCE;
3672 	lo->flags |= F_MASK_SOURCE;
3673 
3674 	return 0;
3675 }
3676 
3677