xref: /openbsd/usr.sbin/smtpd/parse.y (revision 49ee354d)
1 /*	$OpenBSD: parse.y,v 1.41 2009/10/19 20:00:46 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.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/time.h>
27 #include <sys/queue.h>
28 #include <sys/tree.h>
29 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 
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 <limits.h>
42 #include <paths.h>
43 #include <pwd.h>
44 #include <netdb.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #include "smtpd.h"
52 
53 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
54 static struct file {
55 	TAILQ_ENTRY(file)	 entry;
56 	FILE			*stream;
57 	char			*name;
58 	int			 lineno;
59 	int			 errors;
60 } *file, *topfile;
61 struct file	*pushfile(const char *, int);
62 int		 popfile(void);
63 int		 check_file_secrecy(int, const char *);
64 int		 yyparse(void);
65 int		 yylex(void);
66 int		 kw_cmp(const void *, const void *);
67 int		 lookup(char *);
68 int		 lgetc(int);
69 int		 lungetc(int);
70 int		 findeol(void);
71 int		 yyerror(const char *, ...)
72     __attribute__ ((format (printf, 1, 2)));
73 
74 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
75 struct sym {
76 	TAILQ_ENTRY(sym)	 entry;
77 	int			 used;
78 	int			 persist;
79 	char			*nam;
80 	char			*val;
81 };
82 int		 symset(const char *, const char *, int);
83 char		*symget(const char *);
84 
85 struct smtpd		*conf = NULL;
86 static int		 errors = 0;
87 
88 objid_t			 last_map_id = 0;
89 struct map		*map = NULL;
90 struct rule		*rule = NULL;
91 struct mapel_list	*contents = NULL;
92 
93 struct listener	*host_v4(const char *, in_port_t);
94 struct listener	*host_v6(const char *, in_port_t);
95 int		 host_dns(const char *, const char *, struct listenerlist *,
96 		    int, in_port_t, u_int8_t);
97 int		 host(const char *, const char *, struct listenerlist *,
98 		    int, in_port_t, u_int8_t);
99 int		 interface(const char *, const char *, struct listenerlist *,
100 		    int, in_port_t, u_int8_t);
101 void		 set_localaddrs(void);
102 
103 typedef struct {
104 	union {
105 		int64_t		 number;
106 		objid_t		 object;
107 		struct timeval	 tv;
108 		struct cond	*cond;
109 		char		*string;
110 		struct host	*host;
111 	} v;
112 	int lineno;
113 } YYSTYPE;
114 
115 %}
116 
117 %token	QUEUE INTERVAL LISTEN ON ALL PORT
118 %token	MAP TYPE HASH LIST SINGLE SSL SMTPS CERTIFICATE
119 %token	DNS DB TFILE EXTERNAL DOMAIN CONFIG SOURCE
120 %token  RELAY VIA DELIVER TO MAILDIR MBOX HOSTNAME
121 %token	ACCEPT REJECT INCLUDE NETWORK ERROR MDA FROM FOR
122 %token	ARROW ENABLE AUTH TLS LOCAL VIRTUAL USER
123 %token	<v.string>	STRING
124 %token  <v.number>	NUMBER
125 %type	<v.map>		map
126 %type	<v.number>	quantifier decision port from auth ssl
127 %type	<v.cond>	condition
128 %type	<v.tv>		interval
129 %type	<v.object>	mapref
130 %type	<v.string>	certname user
131 
132 %%
133 
134 grammar		: /* empty */
135 		| grammar '\n'
136 		| grammar include '\n'
137 		| grammar varset '\n'
138 		| grammar main '\n'
139 		| grammar map '\n'
140 		| grammar rule '\n'
141 		| grammar error '\n'		{ file->errors++; }
142 		;
143 
144 include		: INCLUDE STRING		{
145 			struct file	*nfile;
146 
147 			if ((nfile = pushfile($2, 0)) == NULL) {
148 				yyerror("failed to include file %s", $2);
149 				free($2);
150 				YYERROR;
151 			}
152 			free($2);
153 
154 			file = nfile;
155 			lungetc('\n');
156 		}
157 		;
158 
159 varset		: STRING '=' STRING		{
160 			if (symset($1, $3, 0) == -1)
161 				fatal("cannot store variable");
162 			free($1);
163 			free($3);
164 		}
165 		;
166 
167 comma		: ','
168 		| nl
169 		| /* empty */
170 		;
171 
172 optnl		: '\n' optnl
173 		|
174 		;
175 
176 nl		: '\n' optnl
177 		;
178 
179 quantifier	: /* empty */			{ $$ = 1; }
180 		| 'm'				{ $$ = 60; }
181 		| 'h'				{ $$ = 3600; }
182 		| 'd'				{ $$ = 86400; }
183 		;
184 
185 interval	: NUMBER quantifier		{
186 			if ($1 < 0) {
187 				yyerror("invalid interval: %lld", $1);
188 				YYERROR;
189 			}
190 			$$.tv_usec = 0;
191 			$$.tv_sec = $1 * $2;
192 		}
193 
194 port		: PORT STRING			{
195 			struct servent	*servent;
196 
197 			servent = getservbyname($2, "tcp");
198 			if (servent == NULL) {
199 				yyerror("port %s is invalid", $2);
200 				free($2);
201 				YYERROR;
202 			}
203 			$$ = servent->s_port;
204 			free($2);
205 		}
206 		| PORT NUMBER			{
207 			if ($2 <= 0 || $2 >= (int)USHRT_MAX) {
208 				yyerror("invalid port: %lld", $2);
209 				YYERROR;
210 			}
211 			$$ = htons($2);
212 		}
213 		| /* empty */			{
214 			$$ = 0;
215 		}
216 		;
217 
218 certname	: CERTIFICATE STRING	{
219 			if (($$ = strdup($2)) == NULL)
220 				fatal(NULL);
221 			free($2);
222 		}
223 		| /* empty */			{ $$ = NULL; }
224 		;
225 
226 ssl		: SMTPS				{ $$ = F_SMTPS; }
227 		| TLS				{ $$ = F_STARTTLS; }
228 		| SSL				{ $$ = F_SSL; }
229 		| /* empty */			{ $$ = 0; }
230 
231 auth		: ENABLE AUTH  			{ $$ = 1; }
232 		| /* empty */			{ $$ = 0; }
233 		;
234 
235 main		: QUEUE INTERVAL interval	{
236 			conf->sc_qintval = $3;
237 		}
238 		| LISTEN ON STRING port ssl certname auth {
239 			char		*cert;
240 			u_int8_t	 flags;
241 
242 			if ($5 == F_SSL) {
243 				yyerror("syntax error");
244 				free($6);
245 				free($3);
246 				YYERROR;
247 			}
248 
249 			if ($5 == 0 && ($6 != NULL || $7)) {
250 				yyerror("error: must specify tls or smtps");
251 				free($6);
252 				free($3);
253 				YYERROR;
254 			}
255 
256 			if ($4 == 0) {
257 				if ($5 == F_SMTPS)
258 					$4 = htons(465);
259 				else
260 					$4 = htons(25);
261 			}
262 
263 			cert = ($6 != NULL) ? $6 : $3;
264 			flags = $5;
265 
266 			if ($7)
267 				flags |= F_AUTH;
268 
269 			if ($5 && ssl_load_certfile(conf, cert, F_SCERT) < 0) {
270 				yyerror("cannot load certificate: %s", cert);
271 				free($6);
272 				free($3);
273 				YYERROR;
274 			}
275 
276 			if (! interface($3, cert, conf->sc_listeners,
277 				MAX_LISTEN, $4, flags)) {
278 				if (host($3, cert, conf->sc_listeners,
279 					MAX_LISTEN, $4, flags) <= 0) {
280 					yyerror("invalid virtual ip or interface: %s", $3);
281 					free($6);
282 					free($3);
283 					YYERROR;
284 				}
285 			}
286 			free($6);
287 			free($3);
288 		}
289 		| HOSTNAME STRING		{
290 			if (strlcpy(conf->sc_hostname, $2,
291 			    sizeof(conf->sc_hostname)) >=
292 			    sizeof(conf->sc_hostname)) {
293 				yyerror("hostname truncated");
294 				free($2);
295 				YYERROR;
296 			}
297 			free($2);
298 		}
299 		;
300 
301 maptype		: SINGLE			{ map->m_type = T_SINGLE; }
302 		| LIST				{ map->m_type = T_LIST; }
303 		| HASH				{ map->m_type = T_HASH; }
304 		;
305 
306 mapsource	: DNS				{ map->m_src = S_DNS; }
307 		| TFILE				{ map->m_src = S_FILE; }
308 		| DB STRING			{
309 			map->m_src = S_DB;
310 			if (strlcpy(map->m_config, $2, sizeof(map->m_config))
311 			    >= sizeof(map->m_config))
312 				err(1, "pathname too long");
313 		}
314 		| EXTERNAL			{ map->m_src = S_EXT; }
315 		;
316 
317 mapopt		: TYPE maptype
318 		| SOURCE mapsource
319 		| CONFIG STRING			{
320 		}
321 		;
322 
323 mapopts_l	: mapopts_l mapopt nl
324 		| mapopt optnl
325 		;
326 
327 map		: MAP STRING			{
328 			struct map	*m;
329 
330 			TAILQ_FOREACH(m, conf->sc_maps, m_entry)
331 				if (strcmp(m->m_name, $2) == 0)
332 					break;
333 
334 			if (m != NULL) {
335 				yyerror("map %s defined twice", $2);
336 				free($2);
337 				YYERROR;
338 			}
339 			if ((m = calloc(1, sizeof(*m))) == NULL)
340 				fatal("out of memory");
341 			if (strlcpy(m->m_name, $2, sizeof(m->m_name)) >=
342 			    sizeof(m->m_name)) {
343 				yyerror("map name truncated");
344 				free(m);
345 				free($2);
346 				YYERROR;
347 			}
348 
349 			m->m_id = last_map_id++;
350 			m->m_type = T_SINGLE;
351 
352 			if (m->m_id == INT_MAX) {
353 				yyerror("too many maps defined");
354 				free($2);
355 				free(m);
356 				YYERROR;
357 			}
358 			map = m;
359 		} '{' optnl mapopts_l '}'	{
360 			if (map->m_src == S_NONE) {
361 				yyerror("map %s has no source defined", $2);
362 				free(map);
363 				map = NULL;
364 				YYERROR;
365 			}
366 			if (strcmp(map->m_name, "aliases") == 0 ||
367 			    strcmp(map->m_name, "virtual") == 0) {
368 				if (map->m_src != S_DB) {
369 					yyerror("map source must be db");
370 					free(map);
371 					map = NULL;
372 					YYERROR;
373 				}
374 			}
375 			TAILQ_INSERT_TAIL(conf->sc_maps, map, m_entry);
376 			map = NULL;
377 		}
378 		;
379 
380 keyval		: STRING ARROW STRING		{
381 			struct mapel	*me;
382 
383 			if ((me = calloc(1, sizeof(*me))) == NULL)
384 				fatal("out of memory");
385 
386 			if (strlcpy(me->me_key.med_string, $1,
387 			    sizeof(me->me_key.med_string)) >=
388 			    sizeof(me->me_key.med_string) ||
389 			    strlcpy(me->me_val.med_string, $3,
390 			    sizeof(me->me_val.med_string)) >=
391 			    sizeof(me->me_val.med_string)) {
392 				yyerror("map elements too long: %s, %s",
393 				    $1, $3);
394 				free(me);
395 				free($1);
396 				free($3);
397 				YYERROR;
398 			}
399 			free($1);
400 			free($3);
401 
402 			TAILQ_INSERT_TAIL(contents, me, me_entry);
403 		}
404 
405 keyval_list	: keyval
406 		| keyval comma keyval_list
407 		;
408 
409 stringel	: STRING			{
410 			struct mapel	*me;
411 			int bits;
412 			struct sockaddr_in ssin;
413 			struct sockaddr_in6 ssin6;
414 
415 			if ((me = calloc(1, sizeof(*me))) == NULL)
416 				fatal("out of memory");
417 
418 			/* Attempt detection of $1 format */
419 			if (strchr($1, '/') != NULL) {
420 				/* Dealing with a netmask */
421 				bzero(&ssin, sizeof(struct sockaddr_in));
422 				bits = inet_net_pton(AF_INET, $1, &ssin.sin_addr, sizeof(struct in_addr));
423 				if (bits != -1) {
424 					ssin.sin_family = AF_INET;
425 					me->me_key.med_addr.bits = bits;
426 					me->me_key.med_addr.ss = *(struct sockaddr_storage *)&ssin;
427 					me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in);
428 				}
429 				else {
430 					bzero(&ssin6, sizeof(struct sockaddr_in6));
431 					bits = inet_net_pton(AF_INET6, $1, &ssin6.sin6_addr, sizeof(struct in6_addr));
432 					if (bits == -1)
433 						err(1, "inet_net_pton");
434 					ssin6.sin6_family = AF_INET6;
435 					me->me_key.med_addr.bits = bits;
436 					me->me_key.med_addr.ss = *(struct sockaddr_storage *)&ssin6;
437 					me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in6);
438 				}
439 			}
440 			else {
441 				/* IP address ? */
442 				if (inet_pton(AF_INET, $1, &ssin.sin_addr) == 1) {
443 					ssin.sin_family = AF_INET;
444 					me->me_key.med_addr.bits = 0;
445 					me->me_key.med_addr.ss = *(struct sockaddr_storage *)&ssin;
446 					me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in);
447 				}
448 				else if (inet_pton(AF_INET6, $1, &ssin6.sin6_addr) == 1) {
449 					ssin6.sin6_family = AF_INET6;
450 					me->me_key.med_addr.bits = 0;
451 					me->me_key.med_addr.ss = *(struct sockaddr_storage *)&ssin6;
452 					me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in6);
453 				}
454 				else {
455 					/* either a hostname or a value unrelated to network */
456 					if (strlcpy(me->me_key.med_string, $1,
457 						sizeof(me->me_key.med_string)) >=
458 					    sizeof(me->me_key.med_string)) {
459 						yyerror("map element too long: %s", $1);
460 						free(me);
461 						free($1);
462 						YYERROR;
463 					}
464 				}
465 			}
466 			free($1);
467 			TAILQ_INSERT_TAIL(contents, me, me_entry);
468 		}
469 		;
470 
471 string_list	: stringel
472 		| stringel comma string_list
473 		;
474 
475 mapref		: STRING			{
476 			struct map	*m;
477 			struct mapel	*me;
478 			int bits;
479 			struct sockaddr_in ssin;
480 			struct sockaddr_in6 ssin6;
481 
482 			if ((m = calloc(1, sizeof(*m))) == NULL)
483 				fatal("out of memory");
484 			m->m_id = last_map_id++;
485 			if (m->m_id == INT_MAX) {
486 				yyerror("too many maps defined");
487 				free(m);
488 				YYERROR;
489 			}
490 			if (! bsnprintf(m->m_name, sizeof(m->m_name),
491 				"<dynamic(%u)>", m->m_id))
492 				fatal("snprintf");
493 			m->m_flags |= F_DYNAMIC|F_USED;
494 			m->m_type = T_SINGLE;
495 
496 			TAILQ_INIT(&m->m_contents);
497 
498 			if ((me = calloc(1, sizeof(*me))) == NULL)
499 				fatal("out of memory");
500 
501 			/* Attempt detection of $1 format */
502 			if (strchr($1, '/') != NULL) {
503 				/* Dealing with a netmask */
504 				bzero(&ssin, sizeof(struct sockaddr_in));
505 				bits = inet_net_pton(AF_INET, $1, &ssin.sin_addr, sizeof(struct in_addr));
506 				if (bits != -1) {
507 					ssin.sin_family = AF_INET;
508 					me->me_key.med_addr.bits = bits;
509 					me->me_key.med_addr.ss = *(struct sockaddr_storage *)&ssin;
510 					me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in);
511 				}
512 				else {
513 					bzero(&ssin6, sizeof(struct sockaddr_in6));
514 					bits = inet_net_pton(AF_INET6, $1, &ssin6.sin6_addr, sizeof(struct in6_addr));
515 					if (bits == -1)
516 						err(1, "inet_net_pton");
517 					ssin6.sin6_family = AF_INET6;
518 					me->me_key.med_addr.bits = bits;
519 					me->me_key.med_addr.ss = *(struct sockaddr_storage *)&ssin6;
520 					me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in6);
521 				}
522 			}
523 			else {
524 				/* IP address ? */
525 				if (inet_pton(AF_INET, $1, &ssin.sin_addr) == 1) {
526 					ssin.sin_family = AF_INET;
527 					me->me_key.med_addr.bits = 0;
528 					me->me_key.med_addr.ss = *(struct sockaddr_storage *)&ssin;
529 					me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in);
530 				}
531 				else if (inet_pton(AF_INET6, $1, &ssin6.sin6_addr) == 1) {
532 					ssin6.sin6_family = AF_INET6;
533 					me->me_key.med_addr.bits = 0;
534 					me->me_key.med_addr.ss = *(struct sockaddr_storage *)&ssin6;
535 					me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in6);
536 				}
537 				else {
538 					/* either a hostname or a value unrelated to network */
539 					if (strlcpy(me->me_key.med_string, $1,
540 						sizeof(me->me_key.med_string)) >=
541 					    sizeof(me->me_key.med_string)) {
542 						yyerror("map element too long: %s", $1);
543 						free(me);
544 						free(m);
545 						free($1);
546 						YYERROR;
547 					}
548 				}
549 			}
550 			free($1);
551 
552 			TAILQ_INSERT_TAIL(&m->m_contents, me, me_entry);
553 			TAILQ_INSERT_TAIL(conf->sc_maps, m, m_entry);
554 			$$ = m->m_id;
555 		}
556 		| '('				{
557 			struct map	*m;
558 
559 			if ((m = calloc(1, sizeof(*m))) == NULL)
560 				fatal("out of memory");
561 
562 			m->m_id = last_map_id++;
563 			if (m->m_id == INT_MAX) {
564 				yyerror("too many maps defined");
565 				free(m);
566 				YYERROR;
567 			}
568 			if (! bsnprintf(m->m_name, sizeof(m->m_name),
569 				"<dynamic(%u)>", m->m_id))
570 				fatal("snprintf");
571 			m->m_flags |= F_DYNAMIC|F_USED;
572 			m->m_type = T_LIST;
573 
574 			TAILQ_INIT(&m->m_contents);
575 			contents = &m->m_contents;
576 			map = m;
577 
578 		} string_list ')'		{
579 			TAILQ_INSERT_TAIL(conf->sc_maps, map, m_entry);
580 			$$ = map->m_id;
581 		}
582 		| '{'				{
583 			struct map	*m;
584 
585 			if ((m = calloc(1, sizeof(*m))) == NULL)
586 				fatal("out of memory");
587 
588 			m->m_id = last_map_id++;
589 			if (m->m_id == INT_MAX) {
590 				yyerror("too many maps defined");
591 				free(m);
592 				YYERROR;
593 			}
594 			if (! bsnprintf(m->m_name, sizeof(m->m_name),
595 				"<dynamic(%u)>", m->m_id))
596 				fatal("snprintf");
597 			m->m_flags |= F_DYNAMIC|F_USED;
598 			m->m_type = T_HASH;
599 
600 			TAILQ_INIT(&m->m_contents);
601 			contents = &m->m_contents;
602 			map = m;
603 
604 		} keyval_list '}'		{
605 			TAILQ_INSERT_TAIL(conf->sc_maps, map, m_entry);
606 			$$ = map->m_id;
607 		}
608 		| MAP STRING			{
609 			struct map	*m;
610 
611 			if ((m = map_findbyname(conf, $2)) == NULL) {
612 				yyerror("no such map: %s", $2);
613 				free($2);
614 				YYERROR;
615 			}
616 			free($2);
617 			m->m_flags |= F_USED;
618 			$$ = m->m_id;
619 		}
620 		;
621 
622 decision	: ACCEPT			{ $$ = 1; }
623 		| REJECT			{ $$ = 0; }
624 		;
625 
626 condition	: NETWORK mapref		{
627 			struct cond	*c;
628 
629 			if ((c = calloc(1, sizeof *c)) == NULL)
630 				fatal("out of memory");
631 			c->c_type = C_NET;
632 			c->c_map = $2;
633 			$$ = c;
634 		}
635 		| DOMAIN mapref			{
636 			struct cond	*c;
637 
638 			if ((c = calloc(1, sizeof *c)) == NULL)
639 				fatal("out of memory");
640 			c->c_type = C_DOM;
641 			c->c_map = $2;
642 			$$ = c;
643 		}
644 		| VIRTUAL MAP STRING		{
645 			struct cond	*c;
646 			struct map	*m;
647 
648 			if ((m = map_findbyname(conf, $3)) == NULL) {
649 				yyerror("no such map: %s", $3);
650 				free($3);
651 				YYERROR;
652 			}
653 			free($3);
654 			m->m_flags |= F_USED;
655 
656 
657 			if ((c = calloc(1, sizeof *c)) == NULL)
658 				fatal("out of memory");
659 			c->c_type = C_VDOM;
660 			c->c_map = m->m_id;
661 			$$ = c;
662 		}
663 		| LOCAL {
664 			struct cond	*c;
665 			struct map	*m;
666 			struct mapel	*me;
667 
668 			if ((m = calloc(1, sizeof(*m))) == NULL)
669 				fatal("out of memory");
670 			m->m_id = last_map_id++;
671 			if (m->m_id == INT_MAX) {
672 				yyerror("too many maps defined");
673 				free(m);
674 				YYERROR;
675 			}
676 			if (! bsnprintf(m->m_name, sizeof(m->m_name),
677 				"<dynamic(%u)>", m->m_id))
678 				fatal("snprintf");
679 			m->m_flags |= F_DYNAMIC|F_USED;
680 			m->m_type = T_SINGLE;
681 
682 			TAILQ_INIT(&m->m_contents);
683 
684 			if ((me = calloc(1, sizeof(*me))) == NULL)
685 				fatal("out of memory");
686 
687 			(void)strlcpy(me->me_key.med_string, "localhost",
688 			    sizeof(me->me_key.med_string));
689 			TAILQ_INSERT_TAIL(&m->m_contents, me, me_entry);
690 
691 			if ((me = calloc(1, sizeof(*me))) == NULL)
692 				fatal("out of memory");
693 
694 			if (gethostname(me->me_key.med_string,
695 				sizeof(me->me_key.med_string)) == -1) {
696 				yyerror("gethostname() failed");
697 				free(me);
698 				free(m);
699 				YYERROR;
700 			}
701 			TAILQ_INSERT_TAIL(&m->m_contents, me, me_entry);
702 
703 			TAILQ_INSERT_TAIL(conf->sc_maps, m, m_entry);
704 
705 			if ((c = calloc(1, sizeof *c)) == NULL)
706 				fatal("out of memory");
707 			c->c_type = C_DOM;
708 			c->c_map = m->m_id;
709 
710 			$$ = c;
711 		}
712 		| ALL				{
713 			struct cond	*c;
714 
715 			if ((c = calloc(1, sizeof *c)) == NULL)
716 				fatal("out of memory");
717 			c->c_type = C_ALL;
718 			$$ = c;
719 		}
720 		;
721 
722 condition_list	: condition comma condition_list	{
723 			TAILQ_INSERT_TAIL(&rule->r_conditions, $1, c_entry);
724 		}
725 		| condition	{
726 			TAILQ_INSERT_TAIL(&rule->r_conditions, $1, c_entry);
727 		}
728 		;
729 
730 conditions	: condition				{
731 			TAILQ_INSERT_TAIL(&rule->r_conditions, $1, c_entry);
732 		}
733 		| '{' condition_list '}'
734 		;
735 
736 user		: USER STRING		{
737 			struct passwd *pw;
738 
739 			pw = getpwnam($2);
740 			if (pw == NULL) {
741 				yyerror("user '%s' does not exist.", $2);
742 				free($2);
743 				YYERROR;
744 			}
745 			$$ = $2;
746 		}
747 		| /* empty */		{ $$ = NULL; }
748 		;
749 
750 action		: DELIVER TO MAILDIR user		{
751 			rule->r_user = $4;
752 			rule->r_action = A_MAILDIR;
753 			if (strlcpy(rule->r_value.path, "~/Maildir",
754 			    sizeof(rule->r_value.path)) >=
755 			    sizeof(rule->r_value.path))
756 				fatal("pathname too long");
757 		}
758 		| DELIVER TO MAILDIR STRING user	{
759 			rule->r_user = $5;
760 			rule->r_action = A_MAILDIR;
761 			if (strlcpy(rule->r_value.path, $4,
762 			    sizeof(rule->r_value.path)) >=
763 			    sizeof(rule->r_value.path))
764 				fatal("pathname too long");
765 			free($4);
766 		}
767 		| DELIVER TO MBOX			{
768 			rule->r_action = A_MBOX;
769 			if (strlcpy(rule->r_value.path, _PATH_MAILDIR "/%u",
770 			    sizeof(rule->r_value.path))
771 			    >= sizeof(rule->r_value.path))
772 				fatal("pathname too long");
773 		}
774 		| DELIVER TO MDA STRING user		{
775 			rule->r_user = $5;
776 			rule->r_action = A_EXT;
777 			if (strlcpy(rule->r_value.command, $4,
778 			    sizeof(rule->r_value.command))
779 			    >= sizeof(rule->r_value.command))
780 				fatal("command too long");
781 			free($4);
782 		}
783 		| RELAY				{
784 			rule->r_action = A_RELAY;
785 		}
786 		| RELAY VIA STRING port ssl certname auth {
787 			rule->r_action = A_RELAYVIA;
788 
789 			if ($5 == 0 && ($6 != NULL || $7)) {
790 				yyerror("error: must specify tls, smtps, or ssl");
791 				free($6);
792 				free($3);
793 				YYERROR;
794 			}
795 
796 			if (strlcpy(rule->r_value.relayhost.hostname, $3,
797 			    sizeof(rule->r_value.relayhost.hostname))
798 			    >= sizeof(rule->r_value.relayhost.hostname))
799 				fatal("hostname too long");
800 
801 			rule->r_value.relayhost.port = $4;
802 			rule->r_value.relayhost.flags |= $5;
803 
804 			if ($7)
805 				rule->r_value.relayhost.flags |= F_AUTH;
806 
807 			if ($6 != NULL) {
808 				if (ssl_load_certfile(conf, $6, F_CCERT) < 0) {
809 					yyerror("cannot load certificate: %s",
810 					    $6);
811 					free($6);
812 					free($3);
813 					YYERROR;
814 				}
815 				if (strlcpy(rule->r_value.relayhost.cert, $6,
816 					sizeof(rule->r_value.relayhost.cert))
817 				    >= sizeof(rule->r_value.relayhost.cert))
818 					fatal("certificate path too long");
819 			}
820 
821 			free($3);
822 			free($6);
823 		}
824 		;
825 
826 from		: FROM mapref			{
827 			$$ = $2;
828 		}
829 		| FROM ALL			{
830 			struct map	*m;
831 			struct mapel	*me;
832 			struct sockaddr_in *ssin;
833 			struct sockaddr_in6 *ssin6;
834 
835 			if ((m = calloc(1, sizeof(*m))) == NULL)
836 				fatal("out of memory");
837 			m->m_id = last_map_id++;
838 			if (m->m_id == INT_MAX) {
839 				yyerror("too many maps defined");
840 				free(m);
841 				YYERROR;
842 			}
843 			if (! bsnprintf(m->m_name, sizeof(m->m_name),
844 				"<dynamic(%u)>", m->m_id))
845 				fatal("snprintf");
846 			m->m_flags |= F_DYNAMIC|F_USED;
847 			m->m_type = T_SINGLE;
848 
849 			TAILQ_INIT(&m->m_contents);
850 
851 			if ((me = calloc(1, sizeof(*me))) == NULL)
852 				fatal("out of memory");
853 			me->me_key.med_addr.bits = 32;
854 			me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in);
855 			ssin = (struct sockaddr_in *)&me->me_key.med_addr.ss;
856 			ssin->sin_family = AF_INET;
857 			if (inet_pton(AF_INET, "0.0.0.0", &ssin->sin_addr) != 1) {
858 				free(me);
859 				free(m);
860 				YYERROR;
861 			}
862 			TAILQ_INSERT_TAIL(&m->m_contents, me, me_entry);
863 
864 			if ((me = calloc(1, sizeof(*me))) == NULL)
865 				fatal("out of memory");
866 			me->me_key.med_addr.bits = 128;
867 			me->me_key.med_addr.ss.ss_len = sizeof(struct sockaddr_in6);
868 			ssin6 = (struct sockaddr_in6 *)&me->me_key.med_addr.ss;
869 			ssin6->sin6_family = AF_INET6;
870 			if (inet_pton(AF_INET6, "::", &ssin6->sin6_addr) != 1) {
871 				free(me);
872 				free(m);
873 				YYERROR;
874 			}
875 			TAILQ_INSERT_TAIL(&m->m_contents, me, me_entry);
876 
877 			TAILQ_INSERT_TAIL(conf->sc_maps, m, m_entry);
878 			$$ = m->m_id;
879 		}
880 		| /* empty */			{
881 			struct map	*m;
882 
883 			m = map_findbyname(conf, "localhost");
884 			$$ = m->m_id;
885 		}
886 		;
887 
888 rule		: decision from			{
889 			struct rule	*r;
890 
891 			if ((r = calloc(1, sizeof(*r))) == NULL)
892 				fatal("out of memory");
893 			rule = r;
894 			rule->r_sources = map_find(conf, $2);
895 			TAILQ_INIT(&rule->r_conditions);
896 			TAILQ_INIT(&rule->r_options);
897 
898 		} FOR conditions action	{
899 			TAILQ_INSERT_TAIL(conf->sc_rules, rule, r_entry);
900 		}
901 		;
902 %%
903 
904 struct keywords {
905 	const char	*k_name;
906 	int		 k_val;
907 };
908 
909 int
910 yyerror(const char *fmt, ...)
911 {
912 	va_list		 ap;
913 
914 	file->errors++;
915 	va_start(ap, fmt);
916 	fprintf(stderr, "%s:%d: ", file->name, yylval.lineno);
917 	vfprintf(stderr, fmt, ap);
918 	fprintf(stderr, "\n");
919 	va_end(ap);
920 	return (0);
921 }
922 
923 int
924 kw_cmp(const void *k, const void *e)
925 {
926 	return (strcmp(k, ((const struct keywords *)e)->k_name));
927 }
928 
929 int
930 lookup(char *s)
931 {
932 	/* this has to be sorted always */
933 	static const struct keywords keywords[] = {
934 		{ "accept",		ACCEPT },
935 		{ "all",		ALL },
936 		{ "auth",		AUTH },
937 		{ "certificate",	CERTIFICATE },
938 		{ "config",		CONFIG },
939 		{ "db",			DB },
940 		{ "deliver",		DELIVER },
941 		{ "dns",		DNS },
942 		{ "domain",		DOMAIN },
943 		{ "enable",		ENABLE },
944 		{ "external",		EXTERNAL },
945 		{ "file",		TFILE },
946 		{ "for",		FOR },
947 		{ "from",		FROM },
948 		{ "hash",		HASH },
949 		{ "hostname",		HOSTNAME },
950 		{ "include",		INCLUDE },
951 		{ "interval",		INTERVAL },
952 		{ "list",		LIST },
953 		{ "listen",		LISTEN },
954 		{ "local",		LOCAL },
955 		{ "maildir",		MAILDIR },
956 		{ "map",		MAP },
957 		{ "mbox",		MBOX },
958 		{ "mda",		MDA },
959 		{ "network",		NETWORK },
960 		{ "on",			ON },
961 		{ "port",		PORT },
962 		{ "queue",		QUEUE },
963 		{ "reject",		REJECT },
964 		{ "relay",		RELAY },
965 		{ "single",		SINGLE },
966 		{ "smtps",		SMTPS },
967 		{ "source",		SOURCE },
968 		{ "ssl",		SSL },
969 		{ "tls",		TLS },
970 		{ "to",			TO },
971 		{ "type",		TYPE },
972 		{ "user",		USER },
973 		{ "via",		VIA },
974 		{ "virtual",		VIRTUAL },
975 	};
976 	const struct keywords	*p;
977 
978 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
979 	    sizeof(keywords[0]), kw_cmp);
980 
981 	if (p)
982 		return (p->k_val);
983 	else
984 		return (STRING);
985 }
986 
987 #define MAXPUSHBACK	128
988 
989 char	*parsebuf;
990 int	 parseindex;
991 char	 pushback_buffer[MAXPUSHBACK];
992 int	 pushback_index = 0;
993 
994 int
995 lgetc(int quotec)
996 {
997 	int		c, next;
998 
999 	if (parsebuf) {
1000 		/* Read character from the parsebuffer instead of input. */
1001 		if (parseindex >= 0) {
1002 			c = parsebuf[parseindex++];
1003 			if (c != '\0')
1004 				return (c);
1005 			parsebuf = NULL;
1006 		} else
1007 			parseindex++;
1008 	}
1009 
1010 	if (pushback_index)
1011 		return (pushback_buffer[--pushback_index]);
1012 
1013 	if (quotec) {
1014 		if ((c = getc(file->stream)) == EOF) {
1015 			yyerror("reached end of file while parsing "
1016 			    "quoted string");
1017 			if (file == topfile || popfile() == EOF)
1018 				return (EOF);
1019 			return (quotec);
1020 		}
1021 		return (c);
1022 	}
1023 
1024 	while ((c = getc(file->stream)) == '\\') {
1025 		next = getc(file->stream);
1026 		if (next != '\n') {
1027 			c = next;
1028 			break;
1029 		}
1030 		yylval.lineno = file->lineno;
1031 		file->lineno++;
1032 	}
1033 
1034 	while (c == EOF) {
1035 		if (file == topfile || popfile() == EOF)
1036 			return (EOF);
1037 		c = getc(file->stream);
1038 	}
1039 	return (c);
1040 }
1041 
1042 int
1043 lungetc(int c)
1044 {
1045 	if (c == EOF)
1046 		return (EOF);
1047 	if (parsebuf) {
1048 		parseindex--;
1049 		if (parseindex >= 0)
1050 			return (c);
1051 	}
1052 	if (pushback_index < MAXPUSHBACK-1)
1053 		return (pushback_buffer[pushback_index++] = c);
1054 	else
1055 		return (EOF);
1056 }
1057 
1058 int
1059 findeol(void)
1060 {
1061 	int	c;
1062 
1063 	parsebuf = NULL;
1064 	pushback_index = 0;
1065 
1066 	/* skip to either EOF or the first real EOL */
1067 	while (1) {
1068 		c = lgetc(0);
1069 		if (c == '\n') {
1070 			file->lineno++;
1071 			break;
1072 		}
1073 		if (c == EOF)
1074 			break;
1075 	}
1076 	return (ERROR);
1077 }
1078 
1079 int
1080 yylex(void)
1081 {
1082 	char	 buf[8096];
1083 	char	*p, *val;
1084 	int	 quotec, next, c;
1085 	int	 token;
1086 
1087 top:
1088 	p = buf;
1089 	while ((c = lgetc(0)) == ' ' || c == '\t')
1090 		; /* nothing */
1091 
1092 	yylval.lineno = file->lineno;
1093 	if (c == '#')
1094 		while ((c = lgetc(0)) != '\n' && c != EOF)
1095 			; /* nothing */
1096 	if (c == '$' && parsebuf == NULL) {
1097 		while (1) {
1098 			if ((c = lgetc(0)) == EOF)
1099 				return (0);
1100 
1101 			if (p + 1 >= buf + sizeof(buf) - 1) {
1102 				yyerror("string too long");
1103 				return (findeol());
1104 			}
1105 			if (isalnum(c) || c == '_') {
1106 				*p++ = (char)c;
1107 				continue;
1108 			}
1109 			*p = '\0';
1110 			lungetc(c);
1111 			break;
1112 		}
1113 		val = symget(buf);
1114 		if (val == NULL) {
1115 			yyerror("macro '%s' not defined", buf);
1116 			return (findeol());
1117 		}
1118 		parsebuf = val;
1119 		parseindex = 0;
1120 		goto top;
1121 	}
1122 
1123 	switch (c) {
1124 	case '\'':
1125 	case '"':
1126 		quotec = c;
1127 		while (1) {
1128 			if ((c = lgetc(quotec)) == EOF)
1129 				return (0);
1130 			if (c == '\n') {
1131 				file->lineno++;
1132 				continue;
1133 			} else if (c == '\\') {
1134 				if ((next = lgetc(quotec)) == EOF)
1135 					return (0);
1136 				if (next == quotec || c == ' ' || c == '\t')
1137 					c = next;
1138 				else if (next == '\n')
1139 					continue;
1140 				else
1141 					lungetc(next);
1142 			} else if (c == quotec) {
1143 				*p = '\0';
1144 				break;
1145 			}
1146 			if (p + 1 >= buf + sizeof(buf) - 1) {
1147 				yyerror("string too long");
1148 				return (findeol());
1149 			}
1150 			*p++ = (char)c;
1151 		}
1152 		yylval.v.string = strdup(buf);
1153 		if (yylval.v.string == NULL)
1154 			err(1, "yylex: strdup");
1155 		return (STRING);
1156 	}
1157 
1158 #define allowed_to_end_number(x) \
1159 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1160 
1161 	if (c == '-' || isdigit(c)) {
1162 		do {
1163 			*p++ = c;
1164 			if ((unsigned)(p-buf) >= sizeof(buf)) {
1165 				yyerror("string too long");
1166 				return (findeol());
1167 			}
1168 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1169 		lungetc(c);
1170 		if (p == buf + 1 && buf[0] == '-')
1171 			goto nodigits;
1172 		if (c == EOF || allowed_to_end_number(c)) {
1173 			const char *errstr = NULL;
1174 
1175 			*p = '\0';
1176 			yylval.v.number = strtonum(buf, LLONG_MIN,
1177 			    LLONG_MAX, &errstr);
1178 			if (errstr) {
1179 				yyerror("\"%s\" invalid number: %s",
1180 				    buf, errstr);
1181 				return (findeol());
1182 			}
1183 			return (NUMBER);
1184 		} else {
1185 nodigits:
1186 			while (p > buf + 1)
1187 				lungetc(*--p);
1188 			c = *--p;
1189 			if (c == '-')
1190 				return (c);
1191 		}
1192 	}
1193 
1194 	if (c == '=') {
1195 		if ((c = lgetc(0)) != EOF && c == '>')
1196 			return (ARROW);
1197 		lungetc(c);
1198 		c = '=';
1199 	}
1200 
1201 #define allowed_in_string(x) \
1202 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1203 	x != '{' && x != '}' && x != '<' && x != '>' && \
1204 	x != '!' && x != '=' && x != '#' && \
1205 	x != ','))
1206 
1207 	if (isalnum(c) || c == ':' || c == '_') {
1208 		do {
1209 			*p++ = c;
1210 			if ((unsigned)(p-buf) >= sizeof(buf)) {
1211 				yyerror("string too long");
1212 				return (findeol());
1213 			}
1214 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1215 		lungetc(c);
1216 		*p = '\0';
1217 		if ((token = lookup(buf)) == STRING)
1218 			if ((yylval.v.string = strdup(buf)) == NULL)
1219 				err(1, "yylex: strdup");
1220 		return (token);
1221 	}
1222 	if (c == '\n') {
1223 		yylval.lineno = file->lineno;
1224 		file->lineno++;
1225 	}
1226 	if (c == EOF)
1227 		return (0);
1228 	return (c);
1229 }
1230 
1231 int
1232 check_file_secrecy(int fd, const char *fname)
1233 {
1234 	struct stat	st;
1235 
1236 	if (fstat(fd, &st)) {
1237 		log_warn("cannot stat %s", fname);
1238 		return (-1);
1239 	}
1240 	if (st.st_uid != 0 && st.st_uid != getuid()) {
1241 		log_warnx("%s: owner not root or current user", fname);
1242 		return (-1);
1243 	}
1244 	if (st.st_mode & (S_IRWXG | S_IRWXO)) {
1245 		log_warnx("%s: group/world readable/writeable", fname);
1246 		return (-1);
1247 	}
1248 	return (0);
1249 }
1250 
1251 struct file *
1252 pushfile(const char *name, int secret)
1253 {
1254 	struct file	*nfile;
1255 
1256 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
1257 		log_warn("malloc");
1258 		return (NULL);
1259 	}
1260 	if ((nfile->name = strdup(name)) == NULL) {
1261 		log_warn("malloc");
1262 		free(nfile);
1263 		return (NULL);
1264 	}
1265 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1266 		log_warn("%s", nfile->name);
1267 		free(nfile->name);
1268 		free(nfile);
1269 		return (NULL);
1270 	} else if (secret &&
1271 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
1272 		fclose(nfile->stream);
1273 		free(nfile->name);
1274 		free(nfile);
1275 		return (NULL);
1276 	}
1277 	nfile->lineno = 1;
1278 	TAILQ_INSERT_TAIL(&files, nfile, entry);
1279 	return (nfile);
1280 }
1281 
1282 int
1283 popfile(void)
1284 {
1285 	struct file	*prev;
1286 
1287 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1288 		prev->errors += file->errors;
1289 
1290 	TAILQ_REMOVE(&files, file, entry);
1291 	fclose(file->stream);
1292 	free(file->name);
1293 	free(file);
1294 	file = prev;
1295 	return (file ? 0 : EOF);
1296 }
1297 
1298 int
1299 parse_config(struct smtpd *x_conf, const char *filename, int opts)
1300 {
1301 	struct sym	*sym, *next;
1302 	struct map	*m;
1303 
1304 	conf = x_conf;
1305 	bzero(conf, sizeof(*conf));
1306 	if ((conf->sc_maps = calloc(1, sizeof(*conf->sc_maps))) == NULL) {
1307 		log_warn("cannot allocate memory");
1308 		return 0;
1309 	}
1310 	if ((conf->sc_rules = calloc(1, sizeof(*conf->sc_rules))) == NULL) {
1311 		log_warn("cannot allocate memory");
1312 		free(conf->sc_maps);
1313 		return 0;
1314 	}
1315 	if ((conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners))) == NULL) {
1316 		log_warn("cannot allocate memory");
1317 		free(conf->sc_maps);
1318 		free(conf->sc_rules);
1319 		return 0;
1320 	}
1321 	if ((conf->sc_ssl = calloc(1, sizeof(*conf->sc_ssl))) == NULL) {
1322 		log_warn("cannot allocate memory");
1323 		free(conf->sc_maps);
1324 		free(conf->sc_rules);
1325 		free(conf->sc_listeners);
1326 		return 0;
1327 	}
1328 	if ((m = calloc(1, sizeof(*m))) == NULL) {
1329 		log_warn("cannot allocate memory");
1330 		free(conf->sc_maps);
1331 		free(conf->sc_rules);
1332 		free(conf->sc_listeners);
1333 		free(conf->sc_ssl);
1334 		return 0;
1335 	}
1336 
1337 	errors = 0;
1338 	last_map_id = 0;
1339 
1340 	map = NULL;
1341 	rule = NULL;
1342 
1343 	TAILQ_INIT(conf->sc_listeners);
1344 	TAILQ_INIT(conf->sc_maps);
1345 	TAILQ_INIT(conf->sc_rules);
1346 	SPLAY_INIT(conf->sc_ssl);
1347 	SPLAY_INIT(&conf->sc_sessions);
1348 
1349 	conf->sc_qintval.tv_sec = SMTPD_QUEUE_INTERVAL;
1350 	conf->sc_qintval.tv_usec = 0;
1351 	conf->sc_opts = opts;
1352 
1353 	if ((file = pushfile(filename, 0)) == NULL) {
1354 		purge_config(conf, PURGE_EVERYTHING);
1355 		return (-1);
1356 	}
1357 	topfile = file;
1358 
1359 	/*
1360 	 * declare special "local" map
1361 	 */
1362 	m->m_id = last_map_id++;
1363 	if (strlcpy(m->m_name, "localhost", sizeof(m->m_name))
1364 	    >= sizeof(m->m_name))
1365 		fatal("strlcpy");
1366 	m->m_type = T_LIST;
1367 	TAILQ_INIT(&m->m_contents);
1368 	TAILQ_INSERT_TAIL(conf->sc_maps, m, m_entry);
1369 	set_localaddrs();
1370 
1371 	/*
1372 	 * parse configuration
1373 	 */
1374 	setservent(1);
1375 	yyparse();
1376 	errors = file->errors;
1377 	popfile();
1378 	endservent();
1379 
1380 	/* Free macros and check which have not been used. */
1381 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
1382 		next = TAILQ_NEXT(sym, entry);
1383 		if ((conf->sc_opts & SMTPD_OPT_VERBOSE) && !sym->used)
1384 			fprintf(stderr, "warning: macro '%s' not "
1385 			    "used\n", sym->nam);
1386 		if (!sym->persist) {
1387 			free(sym->nam);
1388 			free(sym->val);
1389 			TAILQ_REMOVE(&symhead, sym, entry);
1390 			free(sym);
1391 		}
1392 	}
1393 
1394 	if (TAILQ_EMPTY(conf->sc_rules)) {
1395 		log_warnx("no rules, nothing to do");
1396 		errors++;
1397 	}
1398 
1399 	if (strlen(conf->sc_hostname) == 0)
1400 		if (gethostname(conf->sc_hostname,
1401 		    sizeof(conf->sc_hostname)) == -1) {
1402 			log_warn("could not determine host name");
1403 			bzero(conf->sc_hostname, sizeof(conf->sc_hostname));
1404 			errors++;
1405 		}
1406 
1407 	if (errors) {
1408 		purge_config(conf, PURGE_EVERYTHING);
1409 		return (-1);
1410 	}
1411 
1412 	return (0);
1413 }
1414 
1415 int
1416 symset(const char *nam, const char *val, int persist)
1417 {
1418 	struct sym	*sym;
1419 
1420 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
1421 	    sym = TAILQ_NEXT(sym, entry))
1422 		;	/* nothing */
1423 
1424 	if (sym != NULL) {
1425 		if (sym->persist == 1)
1426 			return (0);
1427 		else {
1428 			free(sym->nam);
1429 			free(sym->val);
1430 			TAILQ_REMOVE(&symhead, sym, entry);
1431 			free(sym);
1432 		}
1433 	}
1434 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1435 		return (-1);
1436 
1437 	sym->nam = strdup(nam);
1438 	if (sym->nam == NULL) {
1439 		free(sym);
1440 		return (-1);
1441 	}
1442 	sym->val = strdup(val);
1443 	if (sym->val == NULL) {
1444 		free(sym->nam);
1445 		free(sym);
1446 		return (-1);
1447 	}
1448 	sym->used = 0;
1449 	sym->persist = persist;
1450 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1451 	return (0);
1452 }
1453 
1454 int
1455 cmdline_symset(char *s)
1456 {
1457 	char	*sym, *val;
1458 	int	ret;
1459 	size_t	len;
1460 
1461 	if ((val = strrchr(s, '=')) == NULL)
1462 		return (-1);
1463 
1464 	len = strlen(s) - strlen(val) + 1;
1465 	if ((sym = malloc(len)) == NULL)
1466 		errx(1, "cmdline_symset: malloc");
1467 
1468 	(void)strlcpy(sym, s, len);
1469 
1470 	ret = symset(sym, val + 1, 1);
1471 	free(sym);
1472 
1473 	return (ret);
1474 }
1475 
1476 char *
1477 symget(const char *nam)
1478 {
1479 	struct sym	*sym;
1480 
1481 	TAILQ_FOREACH(sym, &symhead, entry)
1482 		if (strcmp(nam, sym->nam) == 0) {
1483 			sym->used = 1;
1484 			return (sym->val);
1485 		}
1486 	return (NULL);
1487 }
1488 
1489 struct listener *
1490 host_v4(const char *s, in_port_t port)
1491 {
1492 	struct in_addr		 ina;
1493 	struct sockaddr_in	*sain;
1494 	struct listener		*h;
1495 
1496 	bzero(&ina, sizeof(ina));
1497 	if (inet_pton(AF_INET, s, &ina) != 1)
1498 		return (NULL);
1499 
1500 	if ((h = calloc(1, sizeof(*h))) == NULL)
1501 		fatal(NULL);
1502 	sain = (struct sockaddr_in *)&h->ss;
1503 	sain->sin_len = sizeof(struct sockaddr_in);
1504 	sain->sin_family = AF_INET;
1505 	sain->sin_addr.s_addr = ina.s_addr;
1506 	sain->sin_port = port;
1507 
1508 	return (h);
1509 }
1510 
1511 struct listener *
1512 host_v6(const char *s, in_port_t port)
1513 {
1514 	struct in6_addr		 ina6;
1515 	struct sockaddr_in6	*sin6;
1516 	struct listener		*h;
1517 
1518 	bzero(&ina6, sizeof(ina6));
1519 	if (inet_pton(AF_INET6, s, &ina6) != 1)
1520 		return (NULL);
1521 
1522 	if ((h = calloc(1, sizeof(*h))) == NULL)
1523 		fatal(NULL);
1524 	sin6 = (struct sockaddr_in6 *)&h->ss;
1525 	sin6->sin6_len = sizeof(struct sockaddr_in6);
1526 	sin6->sin6_family = AF_INET6;
1527 	sin6->sin6_port = port;
1528 	memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
1529 
1530 	return (h);
1531 }
1532 
1533 int
1534 host_dns(const char *s, const char *cert, struct listenerlist *al, int max, in_port_t port,
1535     u_int8_t flags)
1536 {
1537 	struct addrinfo		 hints, *res0, *res;
1538 	int			 error, cnt = 0;
1539 	struct sockaddr_in	*sain;
1540 	struct sockaddr_in6	*sin6;
1541 	struct listener		*h;
1542 
1543 	bzero(&hints, sizeof(hints));
1544 	hints.ai_family = PF_UNSPEC;
1545 	hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
1546 	error = getaddrinfo(s, NULL, &hints, &res0);
1547 	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
1548 		return (0);
1549 	if (error) {
1550 		log_warnx("host_dns: could not parse \"%s\": %s", s,
1551 		    gai_strerror(error));
1552 		return (-1);
1553 	}
1554 
1555 	for (res = res0; res && cnt < max; res = res->ai_next) {
1556 		if (res->ai_family != AF_INET &&
1557 		    res->ai_family != AF_INET6)
1558 			continue;
1559 		if ((h = calloc(1, sizeof(*h))) == NULL)
1560 			fatal(NULL);
1561 
1562 		h->port = port;
1563 		h->flags = flags;
1564 		h->ss.ss_family = res->ai_family;
1565 		h->ssl = NULL;
1566 		h->ssl_cert_name[0] = '\0';
1567 		if (cert != NULL)
1568 			(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
1569 
1570 		if (res->ai_family == AF_INET) {
1571 			sain = (struct sockaddr_in *)&h->ss;
1572 			sain->sin_len = sizeof(struct sockaddr_in);
1573 			sain->sin_addr.s_addr = ((struct sockaddr_in *)
1574 			    res->ai_addr)->sin_addr.s_addr;
1575 			sain->sin_port = port;
1576 		} else {
1577 			sin6 = (struct sockaddr_in6 *)&h->ss;
1578 			sin6->sin6_len = sizeof(struct sockaddr_in6);
1579 			memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
1580 			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
1581 			sin6->sin6_port = port;
1582 		}
1583 
1584 		TAILQ_INSERT_HEAD(al, h, entry);
1585 		cnt++;
1586 	}
1587 	if (cnt == max && res) {
1588 		log_warnx("host_dns: %s resolves to more than %d hosts",
1589 		    s, max);
1590 	}
1591 	freeaddrinfo(res0);
1592 	return (cnt);
1593 }
1594 
1595 int
1596 host(const char *s, const char *cert, struct listenerlist *al, int max, in_port_t port,
1597     u_int8_t flags)
1598 {
1599 	struct listener *h;
1600 
1601 	h = host_v4(s, port);
1602 
1603 	/* IPv6 address? */
1604 	if (h == NULL)
1605 		h = host_v6(s, port);
1606 
1607 	if (h != NULL) {
1608 		h->port = port;
1609 		h->flags = flags;
1610 		h->ssl = NULL;
1611 		h->ssl_cert_name[0] = '\0';
1612 		if (cert != NULL)
1613 			(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
1614 
1615 
1616 		TAILQ_INSERT_HEAD(al, h, entry);
1617 		return (1);
1618 	}
1619 
1620 	return (host_dns(s, cert, al, max, port, flags));
1621 }
1622 
1623 int
1624 interface(const char *s, const char *cert, struct listenerlist *al, int max, in_port_t port,
1625     u_int8_t flags)
1626 {
1627 	struct ifaddrs *ifap, *p;
1628 	struct sockaddr_in	*sain;
1629 	struct sockaddr_in6	*sin6;
1630 	struct listener		*h;
1631 	int ret = 0;
1632 
1633 	if (getifaddrs(&ifap) == -1)
1634 		fatal("getifaddrs");
1635 
1636 	for (p = ifap; p != NULL; p = p->ifa_next) {
1637 		if (strcmp(s, p->ifa_name) != 0)
1638 			continue;
1639 
1640 		switch (p->ifa_addr->sa_family) {
1641 		case AF_INET:
1642 			if ((h = calloc(1, sizeof(*h))) == NULL)
1643 				fatal(NULL);
1644 			sain = (struct sockaddr_in *)&h->ss;
1645 			*sain = *(struct sockaddr_in *)p->ifa_addr;
1646 			sain->sin_len = sizeof(struct sockaddr_in);
1647 			sain->sin_port = port;
1648 
1649 			h->fd = -1;
1650 			h->port = port;
1651 			h->flags = flags;
1652 			h->ssl = NULL;
1653 			h->ssl_cert_name[0] = '\0';
1654 			if (cert != NULL)
1655 				(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
1656 
1657 			ret = 1;
1658 			TAILQ_INSERT_HEAD(al, h, entry);
1659 
1660 			break;
1661 
1662 		case AF_INET6:
1663 			if ((h = calloc(1, sizeof(*h))) == NULL)
1664 				fatal(NULL);
1665 			sin6 = (struct sockaddr_in6 *)&h->ss;
1666 			*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
1667 			sin6->sin6_len = sizeof(struct sockaddr_in6);
1668 			sin6->sin6_port = port;
1669 
1670 			h->fd = -1;
1671 			h->port = port;
1672 			h->flags = flags;
1673 			h->ssl = NULL;
1674 			h->ssl_cert_name[0] = '\0';
1675 			if (cert != NULL)
1676 				(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
1677 
1678 			ret = 1;
1679 			TAILQ_INSERT_HEAD(al, h, entry);
1680 
1681 			break;
1682 		}
1683 	}
1684 
1685 	freeifaddrs(ifap);
1686 
1687 	return ret;
1688 }
1689 
1690 void
1691 set_localaddrs(void)
1692 {
1693 	struct ifaddrs *ifap, *p;
1694 	struct sockaddr_storage ss;
1695 	struct sockaddr_in	*sain;
1696 	struct sockaddr_in6	*sin6;
1697 	struct map		*m;
1698 	struct mapel		*me;
1699 
1700 	if (getifaddrs(&ifap) == -1)
1701 		fatal("getifaddrs");
1702 
1703 	m = map_findbyname(conf, "localhost");
1704 
1705 	for (p = ifap; p != NULL; p = p->ifa_next) {
1706 		switch (p->ifa_addr->sa_family) {
1707 		case AF_INET:
1708 			sain = (struct sockaddr_in *)&ss;
1709 			*sain = *(struct sockaddr_in *)p->ifa_addr;
1710 			sain->sin_len = sizeof(struct sockaddr_in);
1711 
1712 			if ((me = calloc(1, sizeof(*me))) == NULL)
1713 				fatal("out of memory");
1714 			me->me_key.med_addr.bits = 0;
1715 			me->me_key.med_addr.ss = *(struct sockaddr_storage *)sain;
1716 			TAILQ_INSERT_TAIL(&m->m_contents, me, me_entry);
1717 
1718 			break;
1719 
1720 		case AF_INET6:
1721 			sin6 = (struct sockaddr_in6 *)&ss;
1722 			*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
1723 			sin6->sin6_len = sizeof(struct sockaddr_in6);
1724 
1725 			if ((me = calloc(1, sizeof(*me))) == NULL)
1726 				fatal("out of memory");
1727 			me->me_key.med_addr.bits = 0;
1728 			me->me_key.med_addr.ss = *(struct sockaddr_storage *)sin6;
1729 			TAILQ_INSERT_TAIL(&m->m_contents, me, me_entry);
1730 
1731 			break;
1732 		}
1733 	}
1734 
1735 	freeifaddrs(ifap);
1736 }
1737