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