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