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