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