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