1 /* $OpenBSD: parse.y,v 1.29 2021/10/22 15:03:28 florian Exp $ */
2
3 /*
4 * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
5 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8 * Copyright (c) 2001 Markus Friedl. All rights reserved.
9 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
10 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
11 *
12 * Permission to use, copy, modify, and distribute this software for any
13 * purpose with or without fee is hereby granted, provided that the above
14 * copyright notice and this permission notice appear in all copies.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 */
24
25 %{
26 #include <sys/queue.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30
31 #include <ctype.h>
32 #include <err.h>
33 #include <errno.h>
34 #include <limits.h>
35 #include <netdb.h>
36 #include <stdarg.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <syslog.h>
40 #include <unistd.h>
41
42 #include "log.h"
43 #include "unwind.h"
44
45 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
46 static struct file {
47 TAILQ_ENTRY(file) entry;
48 FILE *stream;
49 char *name;
50 size_t ungetpos;
51 size_t ungetsize;
52 u_char *ungetbuf;
53 int eof_reached;
54 int lineno;
55 int errors;
56 } *file, *topfile;
57 struct file *pushfile(const char *, int);
58 int popfile(void);
59 int check_file_secrecy(int, const char *);
60 int yyparse(void);
61 int yylex(void);
62 int yyerror(const char *, ...)
63 __attribute__((__format__ (printf, 1, 2)))
64 __attribute__((__nonnull__ (1)));
65 int kw_cmp(const void *, const void *);
66 int lookup(char *);
67 int igetc(void);
68 int lgetc(int);
69 void lungetc(int);
70 int findeol(void);
71
72 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
73 struct sym {
74 TAILQ_ENTRY(sym) entry;
75 int used;
76 int persist;
77 char *nam;
78 char *val;
79 };
80
81 int symset(const char *, const char *, int);
82 char *symget(const char *);
83 int check_pref_uniq(enum uw_resolver_type);
84
85 static struct uw_conf *conf;
86 static int errors;
87
88 void clear_config(struct uw_conf *xconf);
89 struct sockaddr_storage *host_ip(const char *);
90
91 typedef struct {
92 union {
93 int64_t number;
94 char *string;
95 struct force_tree force;
96 } v;
97 int lineno;
98 } YYSTYPE;
99
100 %}
101
102 %token INCLUDE ERROR
103 %token FORWARDER DOT PORT ODOT_FORWARDER ODOT_AUTOCONF ODOT_DHCP
104 %token AUTHENTICATION NAME PREFERENCE RECURSOR AUTOCONF DHCP STUB
105 %token BLOCK LIST LOG FORCE ACCEPT BOGUS
106
107 %token <v.string> STRING
108 %token <v.number> NUMBER
109 %type <v.number> port dot prefopt log acceptbogus
110 %type <v.string> string authname
111 %type <v.force> force_list
112
113 %%
114
115 grammar : /* empty */
116 | grammar include '\n'
117 | grammar '\n'
118 | grammar varset '\n'
119 | grammar uw_pref '\n'
120 | grammar uw_forwarder '\n'
121 | grammar block_list '\n'
122 | grammar force '\n'
123 | grammar error '\n' { file->errors++; }
124 ;
125
126 include : INCLUDE STRING {
127 struct file *nfile;
128
129 if ((nfile = pushfile($2, 0)) == NULL) {
130 yyerror("failed to include file %s", $2);
131 free($2);
132 YYERROR;
133 }
134 free($2);
135
136 file = nfile;
137 lungetc('\n');
138 }
139 ;
140
141 string : string STRING {
142 if (asprintf(&$$, "%s %s", $1, $2) == -1) {
143 free($1);
144 free($2);
145 yyerror("string: asprintf");
146 YYERROR;
147 }
148 free($1);
149 free($2);
150 }
151 | STRING
152 ;
153
154 varset : STRING '=' string {
155 char *s = $1;
156 if (cmd_opts & OPT_VERBOSE)
157 printf("%s = \"%s\"\n", $1, $3);
158 while (*s++) {
159 if (isspace((unsigned char)*s)) {
160 yyerror("macro name cannot contain "
161 "whitespace");
162 free($1);
163 free($3);
164 YYERROR;
165 }
166 }
167 if (symset($1, $3, 0) == -1)
168 fatal("cannot store variable");
169 free($1);
170 free($3);
171 }
172 ;
173
174
175 optnl : '\n' optnl /* zero or more newlines */
176 | /*empty*/
177 ;
178
179 block_list : BLOCK LIST STRING log {
180 if (conf->blocklist_file != NULL) {
181 yyerror("block list already "
182 "configured");
183 free($3);
184 YYERROR;
185 } else {
186 conf->blocklist_file = strdup($3);
187 if (conf->blocklist_file == NULL)
188 err(1, "strdup");
189 free($3);
190 conf->blocklist_log = $4;
191 }
192 }
193 ;
194
195 uw_pref : PREFERENCE {
196 conf->res_pref.len = 0;
197 memset(conf->enabled_resolvers, 0,
198 sizeof(conf->enabled_resolvers));
199 } pref_block
200 ;
201
202 pref_block : '{' optnl prefopts_l '}'
203 | prefoptsl
204 ;
205
206 prefopts_l : prefopts_l prefoptsl optnl
207 | prefoptsl optnl
208 ;
209
210 prefoptsl : prefopt {
211 if (!check_pref_uniq($1))
212 YYERROR;
213 if (conf->res_pref.len >= UW_RES_NONE) {
214 yyerror("preference list too long");
215 YYERROR;
216 }
217 conf->res_pref.types[conf->res_pref.len++] = $1;
218 conf->enabled_resolvers[$1] = 1;
219 }
220 ;
221
222 prefopt : DOT { $$ = UW_RES_DOT; }
223 | FORWARDER { $$ = UW_RES_FORWARDER; }
224 | ODOT_FORWARDER { $$ = UW_RES_ODOT_FORWARDER; }
225 | RECURSOR { $$ = UW_RES_RECURSOR; }
226 | AUTOCONF { $$ = UW_RES_AUTOCONF; }
227 | DHCP { $$ = UW_RES_AUTOCONF; }
228 | ODOT_AUTOCONF { $$ = UW_RES_ODOT_AUTOCONF; }
229 | ODOT_DHCP { $$ = UW_RES_ODOT_AUTOCONF; }
230 | STUB { $$ = UW_RES_ASR; }
231 ;
232
233 uw_forwarder : FORWARDER forwarder_block
234 ;
235
236 forwarder_block : '{' optnl forwarderopts_l '}'
237 | forwarderoptsl
238 ;
239
240 forwarderopts_l : forwarderopts_l forwarderoptsl optnl
241 | forwarderoptsl optnl
242 ;
243
244 forwarderoptsl : STRING port authname dot {
245 struct uw_forwarder *uw_fwd;
246 struct sockaddr_storage *ss;
247
248 if ((ss = host_ip($1)) == NULL) {
249 yyerror("%s is not an ip-address", $1);
250 free($1);
251 YYERROR;
252 }
253 free(ss);
254
255 if ((uw_fwd = calloc(1, sizeof(*uw_fwd))) ==
256 NULL)
257 err(1, NULL);
258
259 if ($2 < 0 || $2 > (int)USHRT_MAX) {
260 yyerror("invalid port: %lld", $2);
261 free($1);
262 free(uw_fwd);
263 YYERROR;
264 }
265 if ($2 == 0)
266 uw_fwd->port = $4 == DOT ? 853 : 53;
267 else
268 uw_fwd->port = $2;
269
270 if ($3 != NULL && $4 == 0) {
271 yyerror("authentication name can only "
272 "be used with DoT");
273 free($1);
274 free(uw_fwd);
275 YYERROR;
276 }
277
278 if (strlcpy(uw_fwd->ip, $1, sizeof(uw_fwd->ip))
279 >= sizeof(uw_fwd->ip)) {
280 free(uw_fwd);
281 yyerror("forwarder %s too long", $1);
282 free($1);
283 YYERROR;
284 }
285
286 if ($4 == DOT && $3 != NULL) {
287 if (strlcpy(uw_fwd->auth_name, $3,
288 sizeof(uw_fwd->auth_name))
289 >= sizeof(uw_fwd->auth_name)) {
290 free(uw_fwd);
291 yyerror("authentication name "
292 "%s too long", $3);
293 free($1);
294 YYERROR;
295 }
296 }
297
298 if ($4 == DOT)
299 TAILQ_INSERT_TAIL(
300 &conf->uw_dot_forwarder_list,
301 uw_fwd, entry);
302 else {
303 TAILQ_INSERT_TAIL(
304 &conf->uw_forwarder_list,
305 uw_fwd, entry);
306 }
307 free($1);
308 }
309 ;
310
311 port : PORT NUMBER { $$ = $2; }
312 | /* empty */ { $$ = 0; }
313 ;
314
315 authname: AUTHENTICATION NAME STRING { $$ = $3; }
316 | /* empty */ { $$ = NULL; }
317 ;
318
319 dot : DOT { $$ = DOT; }
320 | /* empty */ { $$ = 0; }
321 ;
322
323 log : LOG { $$ = 1; }
324 | /* empty */ { $$ = 0; }
325 ;
326
327 force : FORCE acceptbogus prefopt '{' force_list optnl '}' {
328 struct force_tree_entry *n, *nxt;
329 int error = 0;
330
331 RB_FOREACH_SAFE(n, force_tree, &$5, nxt) {
332 n->acceptbogus = $2;
333 n->type = $3;
334 RB_REMOVE(force_tree, &$5, n);
335 if (RB_INSERT(force_tree, &conf->force,
336 n)) {
337 yyerror("%s already in an force "
338 "list", n->domain);
339 error = 1;
340 }
341 }
342 if (error)
343 YYERROR;
344 }
345 ;
346
347 acceptbogus: ACCEPT BOGUS { $$ = 1; }
348 | /* empty */ { $$ = 0; }
349 ;
350
351 force_list: force_list optnl STRING {
352 struct force_tree_entry *e;
353 size_t len;
354
355 len = strlen($3);
356 e = malloc(sizeof(*e));
357 if (e == NULL)
358 err(1, NULL);
359 if (strlcpy(e->domain, $3, sizeof(e->domain)) >=
360 sizeof(e->domain)) {
361 yyerror("force %s too long", $3);
362 free($3);
363 YYERROR;
364 }
365 free($3);
366 if (len == 0 || e->domain[len-1] != '.') {
367 if (strlcat(e->domain, ".",
368 sizeof((e->domain))) >=
369 sizeof((e->domain))) {
370 yyerror("force %s too long", $3);
371 YYERROR;
372 }
373 }
374 if (RB_INSERT(force_tree, &$$, e) != NULL) {
375 log_warnx("duplicate force %s", e->domain);
376 free(e);
377 }
378 }
379 | /* empty */ {
380 RB_INIT(&$$);
381 }
382 ;
383
384 %%
385
386 struct keywords {
387 const char *k_name;
388 int k_val;
389 };
390
391 int
yyerror(const char * fmt,...)392 yyerror(const char *fmt, ...)
393 {
394 va_list ap;
395 char *msg;
396
397 file->errors++;
398 va_start(ap, fmt);
399 if (vasprintf(&msg, fmt, ap) == -1)
400 fatalx("yyerror vasprintf");
401 va_end(ap);
402 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
403 free(msg);
404 return (0);
405 }
406
407 int
kw_cmp(const void * k,const void * e)408 kw_cmp(const void *k, const void *e)
409 {
410 return (strcmp(k, ((const struct keywords *)e)->k_name));
411 }
412
413 int
lookup(char * s)414 lookup(char *s)
415 {
416 /* This has to be sorted always. */
417 static const struct keywords keywords[] = {
418 {"DoT", DOT},
419 {"accept", ACCEPT},
420 {"authentication", AUTHENTICATION},
421 {"autoconf", AUTOCONF},
422 {"block", BLOCK},
423 {"bogus", BOGUS},
424 {"dhcp", DHCP},
425 {"dot", DOT},
426 {"force", FORCE},
427 {"forwarder", FORWARDER},
428 {"include", INCLUDE},
429 {"list", LIST},
430 {"log", LOG},
431 {"name", NAME},
432 {"oDoT-autoconf", ODOT_AUTOCONF},
433 {"oDoT-dhcp", ODOT_DHCP},
434 {"oDoT-forwarder", ODOT_FORWARDER},
435 {"port", PORT},
436 {"preference", PREFERENCE},
437 {"recursor", RECURSOR},
438 {"stub", STUB},
439 {"tls", DOT},
440 };
441 const struct keywords *p;
442
443 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
444 sizeof(keywords[0]), kw_cmp);
445
446 if (p)
447 return (p->k_val);
448 else
449 return (STRING);
450 }
451
452 #define START_EXPAND 1
453 #define DONE_EXPAND 2
454
455 static int expanding;
456
457 int
igetc(void)458 igetc(void)
459 {
460 int c;
461
462 while (1) {
463 if (file->ungetpos > 0)
464 c = file->ungetbuf[--file->ungetpos];
465 else
466 c = getc(file->stream);
467
468 if (c == START_EXPAND)
469 expanding = 1;
470 else if (c == DONE_EXPAND)
471 expanding = 0;
472 else
473 break;
474 }
475 return (c);
476 }
477
478 int
lgetc(int quotec)479 lgetc(int quotec)
480 {
481 int c, next;
482
483 if (quotec) {
484 if ((c = igetc()) == EOF) {
485 yyerror("reached end of file while parsing "
486 "quoted string");
487 if (file == topfile || popfile() == EOF)
488 return (EOF);
489 return (quotec);
490 }
491 return (c);
492 }
493
494 while ((c = igetc()) == '\\') {
495 next = igetc();
496 if (next != '\n') {
497 c = next;
498 break;
499 }
500 yylval.lineno = file->lineno;
501 file->lineno++;
502 }
503
504 if (c == EOF) {
505 /*
506 * Fake EOL when hit EOF for the first time. This gets line
507 * count right if last line in included file is syntactically
508 * invalid and has no newline.
509 */
510 if (file->eof_reached == 0) {
511 file->eof_reached = 1;
512 return ('\n');
513 }
514 while (c == EOF) {
515 if (file == topfile || popfile() == EOF)
516 return (EOF);
517 c = igetc();
518 }
519 }
520 return (c);
521 }
522
523 void
lungetc(int c)524 lungetc(int c)
525 {
526 if (c == EOF)
527 return;
528
529 if (file->ungetpos >= file->ungetsize) {
530 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
531 if (p == NULL)
532 err(1, "lungetc");
533 file->ungetbuf = p;
534 file->ungetsize *= 2;
535 }
536 file->ungetbuf[file->ungetpos++] = c;
537 }
538
539 int
findeol(void)540 findeol(void)
541 {
542 int c;
543
544 /* Skip to either EOF or the first real EOL. */
545 while (1) {
546 c = lgetc(0);
547 if (c == '\n') {
548 file->lineno++;
549 break;
550 }
551 if (c == EOF)
552 break;
553 }
554 return (ERROR);
555 }
556
557 int
yylex(void)558 yylex(void)
559 {
560 char buf[8096];
561 char *p, *val;
562 int quotec, next, c;
563 int token;
564
565 top:
566 p = buf;
567 while ((c = lgetc(0)) == ' ' || c == '\t')
568 ; /* nothing */
569
570 yylval.lineno = file->lineno;
571 if (c == '#')
572 while ((c = lgetc(0)) != '\n' && c != EOF)
573 ; /* nothing */
574 if (c == '$' && !expanding) {
575 while (1) {
576 if ((c = lgetc(0)) == EOF)
577 return (0);
578
579 if (p + 1 >= buf + sizeof(buf) - 1) {
580 yyerror("string too long");
581 return (findeol());
582 }
583 if (isalnum(c) || c == '_') {
584 *p++ = c;
585 continue;
586 }
587 *p = '\0';
588 lungetc(c);
589 break;
590 }
591 val = symget(buf);
592 if (val == NULL) {
593 yyerror("macro '%s' not defined", buf);
594 return (findeol());
595 }
596 p = val + strlen(val) - 1;
597 lungetc(DONE_EXPAND);
598 while (p >= val) {
599 lungetc((unsigned char)*p);
600 p--;
601 }
602 lungetc(START_EXPAND);
603 goto top;
604 }
605
606 switch (c) {
607 case '\'':
608 case '"':
609 quotec = c;
610 while (1) {
611 if ((c = lgetc(quotec)) == EOF)
612 return (0);
613 if (c == '\n') {
614 file->lineno++;
615 continue;
616 } else if (c == '\\') {
617 if ((next = lgetc(quotec)) == EOF)
618 return (0);
619 if (next == quotec || next == ' ' ||
620 next == '\t')
621 c = next;
622 else if (next == '\n') {
623 file->lineno++;
624 continue;
625 } else
626 lungetc(next);
627 } else if (c == quotec) {
628 *p = '\0';
629 break;
630 } else if (c == '\0') {
631 yyerror("syntax error");
632 return (findeol());
633 }
634 if (p + 1 >= buf + sizeof(buf) - 1) {
635 yyerror("string too long");
636 return (findeol());
637 }
638 *p++ = c;
639 }
640 yylval.v.string = strdup(buf);
641 if (yylval.v.string == NULL)
642 err(1, "yylex: strdup");
643 return (STRING);
644 }
645
646 #define allowed_to_end_number(x) \
647 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
648
649 if (c == '-' || isdigit(c)) {
650 do {
651 *p++ = c;
652 if ((size_t)(p-buf) >= sizeof(buf)) {
653 yyerror("string too long");
654 return (findeol());
655 }
656 } while ((c = lgetc(0)) != EOF && isdigit(c));
657 lungetc(c);
658 if (p == buf + 1 && buf[0] == '-')
659 goto nodigits;
660 if (c == EOF || allowed_to_end_number(c)) {
661 const char *errstr = NULL;
662
663 *p = '\0';
664 yylval.v.number = strtonum(buf, LLONG_MIN,
665 LLONG_MAX, &errstr);
666 if (errstr) {
667 yyerror("\"%s\" invalid number: %s",
668 buf, errstr);
669 return (findeol());
670 }
671 return (NUMBER);
672 } else {
673 nodigits:
674 while (p > buf + 1)
675 lungetc((unsigned char)*--p);
676 c = (unsigned char)*--p;
677 if (c == '-')
678 return (c);
679 }
680 }
681
682 #define allowed_in_string(x) \
683 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
684 x != '{' && x != '}' && \
685 x != '!' && x != '=' && x != '#' && \
686 x != ','))
687
688 if (isalnum(c) || c == ':' || c == '_') {
689 do {
690 *p++ = c;
691 if ((size_t)(p-buf) >= sizeof(buf)) {
692 yyerror("string too long");
693 return (findeol());
694 }
695 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
696 lungetc(c);
697 *p = '\0';
698 if ((token = lookup(buf)) == STRING)
699 if ((yylval.v.string = strdup(buf)) == NULL)
700 err(1, "yylex: strdup");
701 return (token);
702 }
703 if (c == '\n') {
704 yylval.lineno = file->lineno;
705 file->lineno++;
706 }
707 if (c == EOF)
708 return (0);
709 return (c);
710 }
711
712 int
check_file_secrecy(int fd,const char * fname)713 check_file_secrecy(int fd, const char *fname)
714 {
715 struct stat st;
716
717 if (fstat(fd, &st)) {
718 log_warn("cannot stat %s", fname);
719 return (-1);
720 }
721 if (st.st_uid != 0 && st.st_uid != getuid()) {
722 log_warnx("%s: owner not root or current user", fname);
723 return (-1);
724 }
725 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
726 log_warnx("%s: group writable or world read/writable", fname);
727 return (-1);
728 }
729 return (0);
730 }
731
732 struct file *
pushfile(const char * name,int secret)733 pushfile(const char *name, int secret)
734 {
735 struct file *nfile;
736
737 if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
738 log_warn("calloc");
739 return (NULL);
740 }
741 if ((nfile->name = strdup(name)) == NULL) {
742 log_warn("strdup");
743 free(nfile);
744 return (NULL);
745 }
746 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
747 free(nfile->name);
748 free(nfile);
749 return (NULL);
750 } else if (secret &&
751 check_file_secrecy(fileno(nfile->stream), nfile->name)) {
752 fclose(nfile->stream);
753 free(nfile->name);
754 free(nfile);
755 return (NULL);
756 }
757 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
758 nfile->ungetsize = 16;
759 nfile->ungetbuf = malloc(nfile->ungetsize);
760 if (nfile->ungetbuf == NULL) {
761 log_warn("malloc");
762 fclose(nfile->stream);
763 free(nfile->name);
764 free(nfile);
765 return (NULL);
766 }
767 TAILQ_INSERT_TAIL(&files, nfile, entry);
768 return (nfile);
769 }
770
771 int
popfile(void)772 popfile(void)
773 {
774 struct file *prev;
775
776 if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
777 prev->errors += file->errors;
778
779 TAILQ_REMOVE(&files, file, entry);
780 fclose(file->stream);
781 free(file->name);
782 free(file->ungetbuf);
783 free(file);
784 file = prev;
785 return (file ? 0 : EOF);
786 }
787
788 struct uw_conf *
parse_config(char * filename)789 parse_config(char *filename)
790 {
791 static enum uw_resolver_type default_res_pref[] = {
792 UW_RES_DOT,
793 UW_RES_ODOT_FORWARDER,
794 UW_RES_FORWARDER,
795 UW_RES_RECURSOR,
796 UW_RES_ODOT_AUTOCONF,
797 UW_RES_AUTOCONF,
798 UW_RES_ASR};
799 struct sym *sym, *next;
800 int i;
801
802 conf = config_new_empty();
803
804 memcpy(&conf->res_pref.types, &default_res_pref,
805 sizeof(default_res_pref));
806 conf->res_pref.len = nitems(default_res_pref);
807 for (i = 0; i < conf->res_pref.len; i++)
808 conf->enabled_resolvers[conf->res_pref.types[i]] = 1;
809
810 file = pushfile(filename != NULL ? filename : _PATH_CONF_FILE, 0);
811 if (file == NULL) {
812 /* no default config file is fine */
813 if (errno == ENOENT && filename == NULL)
814 return (conf);
815 log_warn("%s", filename);
816 free(conf);
817 return (NULL);
818 }
819 topfile = file;
820
821 yyparse();
822 errors = file->errors;
823 popfile();
824
825 /* Free macros and check which have not been used. */
826 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
827 if ((cmd_opts & OPT_VERBOSE2) && !sym->used)
828 fprintf(stderr, "warning: macro '%s' not used\n",
829 sym->nam);
830 if (!sym->persist) {
831 free(sym->nam);
832 free(sym->val);
833 TAILQ_REMOVE(&symhead, sym, entry);
834 free(sym);
835 }
836 }
837
838 if (errors) {
839 clear_config(conf);
840 return (NULL);
841 }
842
843 return (conf);
844 }
845
846 int
symset(const char * nam,const char * val,int persist)847 symset(const char *nam, const char *val, int persist)
848 {
849 struct sym *sym;
850
851 TAILQ_FOREACH(sym, &symhead, entry) {
852 if (strcmp(nam, sym->nam) == 0)
853 break;
854 }
855
856 if (sym != NULL) {
857 if (sym->persist == 1)
858 return (0);
859 else {
860 free(sym->nam);
861 free(sym->val);
862 TAILQ_REMOVE(&symhead, sym, entry);
863 free(sym);
864 }
865 }
866 if ((sym = calloc(1, sizeof(*sym))) == NULL)
867 return (-1);
868
869 sym->nam = strdup(nam);
870 if (sym->nam == NULL) {
871 free(sym);
872 return (-1);
873 }
874 sym->val = strdup(val);
875 if (sym->val == NULL) {
876 free(sym->nam);
877 free(sym);
878 return (-1);
879 }
880 sym->used = 0;
881 sym->persist = persist;
882 TAILQ_INSERT_TAIL(&symhead, sym, entry);
883 return (0);
884 }
885
886 int
cmdline_symset(char * s)887 cmdline_symset(char *s)
888 {
889 char *sym, *val;
890 int ret;
891
892 if ((val = strrchr(s, '=')) == NULL)
893 return (-1);
894 sym = strndup(s, val - s);
895 if (sym == NULL)
896 errx(1, "%s: strndup", __func__);
897 ret = symset(sym, val + 1, 1);
898 free(sym);
899
900 return (ret);
901 }
902
903 char *
symget(const char * nam)904 symget(const char *nam)
905 {
906 struct sym *sym;
907
908 TAILQ_FOREACH(sym, &symhead, entry) {
909 if (strcmp(nam, sym->nam) == 0) {
910 sym->used = 1;
911 return (sym->val);
912 }
913 }
914 return (NULL);
915 }
916
917 void
clear_config(struct uw_conf * xconf)918 clear_config(struct uw_conf *xconf)
919 {
920 struct uw_forwarder *uw_forwarder;
921
922 while ((uw_forwarder = TAILQ_FIRST(&xconf->uw_forwarder_list)) !=
923 NULL) {
924 TAILQ_REMOVE(&xconf->uw_forwarder_list, uw_forwarder, entry);
925 free(uw_forwarder);
926 }
927 while ((uw_forwarder = TAILQ_FIRST(&xconf->uw_dot_forwarder_list)) !=
928 NULL) {
929 TAILQ_REMOVE(&xconf->uw_dot_forwarder_list, uw_forwarder,
930 entry);
931 free(uw_forwarder);
932 }
933
934 free(xconf);
935 }
936
937 struct sockaddr_storage *
host_ip(const char * s)938 host_ip(const char *s)
939 {
940 struct addrinfo hints, *res;
941 struct sockaddr_storage *ss = NULL;
942
943 memset(&hints, 0, sizeof(hints));
944 hints.ai_family = AF_UNSPEC;
945 hints.ai_socktype = SOCK_DGRAM; /*dummy*/
946 hints.ai_flags = AI_NUMERICHOST;
947 if (getaddrinfo(s, "0", &hints, &res) == 0) {
948 if (res->ai_family == AF_INET ||
949 res->ai_family == AF_INET6) {
950 if ((ss = calloc(1, sizeof(*ss))) == NULL)
951 fatal(NULL);
952 memcpy(ss, res->ai_addr, res->ai_addrlen);
953 }
954 freeaddrinfo(res);
955 }
956
957 return (ss);
958 }
959
960 int
check_pref_uniq(enum uw_resolver_type type)961 check_pref_uniq(enum uw_resolver_type type)
962 {
963 int i;
964
965 for (i = 0; i < conf->res_pref.len; i++)
966 if (conf->res_pref.types[i] == type) {
967 yyerror("%s is already in the preference list",
968 uw_resolver_type_str[type]);
969 return (0);
970 }
971
972 return (1);
973 }
974