1 /*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
7 *
8 * See the COPYRIGHT file distributed with this work for additional
9 * information regarding copyright ownership.
10 */
11
12 /*! \file */
13
14 #include <ctype.h>
15 #include <errno.h>
16 #include <inttypes.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include <isc/buffer.h>
22 #include <isc/dir.h>
23 #include <isc/errno.h>
24 #include <isc/formatcheck.h>
25 #include <isc/glob.h>
26 #include <isc/lex.h>
27 #include <isc/log.h>
28 #include <isc/mem.h>
29 #include <isc/net.h>
30 #include <isc/netaddr.h>
31 #include <isc/netmgr.h>
32 #include <isc/netscope.h>
33 #include <isc/print.h>
34 #include <isc/sockaddr.h>
35 #include <isc/string.h>
36 #include <isc/symtab.h>
37 #include <isc/util.h>
38
39 #include <dns/ttl.h>
40
41 #include <isccfg/cfg.h>
42 #include <isccfg/grammar.h>
43 #include <isccfg/log.h>
44
45 /* Shorthand */
46 #define CAT CFG_LOGCATEGORY_CONFIG
47 #define MOD CFG_LOGMODULE_PARSER
48
49 #define MAP_SYM 1 /* Unique type for isc_symtab */
50
51 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
52
53 /* Check a return value. */
54 #define CHECK(op) \
55 do { \
56 result = (op); \
57 if (result != ISC_R_SUCCESS) \
58 goto cleanup; \
59 } while (0)
60
61 /* Clean up a configuration object if non-NULL. */
62 #define CLEANUP_OBJ(obj) \
63 do { \
64 if ((obj) != NULL) \
65 cfg_obj_destroy(pctx, &(obj)); \
66 } while (0)
67
68 /*
69 * Forward declarations of static functions.
70 */
71
72 static void
73 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
74
75 static isc_result_t
76 parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
77
78 static void
79 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
80
81 static void
82 free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
83
84 static isc_result_t
85 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
86
87 static isc_result_t
88 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
89 cfg_obj_t **ret);
90
91 static void
92 free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
93
94 static isc_result_t
95 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
96
97 static void
98 free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
99
100 static isc_result_t
101 parse_symtab_elt(cfg_parser_t *pctx, const char *name, cfg_type_t *elttype,
102 isc_symtab_t *symtab, bool callback);
103
104 static void
105 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
106
107 static isc_result_t
108 cfg_getstringtoken(cfg_parser_t *pctx);
109
110 static void
111 parser_complain(cfg_parser_t *pctx, bool is_warning, unsigned int flags,
112 const char *format, va_list args);
113
114 #if defined(HAVE_GEOIP2)
115 static isc_result_t
116 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
117
118 static void
119 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj);
120
121 static void
122 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type);
123 #endif /* HAVE_GEOIP2 */
124
125 /*
126 * Data representations. These correspond to members of the
127 * "value" union in struct cfg_obj (except "void", which does
128 * not need a union member).
129 */
130
131 cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
132 cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
133 cfg_rep_t cfg_rep_string = { "string", free_string };
134 cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
135 cfg_rep_t cfg_rep_map = { "map", free_map };
136 cfg_rep_t cfg_rep_list = { "list", free_list };
137 cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
138 cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
139 cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
140 cfg_rep_t cfg_rep_void = { "void", free_noop };
141 cfg_rep_t cfg_rep_fixedpoint = { "fixedpoint", free_noop };
142 cfg_rep_t cfg_rep_percentage = { "percentage", free_noop };
143 cfg_rep_t cfg_rep_duration = { "duration", free_noop };
144
145 /*
146 * Configuration type definitions.
147 */
148
149 /*%
150 * An implicit list. These are formed by clauses that occur multiple times.
151 */
152 static cfg_type_t cfg_type_implicitlist = { "implicitlist", NULL,
153 print_list, NULL,
154 &cfg_rep_list, NULL };
155
156 /* Functions. */
157
158 void
cfg_print_obj(cfg_printer_t * pctx,const cfg_obj_t * obj)159 cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
160 REQUIRE(pctx != NULL);
161 REQUIRE(obj != NULL);
162
163 obj->type->print(pctx, obj);
164 }
165
166 void
cfg_print_chars(cfg_printer_t * pctx,const char * text,int len)167 cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
168 REQUIRE(pctx != NULL);
169 REQUIRE(text != NULL);
170
171 pctx->f(pctx->closure, text, len);
172 }
173
174 static void
print_open(cfg_printer_t * pctx)175 print_open(cfg_printer_t *pctx) {
176 if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
177 cfg_print_cstr(pctx, "{ ");
178 } else {
179 cfg_print_cstr(pctx, "{\n");
180 pctx->indent++;
181 }
182 }
183
184 void
cfg_print_indent(cfg_printer_t * pctx)185 cfg_print_indent(cfg_printer_t *pctx) {
186 int indent = pctx->indent;
187 if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
188 cfg_print_cstr(pctx, " ");
189 return;
190 }
191 while (indent > 0) {
192 cfg_print_cstr(pctx, "\t");
193 indent--;
194 }
195 }
196
197 static void
print_close(cfg_printer_t * pctx)198 print_close(cfg_printer_t *pctx) {
199 if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
200 pctx->indent--;
201 cfg_print_indent(pctx);
202 }
203 cfg_print_cstr(pctx, "}");
204 }
205
206 isc_result_t
cfg_parse_obj(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)207 cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
208 isc_result_t result;
209
210 REQUIRE(pctx != NULL);
211 REQUIRE(type != NULL);
212 REQUIRE(ret != NULL && *ret == NULL);
213
214 result = type->parse(pctx, type, ret);
215 if (result != ISC_R_SUCCESS) {
216 return (result);
217 }
218 ENSURE(*ret != NULL);
219 return (ISC_R_SUCCESS);
220 }
221
222 void
cfg_print(const cfg_obj_t * obj,void (* f)(void * closure,const char * text,int textlen),void * closure)223 cfg_print(const cfg_obj_t *obj,
224 void (*f)(void *closure, const char *text, int textlen),
225 void *closure) {
226 REQUIRE(obj != NULL);
227 REQUIRE(f != NULL);
228
229 cfg_printx(obj, 0, f, closure);
230 }
231
232 void
cfg_printx(const cfg_obj_t * obj,unsigned int flags,void (* f)(void * closure,const char * text,int textlen),void * closure)233 cfg_printx(const cfg_obj_t *obj, unsigned int flags,
234 void (*f)(void *closure, const char *text, int textlen),
235 void *closure) {
236 cfg_printer_t pctx;
237
238 REQUIRE(obj != NULL);
239 REQUIRE(f != NULL);
240
241 pctx.f = f;
242 pctx.closure = closure;
243 pctx.indent = 0;
244 pctx.flags = flags;
245 obj->type->print(&pctx, obj);
246 }
247
248 /* Tuples. */
249
250 isc_result_t
cfg_create_tuple(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)251 cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
252 isc_result_t result;
253 const cfg_tuplefielddef_t *fields;
254 const cfg_tuplefielddef_t *f;
255 cfg_obj_t *obj = NULL;
256 unsigned int nfields = 0;
257 int i;
258
259 REQUIRE(pctx != NULL);
260 REQUIRE(type != NULL);
261 REQUIRE(ret != NULL && *ret == NULL);
262
263 fields = type->of;
264
265 for (f = fields; f->name != NULL; f++) {
266 nfields++;
267 }
268
269 CHECK(cfg_create_obj(pctx, type, &obj));
270 obj->value.tuple = isc_mem_get(pctx->mctx,
271 nfields * sizeof(cfg_obj_t *));
272 for (f = fields, i = 0; f->name != NULL; f++, i++) {
273 obj->value.tuple[i] = NULL;
274 }
275 *ret = obj;
276 return (ISC_R_SUCCESS);
277
278 cleanup:
279 if (obj != NULL) {
280 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
281 }
282 return (result);
283 }
284
285 isc_result_t
cfg_parse_tuple(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)286 cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
287 isc_result_t result;
288 const cfg_tuplefielddef_t *fields;
289 const cfg_tuplefielddef_t *f;
290 cfg_obj_t *obj = NULL;
291 unsigned int i;
292
293 REQUIRE(pctx != NULL);
294 REQUIRE(type != NULL);
295 REQUIRE(ret != NULL && *ret == NULL);
296
297 fields = type->of;
298
299 CHECK(cfg_create_tuple(pctx, type, &obj));
300 for (f = fields, i = 0; f->name != NULL; f++, i++) {
301 CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
302 }
303
304 *ret = obj;
305 return (ISC_R_SUCCESS);
306
307 cleanup:
308 CLEANUP_OBJ(obj);
309 return (result);
310 }
311
312 void
cfg_print_tuple(cfg_printer_t * pctx,const cfg_obj_t * obj)313 cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
314 unsigned int i;
315 const cfg_tuplefielddef_t *fields;
316 const cfg_tuplefielddef_t *f;
317 bool need_space = false;
318
319 REQUIRE(pctx != NULL);
320 REQUIRE(obj != NULL);
321
322 fields = obj->type->of;
323
324 for (f = fields, i = 0; f->name != NULL; f++, i++) {
325 const cfg_obj_t *fieldobj = obj->value.tuple[i];
326 if (need_space && fieldobj->type->rep != &cfg_rep_void) {
327 cfg_print_cstr(pctx, " ");
328 }
329 cfg_print_obj(pctx, fieldobj);
330 need_space = (need_space ||
331 fieldobj->type->print != cfg_print_void);
332 }
333 }
334
335 void
cfg_doc_tuple(cfg_printer_t * pctx,const cfg_type_t * type)336 cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
337 const cfg_tuplefielddef_t *fields;
338 const cfg_tuplefielddef_t *f;
339 bool need_space = false;
340
341 REQUIRE(pctx != NULL);
342 REQUIRE(type != NULL);
343
344 fields = type->of;
345
346 for (f = fields; f->name != NULL; f++) {
347 if (need_space) {
348 cfg_print_cstr(pctx, " ");
349 }
350 cfg_doc_obj(pctx, f->type);
351 need_space = (f->type->print != cfg_print_void);
352 }
353 }
354
355 static void
free_tuple(cfg_parser_t * pctx,cfg_obj_t * obj)356 free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
357 unsigned int i;
358 const cfg_tuplefielddef_t *fields = obj->type->of;
359 const cfg_tuplefielddef_t *f;
360 unsigned int nfields = 0;
361
362 if (obj->value.tuple == NULL) {
363 return;
364 }
365
366 for (f = fields, i = 0; f->name != NULL; f++, i++) {
367 CLEANUP_OBJ(obj->value.tuple[i]);
368 nfields++;
369 }
370 isc_mem_put(pctx->mctx, obj->value.tuple,
371 nfields * sizeof(cfg_obj_t *));
372 }
373
374 bool
cfg_obj_istuple(const cfg_obj_t * obj)375 cfg_obj_istuple(const cfg_obj_t *obj) {
376 REQUIRE(obj != NULL);
377 return (obj->type->rep == &cfg_rep_tuple);
378 }
379
380 const cfg_obj_t *
cfg_tuple_get(const cfg_obj_t * tupleobj,const char * name)381 cfg_tuple_get(const cfg_obj_t *tupleobj, const char *name) {
382 unsigned int i;
383 const cfg_tuplefielddef_t *fields;
384 const cfg_tuplefielddef_t *f;
385
386 REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
387 REQUIRE(name != NULL);
388
389 fields = tupleobj->type->of;
390 for (f = fields, i = 0; f->name != NULL; f++, i++) {
391 if (strcmp(f->name, name) == 0) {
392 return (tupleobj->value.tuple[i]);
393 }
394 }
395 INSIST(0);
396 ISC_UNREACHABLE();
397 }
398
399 isc_result_t
cfg_parse_special(cfg_parser_t * pctx,int special)400 cfg_parse_special(cfg_parser_t *pctx, int special) {
401 isc_result_t result;
402
403 REQUIRE(pctx != NULL);
404
405 CHECK(cfg_gettoken(pctx, 0));
406 if (pctx->token.type == isc_tokentype_special &&
407 pctx->token.value.as_char == special)
408 {
409 return (ISC_R_SUCCESS);
410 }
411
412 cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
413 return (ISC_R_UNEXPECTEDTOKEN);
414 cleanup:
415 return (result);
416 }
417
418 /*
419 * Parse a required semicolon. If it is not there, log
420 * an error and increment the error count but continue
421 * parsing. Since the next token is pushed back,
422 * care must be taken to make sure it is eventually
423 * consumed or an infinite loop may result.
424 */
425 static isc_result_t
parse_semicolon(cfg_parser_t * pctx)426 parse_semicolon(cfg_parser_t *pctx) {
427 isc_result_t result;
428
429 CHECK(cfg_gettoken(pctx, 0));
430 if (pctx->token.type == isc_tokentype_special &&
431 pctx->token.value.as_char == ';')
432 {
433 return (ISC_R_SUCCESS);
434 }
435
436 cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
437 cfg_ungettoken(pctx);
438 cleanup:
439 return (result);
440 }
441
442 /*
443 * Parse EOF, logging and returning an error if not there.
444 */
445 static isc_result_t
parse_eof(cfg_parser_t * pctx)446 parse_eof(cfg_parser_t *pctx) {
447 isc_result_t result;
448
449 CHECK(cfg_gettoken(pctx, 0));
450
451 if (pctx->token.type == isc_tokentype_eof) {
452 return (ISC_R_SUCCESS);
453 }
454
455 cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
456 return (ISC_R_UNEXPECTEDTOKEN);
457 cleanup:
458 return (result);
459 }
460
461 /* A list of files, used internally for pctx->files. */
462
463 static cfg_type_t cfg_type_filelist = { "filelist", NULL,
464 print_list, NULL,
465 &cfg_rep_list, &cfg_type_qstring };
466
467 isc_result_t
cfg_parser_create(isc_mem_t * mctx,isc_log_t * lctx,cfg_parser_t ** ret)468 cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
469 isc_result_t result;
470 cfg_parser_t *pctx;
471 isc_lexspecials_t specials;
472
473 REQUIRE(mctx != NULL);
474 REQUIRE(ret != NULL && *ret == NULL);
475
476 pctx = isc_mem_get(mctx, sizeof(*pctx));
477
478 pctx->mctx = NULL;
479 isc_mem_attach(mctx, &pctx->mctx);
480
481 isc_refcount_init(&pctx->references, 1);
482
483 pctx->lctx = lctx;
484 pctx->lexer = NULL;
485 pctx->seen_eof = false;
486 pctx->ungotten = false;
487 pctx->errors = 0;
488 pctx->warnings = 0;
489 pctx->open_files = NULL;
490 pctx->closed_files = NULL;
491 pctx->line = 0;
492 pctx->callback = NULL;
493 pctx->callbackarg = NULL;
494 pctx->token.type = isc_tokentype_unknown;
495 pctx->flags = 0;
496 pctx->buf_name = NULL;
497
498 memset(specials, 0, sizeof(specials));
499 specials['{'] = 1;
500 specials['}'] = 1;
501 specials[';'] = 1;
502 specials['/'] = 1;
503 specials['"'] = 1;
504 specials['!'] = 1;
505
506 CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
507
508 isc_lex_setspecials(pctx->lexer, specials);
509 isc_lex_setcomments(pctx->lexer,
510 (ISC_LEXCOMMENT_C | ISC_LEXCOMMENT_CPLUSPLUS |
511 ISC_LEXCOMMENT_SHELL));
512
513 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
514 CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
515
516 *ret = pctx;
517 return (ISC_R_SUCCESS);
518
519 cleanup:
520 if (pctx->lexer != NULL) {
521 isc_lex_destroy(&pctx->lexer);
522 }
523 CLEANUP_OBJ(pctx->open_files);
524 CLEANUP_OBJ(pctx->closed_files);
525 isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
526 return (result);
527 }
528
529 void
cfg_parser_setflags(cfg_parser_t * pctx,unsigned int flags,bool turn_on)530 cfg_parser_setflags(cfg_parser_t *pctx, unsigned int flags, bool turn_on) {
531 REQUIRE(pctx != NULL);
532
533 if (turn_on) {
534 pctx->flags |= flags;
535 } else {
536 pctx->flags &= ~flags;
537 }
538 }
539
540 static isc_result_t
parser_openfile(cfg_parser_t * pctx,const char * filename)541 parser_openfile(cfg_parser_t *pctx, const char *filename) {
542 isc_result_t result;
543 cfg_listelt_t *elt = NULL;
544 cfg_obj_t *stringobj = NULL;
545
546 result = isc_lex_openfile(pctx->lexer, filename);
547 if (result != ISC_R_SUCCESS) {
548 cfg_parser_error(pctx, 0, "open: %s: %s", filename,
549 isc_result_totext(result));
550 goto cleanup;
551 }
552
553 CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
554 CHECK(create_listelt(pctx, &elt));
555 elt->obj = stringobj;
556 ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
557
558 return (ISC_R_SUCCESS);
559 cleanup:
560 CLEANUP_OBJ(stringobj);
561 return (result);
562 }
563
564 void
cfg_parser_setcallback(cfg_parser_t * pctx,cfg_parsecallback_t callback,void * arg)565 cfg_parser_setcallback(cfg_parser_t *pctx, cfg_parsecallback_t callback,
566 void *arg) {
567 REQUIRE(pctx != NULL);
568
569 pctx->callback = callback;
570 pctx->callbackarg = arg;
571 }
572
573 void
cfg_parser_reset(cfg_parser_t * pctx)574 cfg_parser_reset(cfg_parser_t *pctx) {
575 REQUIRE(pctx != NULL);
576
577 if (pctx->lexer != NULL) {
578 isc_lex_close(pctx->lexer);
579 }
580
581 pctx->seen_eof = false;
582 pctx->ungotten = false;
583 pctx->errors = 0;
584 pctx->warnings = 0;
585 pctx->line = 0;
586 }
587
588 /*
589 * Parse a configuration using a pctx where a lexer has already
590 * been set up with a source.
591 */
592 static isc_result_t
parse2(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)593 parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
594 isc_result_t result;
595 cfg_obj_t *obj = NULL;
596
597 result = cfg_parse_obj(pctx, type, &obj);
598
599 if (pctx->errors != 0) {
600 /* Errors have been logged. */
601 if (result == ISC_R_SUCCESS) {
602 result = ISC_R_FAILURE;
603 }
604 goto cleanup;
605 }
606
607 if (result != ISC_R_SUCCESS) {
608 /* Parsing failed but no errors have been logged. */
609 cfg_parser_error(pctx, 0, "parsing failed: %s",
610 isc_result_totext(result));
611 goto cleanup;
612 }
613
614 CHECK(parse_eof(pctx));
615
616 *ret = obj;
617 return (ISC_R_SUCCESS);
618
619 cleanup:
620 CLEANUP_OBJ(obj);
621 return (result);
622 }
623
624 isc_result_t
cfg_parse_file(cfg_parser_t * pctx,const char * filename,const cfg_type_t * type,cfg_obj_t ** ret)625 cfg_parse_file(cfg_parser_t *pctx, const char *filename, const cfg_type_t *type,
626 cfg_obj_t **ret) {
627 isc_result_t result;
628 cfg_listelt_t *elt;
629
630 REQUIRE(pctx != NULL);
631 REQUIRE(filename != NULL);
632 REQUIRE(type != NULL);
633 REQUIRE(ret != NULL && *ret == NULL);
634
635 CHECK(parser_openfile(pctx, filename));
636
637 result = parse2(pctx, type, ret);
638
639 /* Clean up the opened file */
640 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
641 INSIST(elt != NULL);
642 ISC_LIST_UNLINK(pctx->open_files->value.list, elt, link);
643 ISC_LIST_APPEND(pctx->closed_files->value.list, elt, link);
644
645 cleanup:
646 return (result);
647 }
648
649 isc_result_t
cfg_parse_buffer(cfg_parser_t * pctx,isc_buffer_t * buffer,const char * file,unsigned int line,const cfg_type_t * type,unsigned int flags,cfg_obj_t ** ret)650 cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer, const char *file,
651 unsigned int line, const cfg_type_t *type, unsigned int flags,
652 cfg_obj_t **ret) {
653 isc_result_t result;
654
655 REQUIRE(pctx != NULL);
656 REQUIRE(type != NULL);
657 REQUIRE(buffer != NULL);
658 REQUIRE(ret != NULL && *ret == NULL);
659 REQUIRE((flags & ~(CFG_PCTX_NODEPRECATED)) == 0);
660
661 CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
662
663 pctx->buf_name = file;
664 pctx->flags = flags;
665
666 if (line != 0U) {
667 CHECK(isc_lex_setsourceline(pctx->lexer, line));
668 }
669
670 CHECK(parse2(pctx, type, ret));
671 pctx->buf_name = NULL;
672
673 cleanup:
674 return (result);
675 }
676
677 void
cfg_parser_attach(cfg_parser_t * src,cfg_parser_t ** dest)678 cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
679 REQUIRE(src != NULL);
680 REQUIRE(dest != NULL && *dest == NULL);
681
682 isc_refcount_increment(&src->references);
683 *dest = src;
684 }
685
686 void
cfg_parser_destroy(cfg_parser_t ** pctxp)687 cfg_parser_destroy(cfg_parser_t **pctxp) {
688 cfg_parser_t *pctx;
689
690 REQUIRE(pctxp != NULL && *pctxp != NULL);
691 pctx = *pctxp;
692 *pctxp = NULL;
693
694 if (isc_refcount_decrement(&pctx->references) == 1) {
695 isc_lex_destroy(&pctx->lexer);
696 /*
697 * Cleaning up open_files does not
698 * close the files; that was already done
699 * by closing the lexer.
700 */
701 CLEANUP_OBJ(pctx->open_files);
702 CLEANUP_OBJ(pctx->closed_files);
703 isc_mem_putanddetach(&pctx->mctx, pctx, sizeof(*pctx));
704 }
705 }
706
707 /*
708 * void
709 */
710 isc_result_t
cfg_parse_void(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)711 cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
712 REQUIRE(pctx != NULL);
713 REQUIRE(ret != NULL && *ret == NULL);
714
715 UNUSED(type);
716
717 return (cfg_create_obj(pctx, &cfg_type_void, ret));
718 }
719
720 void
cfg_print_void(cfg_printer_t * pctx,const cfg_obj_t * obj)721 cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
722 REQUIRE(pctx != NULL);
723 REQUIRE(obj != NULL);
724
725 UNUSED(pctx);
726 UNUSED(obj);
727 }
728
729 void
cfg_doc_void(cfg_printer_t * pctx,const cfg_type_t * type)730 cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
731 REQUIRE(pctx != NULL);
732 REQUIRE(type != NULL);
733
734 UNUSED(pctx);
735 UNUSED(type);
736 }
737
738 bool
cfg_obj_isvoid(const cfg_obj_t * obj)739 cfg_obj_isvoid(const cfg_obj_t *obj) {
740 REQUIRE(obj != NULL);
741 return (obj->type->rep == &cfg_rep_void);
742 }
743
744 cfg_type_t cfg_type_void = { "void", cfg_parse_void, cfg_print_void,
745 cfg_doc_void, &cfg_rep_void, NULL };
746
747 /*
748 * percentage
749 */
750 isc_result_t
cfg_parse_percentage(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)751 cfg_parse_percentage(cfg_parser_t *pctx, const cfg_type_t *type,
752 cfg_obj_t **ret) {
753 char *endp;
754 isc_result_t result;
755 cfg_obj_t *obj = NULL;
756 uint64_t percent;
757
758 REQUIRE(pctx != NULL);
759 REQUIRE(ret != NULL && *ret == NULL);
760
761 UNUSED(type);
762
763 CHECK(cfg_gettoken(pctx, 0));
764 if (pctx->token.type != isc_tokentype_string) {
765 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected percentage");
766 return (ISC_R_UNEXPECTEDTOKEN);
767 }
768
769 percent = strtoull(TOKEN_STRING(pctx), &endp, 10);
770 if (*endp != '%' || *(endp + 1) != 0) {
771 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected percentage");
772 return (ISC_R_UNEXPECTEDTOKEN);
773 }
774
775 CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
776 obj->value.uint32 = (uint32_t)percent;
777 *ret = obj;
778
779 cleanup:
780 return (result);
781 }
782
783 void
cfg_print_percentage(cfg_printer_t * pctx,const cfg_obj_t * obj)784 cfg_print_percentage(cfg_printer_t *pctx, const cfg_obj_t *obj) {
785 char buf[64];
786 int n;
787
788 REQUIRE(pctx != NULL);
789 REQUIRE(obj != NULL);
790
791 n = snprintf(buf, sizeof(buf), "%u%%", obj->value.uint32);
792 INSIST(n > 0 && (size_t)n < sizeof(buf));
793 cfg_print_chars(pctx, buf, strlen(buf));
794 }
795
796 uint32_t
cfg_obj_aspercentage(const cfg_obj_t * obj)797 cfg_obj_aspercentage(const cfg_obj_t *obj) {
798 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_percentage);
799 return (obj->value.uint32);
800 }
801
802 cfg_type_t cfg_type_percentage = { "percentage", cfg_parse_percentage,
803 cfg_print_percentage, cfg_doc_terminal,
804 &cfg_rep_percentage, NULL };
805
806 bool
cfg_obj_ispercentage(const cfg_obj_t * obj)807 cfg_obj_ispercentage(const cfg_obj_t *obj) {
808 REQUIRE(obj != NULL);
809 return (obj->type->rep == &cfg_rep_percentage);
810 }
811
812 /*
813 * Fixed point
814 */
815 isc_result_t
cfg_parse_fixedpoint(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)816 cfg_parse_fixedpoint(cfg_parser_t *pctx, const cfg_type_t *type,
817 cfg_obj_t **ret) {
818 isc_result_t result;
819 cfg_obj_t *obj = NULL;
820 size_t n1, n2, n3, l;
821 const char *p;
822
823 REQUIRE(pctx != NULL);
824 REQUIRE(ret != NULL && *ret == NULL);
825
826 UNUSED(type);
827
828 CHECK(cfg_gettoken(pctx, 0));
829 if (pctx->token.type != isc_tokentype_string) {
830 cfg_parser_error(pctx, CFG_LOG_NEAR,
831 "expected fixed point number");
832 return (ISC_R_UNEXPECTEDTOKEN);
833 }
834
835 p = TOKEN_STRING(pctx);
836 l = strlen(p);
837 n1 = strspn(p, "0123456789");
838 n2 = strspn(p + n1, ".");
839 n3 = strspn(p + n1 + n2, "0123456789");
840
841 if ((n1 + n2 + n3 != l) || (n1 + n3 == 0) || n1 > 5 || n2 > 1 || n3 > 2)
842 {
843 cfg_parser_error(pctx, CFG_LOG_NEAR,
844 "expected fixed point number");
845 return (ISC_R_UNEXPECTEDTOKEN);
846 }
847
848 CHECK(cfg_create_obj(pctx, &cfg_type_fixedpoint, &obj));
849
850 obj->value.uint32 = strtoul(p, NULL, 10) * 100;
851 switch (n3) {
852 case 2:
853 obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10);
854 break;
855 case 1:
856 obj->value.uint32 += strtoul(p + n1 + n2, NULL, 10) * 10;
857 break;
858 }
859 *ret = obj;
860
861 cleanup:
862 return (result);
863 }
864
865 void
cfg_print_fixedpoint(cfg_printer_t * pctx,const cfg_obj_t * obj)866 cfg_print_fixedpoint(cfg_printer_t *pctx, const cfg_obj_t *obj) {
867 char buf[64];
868 int n;
869
870 REQUIRE(pctx != NULL);
871 REQUIRE(obj != NULL);
872
873 n = snprintf(buf, sizeof(buf), "%u.%02u", obj->value.uint32 / 100,
874 obj->value.uint32 % 100);
875 INSIST(n > 0 && (size_t)n < sizeof(buf));
876 cfg_print_chars(pctx, buf, strlen(buf));
877 }
878
879 uint32_t
cfg_obj_asfixedpoint(const cfg_obj_t * obj)880 cfg_obj_asfixedpoint(const cfg_obj_t *obj) {
881 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_fixedpoint);
882 return (obj->value.uint32);
883 }
884
885 cfg_type_t cfg_type_fixedpoint = { "fixedpoint", cfg_parse_fixedpoint,
886 cfg_print_fixedpoint, cfg_doc_terminal,
887 &cfg_rep_fixedpoint, NULL };
888
889 bool
cfg_obj_isfixedpoint(const cfg_obj_t * obj)890 cfg_obj_isfixedpoint(const cfg_obj_t *obj) {
891 REQUIRE(obj != NULL);
892 return (obj->type->rep == &cfg_rep_fixedpoint);
893 }
894
895 /*
896 * uint32
897 */
898 isc_result_t
cfg_parse_uint32(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)899 cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
900 isc_result_t result;
901 cfg_obj_t *obj = NULL;
902
903 REQUIRE(pctx != NULL);
904 REQUIRE(ret != NULL && *ret == NULL);
905
906 UNUSED(type);
907
908 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
909 if (pctx->token.type != isc_tokentype_number) {
910 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
911 return (ISC_R_UNEXPECTEDTOKEN);
912 }
913
914 CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
915
916 obj->value.uint32 = pctx->token.value.as_ulong;
917 *ret = obj;
918 cleanup:
919 return (result);
920 }
921
922 void
cfg_print_cstr(cfg_printer_t * pctx,const char * s)923 cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
924 cfg_print_chars(pctx, s, strlen(s));
925 }
926
927 void
cfg_print_rawuint(cfg_printer_t * pctx,unsigned int u)928 cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
929 char buf[32];
930
931 snprintf(buf, sizeof(buf), "%u", u);
932 cfg_print_cstr(pctx, buf);
933 }
934
935 void
cfg_print_uint32(cfg_printer_t * pctx,const cfg_obj_t * obj)936 cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
937 cfg_print_rawuint(pctx, obj->value.uint32);
938 }
939
940 bool
cfg_obj_isuint32(const cfg_obj_t * obj)941 cfg_obj_isuint32(const cfg_obj_t *obj) {
942 REQUIRE(obj != NULL);
943 return (obj->type->rep == &cfg_rep_uint32);
944 }
945
946 uint32_t
cfg_obj_asuint32(const cfg_obj_t * obj)947 cfg_obj_asuint32(const cfg_obj_t *obj) {
948 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
949 return (obj->value.uint32);
950 }
951
952 cfg_type_t cfg_type_uint32 = { "integer", cfg_parse_uint32,
953 cfg_print_uint32, cfg_doc_terminal,
954 &cfg_rep_uint32, NULL };
955
956 /*
957 * uint64
958 */
959 bool
cfg_obj_isuint64(const cfg_obj_t * obj)960 cfg_obj_isuint64(const cfg_obj_t *obj) {
961 REQUIRE(obj != NULL);
962 return (obj->type->rep == &cfg_rep_uint64);
963 }
964
965 uint64_t
cfg_obj_asuint64(const cfg_obj_t * obj)966 cfg_obj_asuint64(const cfg_obj_t *obj) {
967 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
968 return (obj->value.uint64);
969 }
970
971 void
cfg_print_uint64(cfg_printer_t * pctx,const cfg_obj_t * obj)972 cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
973 char buf[32];
974
975 snprintf(buf, sizeof(buf), "%" PRIu64, obj->value.uint64);
976 cfg_print_cstr(pctx, buf);
977 }
978
979 cfg_type_t cfg_type_uint64 = { "64_bit_integer", NULL,
980 cfg_print_uint64, cfg_doc_terminal,
981 &cfg_rep_uint64, NULL };
982
983 /*
984 * Get the number of digits in a number.
985 */
986 static size_t
numlen(uint32_t num)987 numlen(uint32_t num) {
988 uint32_t period = num;
989 size_t count = 0;
990
991 if (period == 0) {
992 return (1);
993 }
994 while (period > 0) {
995 count++;
996 period /= 10;
997 }
998 return (count);
999 }
1000
1001 /*
1002 * duration
1003 */
1004 void
cfg_print_duration(cfg_printer_t * pctx,const cfg_obj_t * obj)1005 cfg_print_duration(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1006 char buf[CFG_DURATION_MAXLEN];
1007 char *str;
1008 const char *indicators = "YMWDHMS";
1009 int count, i;
1010 int durationlen[7];
1011 cfg_duration_t duration;
1012 /*
1013 * D ? The duration has a date part.
1014 * T ? The duration has a time part.
1015 */
1016 bool D = false, T = false;
1017
1018 REQUIRE(pctx != NULL);
1019 REQUIRE(obj != NULL);
1020
1021 duration = obj->value.duration;
1022
1023 /* If this is not an ISO 8601 duration, just print it as a number. */
1024 if (!duration.iso8601) {
1025 cfg_print_rawuint(pctx, duration.parts[6]);
1026 return;
1027 }
1028
1029 /* Calculate length of string. */
1030 buf[0] = 'P';
1031 buf[1] = '\0';
1032 str = &buf[1];
1033 count = 2;
1034 for (i = 0; i < 6; i++) {
1035 if (duration.parts[i] > 0) {
1036 durationlen[i] = 1 + numlen(duration.parts[i]);
1037 if (i < 4) {
1038 D = true;
1039 } else {
1040 T = true;
1041 }
1042 } else {
1043 durationlen[i] = 0;
1044 }
1045 count += durationlen[i];
1046 }
1047 /*
1048 * Special case for seconds which is not taken into account in the
1049 * above for loop: Count the length of the seconds part if it is
1050 * non-zero, or if all the other parts are also zero. In the latter
1051 * case this function will print "PT0S".
1052 */
1053 if (duration.parts[6] > 0 ||
1054 (!D && !duration.parts[4] && !duration.parts[5])) {
1055 durationlen[6] = 1 + numlen(duration.parts[6]);
1056 T = true;
1057 count += durationlen[6];
1058 }
1059 /* Add one character for the time indicator. */
1060 if (T) {
1061 count++;
1062 }
1063 INSIST(count < CFG_DURATION_MAXLEN);
1064
1065 /* Now print the duration. */
1066 for (i = 0; i < 6; i++) {
1067 /*
1068 * We don't check here if weeks and other time indicator are
1069 * used mutually exclusively.
1070 */
1071 if (duration.parts[i] > 0) {
1072 snprintf(str, durationlen[i] + 2, "%u%c",
1073 (uint32_t)duration.parts[i], indicators[i]);
1074 str += durationlen[i] + 1;
1075 }
1076 if (i == 3 && T) {
1077 snprintf(str, 2, "T");
1078 str += 1;
1079 }
1080 }
1081 /* Special case for seconds. */
1082 if (duration.parts[6] > 0 ||
1083 (!D && !duration.parts[4] && !duration.parts[3])) {
1084 snprintf(str, durationlen[6] + 2, "%u%c",
1085 (uint32_t)duration.parts[6], indicators[6]);
1086 }
1087 cfg_print_chars(pctx, buf, strlen(buf));
1088 }
1089
1090 void
cfg_print_duration_or_unlimited(cfg_printer_t * pctx,const cfg_obj_t * obj)1091 cfg_print_duration_or_unlimited(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1092 cfg_duration_t duration;
1093
1094 REQUIRE(pctx != NULL);
1095 REQUIRE(obj != NULL);
1096
1097 duration = obj->value.duration;
1098
1099 if (duration.unlimited) {
1100 cfg_print_cstr(pctx, "unlimited");
1101 } else {
1102 cfg_print_duration(pctx, obj);
1103 }
1104 }
1105
1106 bool
cfg_obj_isduration(const cfg_obj_t * obj)1107 cfg_obj_isduration(const cfg_obj_t *obj) {
1108 REQUIRE(obj != NULL);
1109 return (obj->type->rep == &cfg_rep_duration);
1110 }
1111
1112 uint32_t
cfg_obj_asduration(const cfg_obj_t * obj)1113 cfg_obj_asduration(const cfg_obj_t *obj) {
1114 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_duration);
1115 uint32_t duration = 0;
1116 duration += obj->value.duration.parts[6]; /* Seconds */
1117 duration += obj->value.duration.parts[5] * 60; /* Minutes */
1118 duration += obj->value.duration.parts[4] * 3600; /* Hours */
1119 duration += obj->value.duration.parts[3] * 86400; /* Days */
1120 duration += obj->value.duration.parts[2] * 86400 * 7; /* Weaks */
1121 /*
1122 * The below additions are not entirely correct
1123 * because days may very per month and per year.
1124 */
1125 duration += obj->value.duration.parts[1] * 86400 * 31; /* Months */
1126 duration += obj->value.duration.parts[0] * 86400 * 365; /* Years */
1127 return (duration);
1128 }
1129
1130 /*
1131 * duration_fromtext initially taken from OpenDNSSEC code base.
1132 * Modified to fit the BIND 9 code.
1133 *
1134 * Copyright (c) 2009-2018 NLNet Labs.
1135 * All rights reserved.
1136 *
1137 * Redistribution and use in source and binary forms, with or without
1138 * modification, are permitted provided that the following conditions
1139 * are met:
1140 * 1. Redistributions of source code must retain the above copyright
1141 * notice, this list of conditions and the following disclaimer.
1142 * 2. Redistributions in binary form must reproduce the above copyright
1143 * notice, this list of conditions and the following disclaimer in the
1144 * documentation and/or other materials provided with the distribution.
1145 *
1146 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1147 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1148 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1149 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
1150 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1151 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
1152 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1153 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
1154 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
1155 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
1156 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1157 */
1158 static isc_result_t
duration_fromtext(isc_textregion_t * source,cfg_duration_t * duration)1159 duration_fromtext(isc_textregion_t *source, cfg_duration_t *duration) {
1160 char buf[CFG_DURATION_MAXLEN];
1161 char *P, *X, *T, *W, *str;
1162 bool not_weeks = false;
1163 int i;
1164
1165 /*
1166 * Copy the buffer as it may not be NULL terminated.
1167 * Anyone having a duration longer than 63 characters is crazy.
1168 */
1169 if (source->length > sizeof(buf) - 1) {
1170 return (ISC_R_BADNUMBER);
1171 }
1172 /* Copy source->length bytes and NULL terminate. */
1173 snprintf(buf, sizeof(buf), "%.*s", (int)source->length, source->base);
1174 str = buf;
1175
1176 /* Clear out duration. */
1177 for (i = 0; i < 7; i++) {
1178 duration->parts[i] = 0;
1179 }
1180
1181 /* Every duration starts with 'P' */
1182 P = strpbrk(str, "Pp");
1183 if (P == NULL) {
1184 return (ISC_R_BADNUMBER);
1185 }
1186
1187 /* Record the time indicator. */
1188 T = strpbrk(str, "Tt");
1189
1190 /* Record years. */
1191 X = strpbrk(str, "Yy");
1192 if (X != NULL) {
1193 duration->parts[0] = atoi(str + 1);
1194 str = X;
1195 not_weeks = true;
1196 }
1197
1198 /* Record months. */
1199 X = strpbrk(str, "Mm");
1200
1201 /*
1202 * M could be months or minutes. This is months if there is no time
1203 * part, or this M indicator is before the time indicator.
1204 */
1205 if (X != NULL && (T == NULL || (size_t)(X - P) < (size_t)(T - P))) {
1206 duration->parts[1] = atoi(str + 1);
1207 str = X;
1208 not_weeks = true;
1209 }
1210
1211 /* Record days. */
1212 X = strpbrk(str, "Dd");
1213 if (X != NULL) {
1214 duration->parts[3] = atoi(str + 1);
1215 str = X;
1216 not_weeks = true;
1217 }
1218
1219 /* Time part? */
1220 if (T != NULL) {
1221 str = T;
1222 not_weeks = true;
1223 }
1224
1225 /* Record hours. */
1226 X = strpbrk(str, "Hh");
1227 if (X != NULL && T != NULL) {
1228 duration->parts[4] = atoi(str + 1);
1229 str = X;
1230 not_weeks = true;
1231 }
1232
1233 /* Record minutes. */
1234 X = strpbrk(str, "Mm");
1235
1236 /*
1237 * M could be months or minutes. This is minutes if there is a time
1238 * part and the M indicator is behind the time indicator.
1239 */
1240 if (X != NULL && T != NULL && (size_t)(X - P) > (size_t)(T - P)) {
1241 duration->parts[5] = atoi(str + 1);
1242 str = X;
1243 not_weeks = true;
1244 }
1245
1246 /* Record seconds. */
1247 X = strpbrk(str, "Ss");
1248 if (X != NULL && T != NULL) {
1249 duration->parts[6] = atoi(str + 1);
1250 str = X;
1251 not_weeks = true;
1252 }
1253
1254 /* Or is the duration configured in weeks? */
1255 W = strpbrk(buf, "Ww");
1256 if (W != NULL) {
1257 if (not_weeks) {
1258 /* Mix of weeks and other indicators is not allowed */
1259 return (ISC_R_BADNUMBER);
1260 } else {
1261 duration->parts[2] = atoi(str + 1);
1262 str = W;
1263 }
1264 }
1265
1266 /* Deal with trailing garbage. */
1267 if (str[1] != '\0') {
1268 return (ISC_R_BADNUMBER);
1269 }
1270
1271 return (ISC_R_SUCCESS);
1272 }
1273
1274 static isc_result_t
parse_duration(cfg_parser_t * pctx,cfg_obj_t ** ret)1275 parse_duration(cfg_parser_t *pctx, cfg_obj_t **ret) {
1276 isc_result_t result;
1277 cfg_obj_t *obj = NULL;
1278 cfg_duration_t duration;
1279
1280 duration.unlimited = false;
1281
1282 if (toupper((unsigned char)TOKEN_STRING(pctx)[0]) == 'P') {
1283 result = duration_fromtext(&pctx->token.value.as_textregion,
1284 &duration);
1285 duration.iso8601 = true;
1286 } else {
1287 uint32_t ttl;
1288 result = dns_ttl_fromtext(&pctx->token.value.as_textregion,
1289 &ttl);
1290 /*
1291 * With dns_ttl_fromtext() the information on optional units.
1292 * is lost, and is treated as seconds from now on.
1293 */
1294 for (int i = 0; i < 6; i++) {
1295 duration.parts[i] = 0;
1296 }
1297 duration.parts[6] = ttl;
1298 duration.iso8601 = false;
1299 }
1300
1301 if (result == ISC_R_RANGE) {
1302 cfg_parser_error(pctx, CFG_LOG_NEAR,
1303 "duration or TTL out of range");
1304 return (result);
1305 } else if (result != ISC_R_SUCCESS) {
1306 goto cleanup;
1307 }
1308
1309 CHECK(cfg_create_obj(pctx, &cfg_type_duration, &obj));
1310 obj->value.duration = duration;
1311 *ret = obj;
1312
1313 return (ISC_R_SUCCESS);
1314
1315 cleanup:
1316 cfg_parser_error(pctx, CFG_LOG_NEAR,
1317 "expected ISO 8601 duration or TTL value");
1318 return (result);
1319 }
1320
1321 isc_result_t
cfg_parse_duration(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1322 cfg_parse_duration(cfg_parser_t *pctx, const cfg_type_t *type,
1323 cfg_obj_t **ret) {
1324 isc_result_t result;
1325
1326 UNUSED(type);
1327
1328 CHECK(cfg_gettoken(pctx, 0));
1329 if (pctx->token.type != isc_tokentype_string) {
1330 result = ISC_R_UNEXPECTEDTOKEN;
1331 goto cleanup;
1332 }
1333
1334 return (parse_duration(pctx, ret));
1335
1336 cleanup:
1337 cfg_parser_error(pctx, CFG_LOG_NEAR,
1338 "expected ISO 8601 duration or TTL value");
1339 return (result);
1340 }
1341
1342 isc_result_t
cfg_parse_duration_or_unlimited(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1343 cfg_parse_duration_or_unlimited(cfg_parser_t *pctx, const cfg_type_t *type,
1344 cfg_obj_t **ret) {
1345 isc_result_t result;
1346 cfg_obj_t *obj = NULL;
1347 cfg_duration_t duration;
1348
1349 UNUSED(type);
1350
1351 CHECK(cfg_gettoken(pctx, 0));
1352 if (pctx->token.type != isc_tokentype_string) {
1353 result = ISC_R_UNEXPECTEDTOKEN;
1354 goto cleanup;
1355 }
1356
1357 if (strcmp(TOKEN_STRING(pctx), "unlimited") == 0) {
1358 for (int i = 0; i < 7; i++) {
1359 duration.parts[i] = 0;
1360 }
1361 duration.iso8601 = false;
1362 duration.unlimited = true;
1363
1364 CHECK(cfg_create_obj(pctx, &cfg_type_duration, &obj));
1365 obj->value.duration = duration;
1366 *ret = obj;
1367 return (ISC_R_SUCCESS);
1368 }
1369
1370 return (parse_duration(pctx, ret));
1371
1372 cleanup:
1373 cfg_parser_error(pctx, CFG_LOG_NEAR,
1374 "expected ISO 8601 duration, TTL value, or unlimited");
1375 return (result);
1376 }
1377
1378 /*%
1379 * A duration as defined by ISO 8601 (P[n]Y[n]M[n]DT[n]H[n]M[n]S).
1380 * - P is the duration indicator ("period") placed at the start.
1381 * - Y is the year indicator that follows the value for the number of years.
1382 * - M is the month indicator that follows the value for the number of months.
1383 * - D is the day indicator that follows the value for the number of days.
1384 * - T is the time indicator that precedes the time components.
1385 * - H is the hour indicator that follows the value for the number of hours.
1386 * - M is the minute indicator that follows the value for the number of
1387 * minutes.
1388 * - S is the second indicator that follows the value for the number of
1389 * seconds.
1390 *
1391 * A duration can also be a TTL value (number + optional unit).
1392 */
1393 cfg_type_t cfg_type_duration = { "duration", cfg_parse_duration,
1394 cfg_print_duration, cfg_doc_terminal,
1395 &cfg_rep_duration, NULL };
1396 cfg_type_t cfg_type_duration_or_unlimited = { "duration_or_unlimited",
1397 cfg_parse_duration_or_unlimited,
1398 cfg_print_duration_or_unlimited,
1399 cfg_doc_terminal,
1400 &cfg_rep_duration,
1401 NULL };
1402
1403 /*
1404 * qstring (quoted string), ustring (unquoted string), astring
1405 * (any string), sstring (secret string)
1406 */
1407
1408 /* Create a string object from a null-terminated C string. */
1409 static isc_result_t
create_string(cfg_parser_t * pctx,const char * contents,const cfg_type_t * type,cfg_obj_t ** ret)1410 create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
1411 cfg_obj_t **ret) {
1412 isc_result_t result;
1413 cfg_obj_t *obj = NULL;
1414 int len;
1415
1416 CHECK(cfg_create_obj(pctx, type, &obj));
1417 len = strlen(contents);
1418 obj->value.string.length = len;
1419 obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
1420 if (obj->value.string.base == 0) {
1421 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
1422 return (ISC_R_NOMEMORY);
1423 }
1424 memmove(obj->value.string.base, contents, len);
1425 obj->value.string.base[len] = '\0';
1426
1427 *ret = obj;
1428 cleanup:
1429 return (result);
1430 }
1431
1432 isc_result_t
cfg_parse_qstring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1433 cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1434 isc_result_t result;
1435
1436 REQUIRE(pctx != NULL);
1437 REQUIRE(ret != NULL && *ret == NULL);
1438
1439 UNUSED(type);
1440
1441 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
1442 if (pctx->token.type != isc_tokentype_qstring) {
1443 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
1444 return (ISC_R_UNEXPECTEDTOKEN);
1445 }
1446 return (create_string(pctx, TOKEN_STRING(pctx), &cfg_type_qstring,
1447 ret));
1448 cleanup:
1449 return (result);
1450 }
1451
1452 static isc_result_t
parse_ustring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1453 parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1454 isc_result_t result;
1455
1456 UNUSED(type);
1457
1458 CHECK(cfg_gettoken(pctx, 0));
1459 if (pctx->token.type != isc_tokentype_string) {
1460 cfg_parser_error(pctx, CFG_LOG_NEAR,
1461 "expected unquoted string");
1462 return (ISC_R_UNEXPECTEDTOKEN);
1463 }
1464 return (create_string(pctx, TOKEN_STRING(pctx), &cfg_type_ustring,
1465 ret));
1466 cleanup:
1467 return (result);
1468 }
1469
1470 isc_result_t
cfg_parse_astring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1471 cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1472 isc_result_t result;
1473
1474 REQUIRE(pctx != NULL);
1475 REQUIRE(ret != NULL && *ret == NULL);
1476
1477 UNUSED(type);
1478
1479 CHECK(cfg_getstringtoken(pctx));
1480 return (create_string(pctx, TOKEN_STRING(pctx), &cfg_type_qstring,
1481 ret));
1482 cleanup:
1483 return (result);
1484 }
1485
1486 isc_result_t
cfg_parse_sstring(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1487 cfg_parse_sstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1488 isc_result_t result;
1489
1490 REQUIRE(pctx != NULL);
1491 REQUIRE(ret != NULL && *ret == NULL);
1492
1493 UNUSED(type);
1494
1495 CHECK(cfg_getstringtoken(pctx));
1496 return (create_string(pctx, TOKEN_STRING(pctx), &cfg_type_sstring,
1497 ret));
1498 cleanup:
1499 return (result);
1500 }
1501
1502 static isc_result_t
parse_btext(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1503 parse_btext(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1504 isc_result_t result;
1505
1506 UNUSED(type);
1507
1508 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_BTEXT));
1509 if (pctx->token.type != isc_tokentype_btext) {
1510 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected bracketed text");
1511 return (ISC_R_UNEXPECTEDTOKEN);
1512 }
1513 return (create_string(pctx, TOKEN_STRING(pctx),
1514 &cfg_type_bracketed_text, ret));
1515 cleanup:
1516 return (result);
1517 }
1518
1519 static void
print_btext(cfg_printer_t * pctx,const cfg_obj_t * obj)1520 print_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1521 /*
1522 * We need to print "{" instead of running print_open()
1523 * in order to preserve the exact original formatting
1524 * of the bracketed text. But we increment the indent value
1525 * so that print_close() will leave us back in our original
1526 * state.
1527 */
1528 pctx->indent++;
1529 cfg_print_cstr(pctx, "{");
1530 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
1531 print_close(pctx);
1532 }
1533
1534 static void
doc_btext(cfg_printer_t * pctx,const cfg_type_t * type)1535 doc_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
1536 UNUSED(type);
1537
1538 cfg_print_cstr(pctx, "{ <unspecified-text> }");
1539 }
1540
1541 bool
cfg_is_enum(const char * s,const char * const * enums)1542 cfg_is_enum(const char *s, const char *const *enums) {
1543 const char *const *p;
1544
1545 REQUIRE(s != NULL);
1546 REQUIRE(enums != NULL);
1547
1548 for (p = enums; *p != NULL; p++) {
1549 if (strcasecmp(*p, s) == 0) {
1550 return (true);
1551 }
1552 }
1553 return (false);
1554 }
1555
1556 static isc_result_t
check_enum(cfg_parser_t * pctx,cfg_obj_t * obj,const char * const * enums)1557 check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
1558 const char *s = obj->value.string.base;
1559
1560 if (cfg_is_enum(s, enums)) {
1561 return (ISC_R_SUCCESS);
1562 }
1563 cfg_parser_error(pctx, 0, "'%s' unexpected", s);
1564 return (ISC_R_UNEXPECTEDTOKEN);
1565 }
1566
1567 isc_result_t
cfg_parse_enum(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1568 cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1569 isc_result_t result;
1570 cfg_obj_t *obj = NULL;
1571
1572 REQUIRE(pctx != NULL);
1573 REQUIRE(type != NULL);
1574 REQUIRE(ret != NULL && *ret == NULL);
1575
1576 CHECK(parse_ustring(pctx, NULL, &obj));
1577 CHECK(check_enum(pctx, obj, type->of));
1578 *ret = obj;
1579 return (ISC_R_SUCCESS);
1580 cleanup:
1581 CLEANUP_OBJ(obj);
1582 return (result);
1583 }
1584
1585 void
cfg_doc_enum(cfg_printer_t * pctx,const cfg_type_t * type)1586 cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
1587 const char *const *p;
1588
1589 REQUIRE(pctx != NULL);
1590 REQUIRE(type != NULL);
1591
1592 cfg_print_cstr(pctx, "( ");
1593 for (p = type->of; *p != NULL; p++) {
1594 cfg_print_cstr(pctx, *p);
1595 if (p[1] != NULL) {
1596 cfg_print_cstr(pctx, " | ");
1597 }
1598 }
1599 cfg_print_cstr(pctx, " )");
1600 }
1601
1602 isc_result_t
cfg_parse_enum_or_other(cfg_parser_t * pctx,const cfg_type_t * enumtype,const cfg_type_t * othertype,cfg_obj_t ** ret)1603 cfg_parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
1604 const cfg_type_t *othertype, cfg_obj_t **ret) {
1605 isc_result_t result;
1606 CHECK(cfg_peektoken(pctx, 0));
1607 if (pctx->token.type == isc_tokentype_string &&
1608 cfg_is_enum(TOKEN_STRING(pctx), enumtype->of))
1609 {
1610 CHECK(cfg_parse_enum(pctx, enumtype, ret));
1611 } else {
1612 CHECK(cfg_parse_obj(pctx, othertype, ret));
1613 }
1614 cleanup:
1615 return (result);
1616 }
1617
1618 void
cfg_doc_enum_or_other(cfg_printer_t * pctx,const cfg_type_t * enumtype,const cfg_type_t * othertype)1619 cfg_doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *enumtype,
1620 const cfg_type_t *othertype) {
1621 const char *const *p;
1622 bool first = true;
1623
1624 /*
1625 * If othertype is cfg_type_void, it means that enumtype is
1626 * optional.
1627 */
1628
1629 if (othertype == &cfg_type_void) {
1630 cfg_print_cstr(pctx, "[ ");
1631 }
1632 cfg_print_cstr(pctx, "( ");
1633 for (p = enumtype->of; *p != NULL; p++) {
1634 if (!first) {
1635 cfg_print_cstr(pctx, " | ");
1636 }
1637 first = false;
1638 cfg_print_cstr(pctx, *p);
1639 }
1640 if (othertype != &cfg_type_void) {
1641 if (!first) {
1642 cfg_print_cstr(pctx, " | ");
1643 }
1644 cfg_doc_terminal(pctx, othertype);
1645 }
1646 cfg_print_cstr(pctx, " )");
1647 if (othertype == &cfg_type_void) {
1648 cfg_print_cstr(pctx, " ]");
1649 }
1650 }
1651
1652 void
cfg_print_ustring(cfg_printer_t * pctx,const cfg_obj_t * obj)1653 cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1654 REQUIRE(pctx != NULL);
1655 REQUIRE(obj != NULL);
1656
1657 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
1658 }
1659
1660 static void
print_qstring(cfg_printer_t * pctx,const cfg_obj_t * obj)1661 print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1662 cfg_print_cstr(pctx, "\"");
1663 for (size_t i = 0; i < obj->value.string.length; i++) {
1664 if (obj->value.string.base[i] == '"') {
1665 cfg_print_cstr(pctx, "\\");
1666 }
1667 cfg_print_chars(pctx, &obj->value.string.base[i], 1);
1668 }
1669 cfg_print_cstr(pctx, "\"");
1670 }
1671
1672 static void
print_sstring(cfg_printer_t * pctx,const cfg_obj_t * obj)1673 print_sstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1674 cfg_print_cstr(pctx, "\"");
1675 if ((pctx->flags & CFG_PRINTER_XKEY) != 0) {
1676 unsigned int len = obj->value.string.length;
1677 while (len-- > 0) {
1678 cfg_print_cstr(pctx, "?");
1679 }
1680 } else {
1681 cfg_print_ustring(pctx, obj);
1682 }
1683 cfg_print_cstr(pctx, "\"");
1684 }
1685
1686 static void
free_string(cfg_parser_t * pctx,cfg_obj_t * obj)1687 free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
1688 isc_mem_put(pctx->mctx, obj->value.string.base,
1689 obj->value.string.length + 1);
1690 }
1691
1692 bool
cfg_obj_isstring(const cfg_obj_t * obj)1693 cfg_obj_isstring(const cfg_obj_t *obj) {
1694 REQUIRE(obj != NULL);
1695 return (obj->type->rep == &cfg_rep_string);
1696 }
1697
1698 const char *
cfg_obj_asstring(const cfg_obj_t * obj)1699 cfg_obj_asstring(const cfg_obj_t *obj) {
1700 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
1701 return (obj->value.string.base);
1702 }
1703
1704 /* Quoted string only */
1705 cfg_type_t cfg_type_qstring = { "quoted_string", cfg_parse_qstring,
1706 print_qstring, cfg_doc_terminal,
1707 &cfg_rep_string, NULL };
1708
1709 /* Unquoted string only */
1710 cfg_type_t cfg_type_ustring = { "string", parse_ustring,
1711 cfg_print_ustring, cfg_doc_terminal,
1712 &cfg_rep_string, NULL };
1713
1714 /* Any string (quoted or unquoted); printed with quotes */
1715 cfg_type_t cfg_type_astring = { "string", cfg_parse_astring,
1716 print_qstring, cfg_doc_terminal,
1717 &cfg_rep_string, NULL };
1718
1719 /*
1720 * Any string (quoted or unquoted); printed with quotes.
1721 * If CFG_PRINTER_XKEY is set when printing the string will be '?' out.
1722 */
1723 cfg_type_t cfg_type_sstring = { "string", cfg_parse_sstring,
1724 print_sstring, cfg_doc_terminal,
1725 &cfg_rep_string, NULL };
1726
1727 /*
1728 * Text enclosed in brackets. Used to pass a block of configuration
1729 * text to dynamic library or external application. Checked for
1730 * bracket balance, but not otherwise parsed.
1731 */
1732 cfg_type_t cfg_type_bracketed_text = { "bracketed_text", parse_btext,
1733 print_btext, doc_btext,
1734 &cfg_rep_string, NULL };
1735
1736 #if defined(HAVE_GEOIP2)
1737 /*
1738 * "geoip" ACL element:
1739 * geoip [ db <database> ] search-type <string>
1740 */
1741 static const char *geoiptype_enums[] = {
1742 "area", "areacode", "asnum", "city", "continent",
1743 "country", "country3", "countryname", "domain", "isp",
1744 "metro", "metrocode", "netspeed", "org", "postal",
1745 "postalcode", "region", "regionname", "timezone", "tz",
1746 NULL
1747 };
1748 static cfg_type_t cfg_type_geoiptype = { "geoiptype", cfg_parse_enum,
1749 cfg_print_ustring, cfg_doc_enum,
1750 &cfg_rep_string, &geoiptype_enums };
1751
1752 static cfg_tuplefielddef_t geoip_fields[] = {
1753 { "negated", &cfg_type_void, 0 },
1754 { "db", &cfg_type_astring, 0 },
1755 { "subtype", &cfg_type_geoiptype, 0 },
1756 { "search", &cfg_type_astring, 0 },
1757 { NULL, NULL, 0 }
1758 };
1759
1760 static cfg_type_t cfg_type_geoip = { "geoip", parse_geoip, print_geoip,
1761 doc_geoip, &cfg_rep_tuple, geoip_fields };
1762
1763 static isc_result_t
parse_geoip(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1764 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1765 isc_result_t result;
1766 cfg_obj_t *obj = NULL;
1767 const cfg_tuplefielddef_t *fields = type->of;
1768
1769 CHECK(cfg_create_tuple(pctx, type, &obj));
1770 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[0]));
1771
1772 /* Parse the optional "db" field. */
1773 CHECK(cfg_peektoken(pctx, 0));
1774 if (pctx->token.type == isc_tokentype_string) {
1775 CHECK(cfg_gettoken(pctx, 0));
1776 if (strcasecmp(TOKEN_STRING(pctx), "db") == 0 &&
1777 obj->value.tuple[1] == NULL) {
1778 CHECK(cfg_parse_obj(pctx, fields[1].type,
1779 &obj->value.tuple[1]));
1780 } else {
1781 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
1782 cfg_ungettoken(pctx);
1783 }
1784 }
1785
1786 CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2]));
1787 CHECK(cfg_parse_obj(pctx, fields[3].type, &obj->value.tuple[3]));
1788
1789 *ret = obj;
1790 return (ISC_R_SUCCESS);
1791
1792 cleanup:
1793 CLEANUP_OBJ(obj);
1794 return (result);
1795 }
1796
1797 static void
print_geoip(cfg_printer_t * pctx,const cfg_obj_t * obj)1798 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1799 if (obj->value.tuple[1]->type->print != cfg_print_void) {
1800 cfg_print_cstr(pctx, " db ");
1801 cfg_print_obj(pctx, obj->value.tuple[1]);
1802 }
1803 cfg_print_obj(pctx, obj->value.tuple[2]);
1804 cfg_print_obj(pctx, obj->value.tuple[3]);
1805 }
1806
1807 static void
doc_geoip(cfg_printer_t * pctx,const cfg_type_t * type)1808 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) {
1809 UNUSED(type);
1810 cfg_print_cstr(pctx, "[ db ");
1811 cfg_doc_obj(pctx, &cfg_type_astring);
1812 cfg_print_cstr(pctx, " ]");
1813 cfg_print_cstr(pctx, " ");
1814 cfg_doc_enum(pctx, &cfg_type_geoiptype);
1815 cfg_print_cstr(pctx, " ");
1816 cfg_doc_obj(pctx, &cfg_type_astring);
1817 }
1818 #endif /* HAVE_GEOIP2 */
1819
1820 static cfg_type_t cfg_type_addrmatchelt;
1821 static cfg_type_t cfg_type_negated;
1822
1823 static isc_result_t
parse_addrmatchelt(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1824 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type,
1825 cfg_obj_t **ret) {
1826 isc_result_t result;
1827 UNUSED(type);
1828
1829 CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1830
1831 if (pctx->token.type == isc_tokentype_string ||
1832 pctx->token.type == isc_tokentype_qstring)
1833 {
1834 if (pctx->token.type == isc_tokentype_string &&
1835 (strcasecmp(TOKEN_STRING(pctx), "key") == 0))
1836 {
1837 CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
1838 } else if (pctx->token.type == isc_tokentype_string &&
1839 (strcasecmp(TOKEN_STRING(pctx), "geoip") == 0))
1840 {
1841 #if defined(HAVE_GEOIP2)
1842 CHECK(cfg_gettoken(pctx, 0));
1843 CHECK(cfg_parse_obj(pctx, &cfg_type_geoip, ret));
1844 #else /* if defined(HAVE_GEOIP2) */
1845 cfg_parser_error(pctx, CFG_LOG_NEAR,
1846 "'geoip' "
1847 "not supported in this build");
1848 return (ISC_R_UNEXPECTEDTOKEN);
1849 #endif /* if defined(HAVE_GEOIP2) */
1850 } else {
1851 if (cfg_lookingat_netaddr(
1852 pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
1853 CFG_ADDR_V6OK))
1854 {
1855 CHECK(cfg_parse_netprefix(pctx, NULL, ret));
1856 } else {
1857 CHECK(cfg_parse_astring(pctx, NULL, ret));
1858 }
1859 }
1860 } else if (pctx->token.type == isc_tokentype_special) {
1861 if (pctx->token.value.as_char == '{') {
1862 /* Nested match list. */
1863 CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml,
1864 ret));
1865 } else if (pctx->token.value.as_char == '!') {
1866 CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
1867 CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
1868 } else {
1869 goto bad;
1870 }
1871 } else {
1872 bad:
1873 cfg_parser_error(pctx, CFG_LOG_NEAR,
1874 "expected IP match list element");
1875 return (ISC_R_UNEXPECTEDTOKEN);
1876 }
1877 cleanup:
1878 return (result);
1879 }
1880
1881 /*%
1882 * A negated address match list element (like "! 10.0.0.1").
1883 * Somewhat sneakily, the caller is expected to parse the
1884 * "!", but not to print it.
1885 */
1886 static cfg_tuplefielddef_t negated_fields[] = {
1887 { "negated", &cfg_type_addrmatchelt, 0 }, { NULL, NULL, 0 }
1888 };
1889
1890 static void
print_negated(cfg_printer_t * pctx,const cfg_obj_t * obj)1891 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1892 cfg_print_cstr(pctx, "!");
1893 cfg_print_tuple(pctx, obj);
1894 }
1895
1896 static cfg_type_t cfg_type_negated = { "negated", cfg_parse_tuple,
1897 print_negated, NULL,
1898 &cfg_rep_tuple, &negated_fields };
1899
1900 /*% An address match list element */
1901
1902 static cfg_type_t cfg_type_addrmatchelt = { "address_match_element",
1903 parse_addrmatchelt,
1904 NULL,
1905 cfg_doc_terminal,
1906 NULL,
1907 NULL };
1908
1909 /*%
1910 * A bracketed address match list
1911 */
1912 cfg_type_t cfg_type_bracketed_aml = { "bracketed_aml",
1913 cfg_parse_bracketed_list,
1914 cfg_print_bracketed_list,
1915 cfg_doc_bracketed_list,
1916 &cfg_rep_list,
1917 &cfg_type_addrmatchelt };
1918
1919 /*
1920 * Optional bracketed text
1921 */
1922 static isc_result_t
parse_optional_btext(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1923 parse_optional_btext(cfg_parser_t *pctx, const cfg_type_t *type,
1924 cfg_obj_t **ret) {
1925 isc_result_t result;
1926
1927 UNUSED(type);
1928
1929 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_BTEXT));
1930 if (pctx->token.type == isc_tokentype_btext) {
1931 CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_text, ret));
1932 } else {
1933 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1934 }
1935 cleanup:
1936 return (result);
1937 }
1938
1939 static void
print_optional_btext(cfg_printer_t * pctx,const cfg_obj_t * obj)1940 print_optional_btext(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1941 if (obj->type == &cfg_type_void) {
1942 return;
1943 }
1944
1945 pctx->indent++;
1946 cfg_print_cstr(pctx, "{");
1947 cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
1948 print_close(pctx);
1949 }
1950
1951 static void
doc_optional_btext(cfg_printer_t * pctx,const cfg_type_t * type)1952 doc_optional_btext(cfg_printer_t *pctx, const cfg_type_t *type) {
1953 UNUSED(type);
1954
1955 cfg_print_cstr(pctx, "[ { <unspecified-text> } ]");
1956 }
1957
1958 cfg_type_t cfg_type_optional_bracketed_text = { "optional_btext",
1959 parse_optional_btext,
1960 print_optional_btext,
1961 doc_optional_btext,
1962 NULL,
1963 NULL };
1964
1965 /*
1966 * Booleans
1967 */
1968
1969 bool
cfg_obj_isboolean(const cfg_obj_t * obj)1970 cfg_obj_isboolean(const cfg_obj_t *obj) {
1971 REQUIRE(obj != NULL);
1972 return (obj->type->rep == &cfg_rep_boolean);
1973 }
1974
1975 bool
cfg_obj_asboolean(const cfg_obj_t * obj)1976 cfg_obj_asboolean(const cfg_obj_t *obj) {
1977 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
1978 return (obj->value.boolean);
1979 }
1980
1981 isc_result_t
cfg_parse_boolean(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)1982 cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1983 isc_result_t result;
1984 bool value;
1985 cfg_obj_t *obj = NULL;
1986
1987 REQUIRE(pctx != NULL);
1988 REQUIRE(ret != NULL && *ret == NULL);
1989
1990 UNUSED(type);
1991
1992 result = cfg_gettoken(pctx, 0);
1993 if (result != ISC_R_SUCCESS) {
1994 return (result);
1995 }
1996
1997 if (pctx->token.type != isc_tokentype_string) {
1998 goto bad_boolean;
1999 }
2000
2001 if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
2002 (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
2003 (strcmp(TOKEN_STRING(pctx), "1") == 0))
2004 {
2005 value = true;
2006 } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
2007 (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
2008 (strcmp(TOKEN_STRING(pctx), "0") == 0))
2009 {
2010 value = false;
2011 } else {
2012 goto bad_boolean;
2013 }
2014
2015 CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
2016 obj->value.boolean = value;
2017 *ret = obj;
2018 return (result);
2019
2020 bad_boolean:
2021 cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
2022 return (ISC_R_UNEXPECTEDTOKEN);
2023
2024 cleanup:
2025 return (result);
2026 }
2027
2028 void
cfg_print_boolean(cfg_printer_t * pctx,const cfg_obj_t * obj)2029 cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2030 REQUIRE(pctx != NULL);
2031 REQUIRE(obj != NULL);
2032
2033 if (obj->value.boolean) {
2034 cfg_print_cstr(pctx, "yes");
2035 } else {
2036 cfg_print_cstr(pctx, "no");
2037 }
2038 }
2039
2040 cfg_type_t cfg_type_boolean = { "boolean", cfg_parse_boolean,
2041 cfg_print_boolean, cfg_doc_terminal,
2042 &cfg_rep_boolean, NULL };
2043
2044 /*
2045 * Lists.
2046 */
2047
2048 isc_result_t
cfg_create_list(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** obj)2049 cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
2050 isc_result_t result;
2051
2052 REQUIRE(pctx != NULL);
2053 REQUIRE(type != NULL);
2054 REQUIRE(obj != NULL && *obj == NULL);
2055
2056 CHECK(cfg_create_obj(pctx, type, obj));
2057 ISC_LIST_INIT((*obj)->value.list);
2058 cleanup:
2059 return (result);
2060 }
2061
2062 static isc_result_t
create_listelt(cfg_parser_t * pctx,cfg_listelt_t ** eltp)2063 create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
2064 cfg_listelt_t *elt;
2065
2066 elt = isc_mem_get(pctx->mctx, sizeof(*elt));
2067 elt->obj = NULL;
2068 ISC_LINK_INIT(elt, link);
2069 *eltp = elt;
2070 return (ISC_R_SUCCESS);
2071 }
2072
2073 static void
free_listelt(cfg_parser_t * pctx,cfg_listelt_t * elt)2074 free_listelt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
2075 if (elt->obj != NULL) {
2076 cfg_obj_destroy(pctx, &elt->obj);
2077 }
2078 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
2079 }
2080
2081 static void
free_list(cfg_parser_t * pctx,cfg_obj_t * obj)2082 free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
2083 cfg_listelt_t *elt, *next;
2084 for (elt = ISC_LIST_HEAD(obj->value.list); elt != NULL; elt = next) {
2085 next = ISC_LIST_NEXT(elt, link);
2086 free_listelt(pctx, elt);
2087 }
2088 }
2089
2090 isc_result_t
cfg_parse_listelt(cfg_parser_t * pctx,const cfg_type_t * elttype,cfg_listelt_t ** ret)2091 cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
2092 cfg_listelt_t **ret) {
2093 isc_result_t result;
2094 cfg_listelt_t *elt = NULL;
2095 cfg_obj_t *value = NULL;
2096
2097 REQUIRE(pctx != NULL);
2098 REQUIRE(elttype != NULL);
2099 REQUIRE(ret != NULL && *ret == NULL);
2100
2101 CHECK(create_listelt(pctx, &elt));
2102
2103 result = cfg_parse_obj(pctx, elttype, &value);
2104 if (result != ISC_R_SUCCESS) {
2105 goto cleanup;
2106 }
2107
2108 elt->obj = value;
2109
2110 *ret = elt;
2111 return (ISC_R_SUCCESS);
2112
2113 cleanup:
2114 isc_mem_put(pctx->mctx, elt, sizeof(*elt));
2115 return (result);
2116 }
2117
2118 /*
2119 * Parse a homogeneous list whose elements are of type 'elttype'
2120 * and where each element is terminated by a semicolon.
2121 */
2122 static isc_result_t
parse_list(cfg_parser_t * pctx,const cfg_type_t * listtype,cfg_obj_t ** ret)2123 parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret) {
2124 cfg_obj_t *listobj = NULL;
2125 const cfg_type_t *listof = listtype->of;
2126 isc_result_t result;
2127 cfg_listelt_t *elt = NULL;
2128
2129 CHECK(cfg_create_list(pctx, listtype, &listobj));
2130
2131 for (;;) {
2132 CHECK(cfg_peektoken(pctx, 0));
2133 if (pctx->token.type == isc_tokentype_special &&
2134 pctx->token.value.as_char == /*{*/ '}')
2135 {
2136 break;
2137 }
2138 CHECK(cfg_parse_listelt(pctx, listof, &elt));
2139 CHECK(parse_semicolon(pctx));
2140 ISC_LIST_APPEND(listobj->value.list, elt, link);
2141 elt = NULL;
2142 }
2143 *ret = listobj;
2144 return (ISC_R_SUCCESS);
2145
2146 cleanup:
2147 if (elt != NULL) {
2148 free_listelt(pctx, elt);
2149 }
2150 CLEANUP_OBJ(listobj);
2151 return (result);
2152 }
2153
2154 static void
print_list(cfg_printer_t * pctx,const cfg_obj_t * obj)2155 print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2156 const cfg_list_t *list = &obj->value.list;
2157 const cfg_listelt_t *elt;
2158
2159 for (elt = ISC_LIST_HEAD(*list); elt != NULL;
2160 elt = ISC_LIST_NEXT(elt, link)) {
2161 if ((pctx->flags & CFG_PRINTER_ONELINE) != 0) {
2162 cfg_print_obj(pctx, elt->obj);
2163 cfg_print_cstr(pctx, "; ");
2164 } else {
2165 cfg_print_indent(pctx);
2166 cfg_print_obj(pctx, elt->obj);
2167 cfg_print_cstr(pctx, ";\n");
2168 }
2169 }
2170 }
2171
2172 isc_result_t
cfg_parse_bracketed_list(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2173 cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
2174 cfg_obj_t **ret) {
2175 isc_result_t result;
2176
2177 REQUIRE(pctx != NULL);
2178 REQUIRE(type != NULL);
2179 REQUIRE(ret != NULL && *ret == NULL);
2180
2181 CHECK(cfg_parse_special(pctx, '{'));
2182 CHECK(parse_list(pctx, type, ret));
2183 CHECK(cfg_parse_special(pctx, '}'));
2184 cleanup:
2185 return (result);
2186 }
2187
2188 void
cfg_print_bracketed_list(cfg_printer_t * pctx,const cfg_obj_t * obj)2189 cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2190 REQUIRE(pctx != NULL);
2191 REQUIRE(obj != NULL);
2192
2193 print_open(pctx);
2194 print_list(pctx, obj);
2195 print_close(pctx);
2196 }
2197
2198 void
cfg_doc_bracketed_list(cfg_printer_t * pctx,const cfg_type_t * type)2199 cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
2200 REQUIRE(pctx != NULL);
2201 REQUIRE(type != NULL);
2202
2203 cfg_print_cstr(pctx, "{ ");
2204 cfg_doc_obj(pctx, type->of);
2205 cfg_print_cstr(pctx, "; ... }");
2206 }
2207
2208 /*
2209 * Parse a homogeneous list whose elements are of type 'elttype'
2210 * and where elements are separated by space. The list ends
2211 * before the first semicolon.
2212 */
2213 isc_result_t
cfg_parse_spacelist(cfg_parser_t * pctx,const cfg_type_t * listtype,cfg_obj_t ** ret)2214 cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
2215 cfg_obj_t **ret) {
2216 cfg_obj_t *listobj = NULL;
2217 const cfg_type_t *listof;
2218 isc_result_t result;
2219
2220 REQUIRE(pctx != NULL);
2221 REQUIRE(listtype != NULL);
2222 REQUIRE(ret != NULL && *ret == NULL);
2223
2224 listof = listtype->of;
2225
2226 CHECK(cfg_create_list(pctx, listtype, &listobj));
2227
2228 for (;;) {
2229 cfg_listelt_t *elt = NULL;
2230
2231 CHECK(cfg_peektoken(pctx, 0));
2232 if (pctx->token.type == isc_tokentype_special &&
2233 pctx->token.value.as_char == ';')
2234 {
2235 break;
2236 }
2237 CHECK(cfg_parse_listelt(pctx, listof, &elt));
2238 ISC_LIST_APPEND(listobj->value.list, elt, link);
2239 }
2240 *ret = listobj;
2241 return (ISC_R_SUCCESS);
2242
2243 cleanup:
2244 CLEANUP_OBJ(listobj);
2245 return (result);
2246 }
2247
2248 void
cfg_print_spacelist(cfg_printer_t * pctx,const cfg_obj_t * obj)2249 cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2250 const cfg_list_t *list = NULL;
2251 const cfg_listelt_t *elt = NULL;
2252
2253 REQUIRE(pctx != NULL);
2254 REQUIRE(obj != NULL);
2255
2256 list = &obj->value.list;
2257
2258 for (elt = ISC_LIST_HEAD(*list); elt != NULL;
2259 elt = ISC_LIST_NEXT(elt, link)) {
2260 cfg_print_obj(pctx, elt->obj);
2261 if (ISC_LIST_NEXT(elt, link) != NULL) {
2262 cfg_print_cstr(pctx, " ");
2263 }
2264 }
2265 }
2266
2267 bool
cfg_obj_islist(const cfg_obj_t * obj)2268 cfg_obj_islist(const cfg_obj_t *obj) {
2269 REQUIRE(obj != NULL);
2270 return (obj->type->rep == &cfg_rep_list);
2271 }
2272
2273 const cfg_listelt_t *
cfg_list_first(const cfg_obj_t * obj)2274 cfg_list_first(const cfg_obj_t *obj) {
2275 REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
2276 if (obj == NULL) {
2277 return (NULL);
2278 }
2279 return (ISC_LIST_HEAD(obj->value.list));
2280 }
2281
2282 const cfg_listelt_t *
cfg_list_next(const cfg_listelt_t * elt)2283 cfg_list_next(const cfg_listelt_t *elt) {
2284 REQUIRE(elt != NULL);
2285 return (ISC_LIST_NEXT(elt, link));
2286 }
2287
2288 /*
2289 * Return the length of a list object. If obj is NULL or is not
2290 * a list, return 0.
2291 */
2292 unsigned int
cfg_list_length(const cfg_obj_t * obj,bool recurse)2293 cfg_list_length(const cfg_obj_t *obj, bool recurse) {
2294 const cfg_listelt_t *elt;
2295 unsigned int count = 0;
2296
2297 if (obj == NULL || !cfg_obj_islist(obj)) {
2298 return (0U);
2299 }
2300 for (elt = cfg_list_first(obj); elt != NULL; elt = cfg_list_next(elt)) {
2301 if (recurse && cfg_obj_islist(elt->obj)) {
2302 count += cfg_list_length(elt->obj, recurse);
2303 } else {
2304 count++;
2305 }
2306 }
2307 return (count);
2308 }
2309
2310 cfg_obj_t *
cfg_listelt_value(const cfg_listelt_t * elt)2311 cfg_listelt_value(const cfg_listelt_t *elt) {
2312 REQUIRE(elt != NULL);
2313 return (elt->obj);
2314 }
2315
2316 /*
2317 * Maps.
2318 */
2319
2320 /*
2321 * Parse a map body. That's something like
2322 *
2323 * "foo 1; bar { glub; }; zap true; zap false;"
2324 *
2325 * i.e., a sequence of option names followed by values and
2326 * terminated by semicolons. Used for the top level of
2327 * the named.conf syntax, as well as for the body of the
2328 * options, view, zone, and other statements.
2329 */
2330 isc_result_t
cfg_parse_mapbody(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2331 cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2332 const cfg_clausedef_t *const *clausesets;
2333 isc_result_t result;
2334 const cfg_clausedef_t *const *clauseset;
2335 const cfg_clausedef_t *clause;
2336 cfg_obj_t *value = NULL;
2337 cfg_obj_t *obj = NULL;
2338 cfg_obj_t *eltobj = NULL;
2339 cfg_obj_t *includename = NULL;
2340 isc_symvalue_t symval;
2341 cfg_list_t *list = NULL;
2342
2343 REQUIRE(pctx != NULL);
2344 REQUIRE(type != NULL);
2345 REQUIRE(ret != NULL && *ret == NULL);
2346
2347 clausesets = type->of;
2348
2349 CHECK(create_map(pctx, type, &obj));
2350
2351 obj->value.map.clausesets = clausesets;
2352
2353 for (;;) {
2354 cfg_listelt_t *elt;
2355
2356 redo:
2357 /*
2358 * Parse the option name and see if it is known.
2359 */
2360 CHECK(cfg_gettoken(pctx, 0));
2361
2362 if (pctx->token.type != isc_tokentype_string) {
2363 cfg_ungettoken(pctx);
2364 break;
2365 }
2366
2367 /*
2368 * We accept "include" statements wherever a map body
2369 * clause can occur.
2370 */
2371 if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
2372 /*
2373 * Turn the file name into a temporary configuration
2374 * object just so that it is not overwritten by the
2375 * semicolon token.
2376 */
2377 CHECK(cfg_parse_obj(pctx, &cfg_type_qstring,
2378 &includename));
2379 CHECK(parse_semicolon(pctx));
2380
2381 /* Allow include to specify a pattern that follows
2382 * the same rules as the shell e.g "/path/zone*.conf" */
2383 glob_t glob_obj;
2384 CHECK(isc_glob(includename->value.string.base,
2385 &glob_obj));
2386 cfg_obj_destroy(pctx, &includename);
2387
2388 for (size_t i = 0; i < glob_obj.gl_pathc; ++i) {
2389 CHECK(parser_openfile(pctx,
2390 glob_obj.gl_pathv[i]));
2391 }
2392
2393 isc_globfree(&glob_obj);
2394
2395 goto redo;
2396 }
2397
2398 clause = NULL;
2399 for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
2400 for (clause = *clauseset; clause->name != NULL;
2401 clause++) {
2402 if (strcasecmp(TOKEN_STRING(pctx),
2403 clause->name) == 0) {
2404 goto done;
2405 }
2406 }
2407 }
2408 done:
2409 if (clause == NULL || clause->name == NULL) {
2410 cfg_parser_error(pctx, CFG_LOG_NOPREP,
2411 "unknown option");
2412 /*
2413 * Try to recover by parsing this option as an unknown
2414 * option and discarding it.
2415 */
2416 CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported,
2417 &eltobj));
2418 cfg_obj_destroy(pctx, &eltobj);
2419 CHECK(parse_semicolon(pctx));
2420 continue;
2421 }
2422
2423 /* Clause is known. */
2424
2425 /* Issue fatal errors if appropriate */
2426 if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) {
2427 cfg_parser_error(pctx, 0,
2428 "option '%s' no longer exists",
2429 clause->name);
2430 CHECK(ISC_R_FAILURE);
2431 }
2432 if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
2433 cfg_parser_error(pctx, 0,
2434 "option '%s' was not "
2435 "enabled at compile time",
2436 clause->name);
2437 CHECK(ISC_R_FAILURE);
2438 }
2439
2440 /* Issue warnings if appropriate */
2441 if ((pctx->flags & CFG_PCTX_NODEPRECATED) == 0 &&
2442 (clause->flags & CFG_CLAUSEFLAG_DEPRECATED) != 0)
2443 {
2444 cfg_parser_warning(pctx, 0, "option '%s' is deprecated",
2445 clause->name);
2446 }
2447 if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) {
2448 cfg_parser_warning(pctx, 0,
2449 "option '%s' is obsolete and "
2450 "should be removed ",
2451 clause->name);
2452 }
2453 if ((clause->flags & CFG_CLAUSEFLAG_EXPERIMENTAL) != 0) {
2454 cfg_parser_warning(pctx, 0,
2455 "option '%s' is experimental and "
2456 "subject to change in the future",
2457 clause->name);
2458 }
2459
2460 /* See if the clause already has a value; if not create one. */
2461 result = isc_symtab_lookup(obj->value.map.symtab, clause->name,
2462 0, &symval);
2463
2464 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
2465 /* Multivalued clause */
2466 cfg_obj_t *listobj = NULL;
2467 if (result == ISC_R_NOTFOUND) {
2468 CHECK(cfg_create_list(pctx,
2469 &cfg_type_implicitlist,
2470 &listobj));
2471 symval.as_pointer = listobj;
2472 result = isc_symtab_define(
2473 obj->value.map.symtab, clause->name, 1,
2474 symval, isc_symexists_reject);
2475 if (result != ISC_R_SUCCESS) {
2476 cfg_parser_error(pctx, CFG_LOG_NEAR,
2477 "isc_symtab_define(%s)"
2478 " "
2479 "failed",
2480 clause->name);
2481 isc_mem_put(pctx->mctx, list,
2482 sizeof(cfg_list_t));
2483 goto cleanup;
2484 }
2485 } else {
2486 INSIST(result == ISC_R_SUCCESS);
2487 listobj = symval.as_pointer;
2488 }
2489
2490 elt = NULL;
2491 CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
2492 CHECK(parse_semicolon(pctx));
2493
2494 ISC_LIST_APPEND(listobj->value.list, elt, link);
2495 } else {
2496 /* Single-valued clause */
2497 if (result == ISC_R_NOTFOUND) {
2498 bool callback = ((clause->flags &
2499 CFG_CLAUSEFLAG_CALLBACK) !=
2500 0);
2501 CHECK(parse_symtab_elt(
2502 pctx, clause->name, clause->type,
2503 obj->value.map.symtab, callback));
2504 CHECK(parse_semicolon(pctx));
2505 } else if (result == ISC_R_SUCCESS) {
2506 cfg_parser_error(pctx, CFG_LOG_NEAR,
2507 "'%s' redefined",
2508 clause->name);
2509 result = ISC_R_EXISTS;
2510 goto cleanup;
2511 } else {
2512 cfg_parser_error(pctx, CFG_LOG_NEAR,
2513 "isc_symtab_define() failed");
2514 goto cleanup;
2515 }
2516 }
2517 }
2518
2519 *ret = obj;
2520 return (ISC_R_SUCCESS);
2521
2522 cleanup:
2523 CLEANUP_OBJ(value);
2524 CLEANUP_OBJ(obj);
2525 CLEANUP_OBJ(eltobj);
2526 CLEANUP_OBJ(includename);
2527 return (result);
2528 }
2529
2530 static isc_result_t
parse_symtab_elt(cfg_parser_t * pctx,const char * name,cfg_type_t * elttype,isc_symtab_t * symtab,bool callback)2531 parse_symtab_elt(cfg_parser_t *pctx, const char *name, cfg_type_t *elttype,
2532 isc_symtab_t *symtab, bool callback) {
2533 isc_result_t result;
2534 cfg_obj_t *obj = NULL;
2535 isc_symvalue_t symval;
2536
2537 CHECK(cfg_parse_obj(pctx, elttype, &obj));
2538
2539 if (callback && pctx->callback != NULL) {
2540 CHECK(pctx->callback(name, obj, pctx->callbackarg));
2541 }
2542
2543 symval.as_pointer = obj;
2544 CHECK(isc_symtab_define(symtab, name, 1, symval, isc_symexists_reject));
2545 return (ISC_R_SUCCESS);
2546
2547 cleanup:
2548 CLEANUP_OBJ(obj);
2549 return (result);
2550 }
2551
2552 /*
2553 * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
2554 */
2555 isc_result_t
cfg_parse_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2556 cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2557 isc_result_t result;
2558
2559 REQUIRE(pctx != NULL);
2560 REQUIRE(type != NULL);
2561 REQUIRE(ret != NULL && *ret == NULL);
2562
2563 CHECK(cfg_parse_special(pctx, '{'));
2564 CHECK(cfg_parse_mapbody(pctx, type, ret));
2565 CHECK(cfg_parse_special(pctx, '}'));
2566 cleanup:
2567 return (result);
2568 }
2569
2570 /*
2571 * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
2572 */
2573 static isc_result_t
parse_any_named_map(cfg_parser_t * pctx,cfg_type_t * nametype,const cfg_type_t * type,cfg_obj_t ** ret)2574 parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype,
2575 const cfg_type_t *type, cfg_obj_t **ret) {
2576 isc_result_t result;
2577 cfg_obj_t *idobj = NULL;
2578 cfg_obj_t *mapobj = NULL;
2579
2580 REQUIRE(pctx != NULL);
2581 REQUIRE(nametype != NULL);
2582 REQUIRE(type != NULL);
2583 REQUIRE(ret != NULL && *ret == NULL);
2584
2585 CHECK(cfg_parse_obj(pctx, nametype, &idobj));
2586 CHECK(cfg_parse_map(pctx, type, &mapobj));
2587 mapobj->value.map.id = idobj;
2588 *ret = mapobj;
2589 return (result);
2590 cleanup:
2591 CLEANUP_OBJ(idobj);
2592 CLEANUP_OBJ(mapobj);
2593 return (result);
2594 }
2595
2596 /*
2597 * Parse a map identified by a string name. E.g., "name { foo 1; }".
2598 * Used for the "key" and "channel" statements.
2599 */
2600 isc_result_t
cfg_parse_named_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2601 cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type,
2602 cfg_obj_t **ret) {
2603 return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
2604 }
2605
2606 /*
2607 * Parse a map identified by a network address.
2608 * Used to be used for the "server" statement.
2609 */
2610 isc_result_t
cfg_parse_addressed_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2611 cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type,
2612 cfg_obj_t **ret) {
2613 return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
2614 }
2615
2616 /*
2617 * Parse a map identified by a network prefix.
2618 * Used for the "server" statement.
2619 */
2620 isc_result_t
cfg_parse_netprefix_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2621 cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type,
2622 cfg_obj_t **ret) {
2623 return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
2624 }
2625
2626 static void
print_symval(cfg_printer_t * pctx,const char * name,cfg_obj_t * obj)2627 print_symval(cfg_printer_t *pctx, const char *name, cfg_obj_t *obj) {
2628 if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
2629 cfg_print_indent(pctx);
2630 }
2631
2632 cfg_print_cstr(pctx, name);
2633 cfg_print_cstr(pctx, " ");
2634 cfg_print_obj(pctx, obj);
2635
2636 if ((pctx->flags & CFG_PRINTER_ONELINE) == 0) {
2637 cfg_print_cstr(pctx, ";\n");
2638 } else {
2639 cfg_print_cstr(pctx, "; ");
2640 }
2641 }
2642
2643 void
cfg_print_mapbody(cfg_printer_t * pctx,const cfg_obj_t * obj)2644 cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2645 const cfg_clausedef_t *const *clauseset;
2646
2647 REQUIRE(pctx != NULL);
2648 REQUIRE(obj != NULL);
2649
2650 for (clauseset = obj->value.map.clausesets; *clauseset != NULL;
2651 clauseset++) {
2652 isc_symvalue_t symval;
2653 const cfg_clausedef_t *clause;
2654
2655 for (clause = *clauseset; clause->name != NULL; clause++) {
2656 isc_result_t result;
2657 result = isc_symtab_lookup(obj->value.map.symtab,
2658 clause->name, 0, &symval);
2659 if (result == ISC_R_SUCCESS) {
2660 cfg_obj_t *symobj = symval.as_pointer;
2661 if (symobj->type == &cfg_type_implicitlist) {
2662 /* Multivalued. */
2663 cfg_list_t *list = &symobj->value.list;
2664 cfg_listelt_t *elt;
2665 for (elt = ISC_LIST_HEAD(*list);
2666 elt != NULL;
2667 elt = ISC_LIST_NEXT(elt, link)) {
2668 print_symval(pctx, clause->name,
2669 elt->obj);
2670 }
2671 } else {
2672 /* Single-valued. */
2673 print_symval(pctx, clause->name,
2674 symobj);
2675 }
2676 } else if (result == ISC_R_NOTFOUND) {
2677 /* do nothing */
2678 } else {
2679 INSIST(0);
2680 ISC_UNREACHABLE();
2681 }
2682 }
2683 }
2684 }
2685
2686 static struct flagtext {
2687 unsigned int flag;
2688 const char *text;
2689 } flagtexts[] = { { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
2690 { CFG_CLAUSEFLAG_TESTONLY, "test only" },
2691 { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
2692 { CFG_CLAUSEFLAG_MULTI, "may occur multiple times" },
2693 { CFG_CLAUSEFLAG_EXPERIMENTAL, "experimental" },
2694 { CFG_CLAUSEFLAG_DEPRECATED, "deprecated" },
2695 { CFG_CLAUSEFLAG_ANCIENT, "ancient" },
2696 { 0, NULL } };
2697
2698 void
cfg_print_clauseflags(cfg_printer_t * pctx,unsigned int flags)2699 cfg_print_clauseflags(cfg_printer_t *pctx, unsigned int flags) {
2700 struct flagtext *p;
2701 bool first = true;
2702 for (p = flagtexts; p->flag != 0; p++) {
2703 if ((flags & p->flag) != 0) {
2704 if (first) {
2705 cfg_print_cstr(pctx, " // ");
2706 } else {
2707 cfg_print_cstr(pctx, ", ");
2708 }
2709 cfg_print_cstr(pctx, p->text);
2710 first = false;
2711 }
2712 }
2713 }
2714
2715 void
cfg_doc_mapbody(cfg_printer_t * pctx,const cfg_type_t * type)2716 cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
2717 const cfg_clausedef_t *const *clauseset;
2718 const cfg_clausedef_t *clause;
2719
2720 REQUIRE(pctx != NULL);
2721 REQUIRE(type != NULL);
2722
2723 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
2724 for (clause = *clauseset; clause->name != NULL; clause++) {
2725 if (((pctx->flags & CFG_PRINTER_ACTIVEONLY) != 0) &&
2726 (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) ||
2727 ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0)))
2728 {
2729 continue;
2730 }
2731 if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) {
2732 continue;
2733 }
2734 cfg_print_cstr(pctx, clause->name);
2735 cfg_print_cstr(pctx, " ");
2736 cfg_doc_obj(pctx, clause->type);
2737 cfg_print_cstr(pctx, ";");
2738 cfg_print_clauseflags(pctx, clause->flags);
2739 cfg_print_cstr(pctx, "\n\n");
2740 }
2741 }
2742 }
2743
2744 void
cfg_print_map(cfg_printer_t * pctx,const cfg_obj_t * obj)2745 cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2746 REQUIRE(pctx != NULL);
2747 REQUIRE(obj != NULL);
2748
2749 if (obj->value.map.id != NULL) {
2750 cfg_print_obj(pctx, obj->value.map.id);
2751 cfg_print_cstr(pctx, " ");
2752 }
2753 print_open(pctx);
2754 cfg_print_mapbody(pctx, obj);
2755 print_close(pctx);
2756 }
2757
2758 void
cfg_doc_map(cfg_printer_t * pctx,const cfg_type_t * type)2759 cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
2760 const cfg_clausedef_t *const *clauseset;
2761 const cfg_clausedef_t *clause;
2762
2763 REQUIRE(pctx != NULL);
2764 REQUIRE(type != NULL);
2765
2766 if (type->parse == cfg_parse_named_map) {
2767 cfg_doc_obj(pctx, &cfg_type_astring);
2768 cfg_print_cstr(pctx, " ");
2769 } else if (type->parse == cfg_parse_addressed_map) {
2770 cfg_doc_obj(pctx, &cfg_type_netaddr);
2771 cfg_print_cstr(pctx, " ");
2772 } else if (type->parse == cfg_parse_netprefix_map) {
2773 cfg_doc_obj(pctx, &cfg_type_netprefix);
2774 cfg_print_cstr(pctx, " ");
2775 }
2776
2777 print_open(pctx);
2778
2779 for (clauseset = type->of; *clauseset != NULL; clauseset++) {
2780 for (clause = *clauseset; clause->name != NULL; clause++) {
2781 if (((pctx->flags & CFG_PRINTER_ACTIVEONLY) != 0) &&
2782 (((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0) ||
2783 ((clause->flags & CFG_CLAUSEFLAG_TESTONLY) != 0)))
2784 {
2785 continue;
2786 }
2787 if ((clause->flags & CFG_CLAUSEFLAG_ANCIENT) != 0) {
2788 continue;
2789 }
2790 cfg_print_indent(pctx);
2791 cfg_print_cstr(pctx, clause->name);
2792 if (clause->type->print != cfg_print_void) {
2793 cfg_print_cstr(pctx, " ");
2794 }
2795 cfg_doc_obj(pctx, clause->type);
2796 cfg_print_cstr(pctx, ";");
2797 cfg_print_clauseflags(pctx, clause->flags);
2798 cfg_print_cstr(pctx, "\n");
2799 }
2800 }
2801 print_close(pctx);
2802 }
2803
2804 bool
cfg_obj_ismap(const cfg_obj_t * obj)2805 cfg_obj_ismap(const cfg_obj_t *obj) {
2806 REQUIRE(obj != NULL);
2807 return (obj->type->rep == &cfg_rep_map);
2808 }
2809
2810 isc_result_t
cfg_map_get(const cfg_obj_t * mapobj,const char * name,const cfg_obj_t ** obj)2811 cfg_map_get(const cfg_obj_t *mapobj, const char *name, const cfg_obj_t **obj) {
2812 isc_result_t result;
2813 isc_symvalue_t val;
2814 const cfg_map_t *map;
2815
2816 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2817 REQUIRE(name != NULL);
2818 REQUIRE(obj != NULL && *obj == NULL);
2819
2820 map = &mapobj->value.map;
2821
2822 result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
2823 if (result != ISC_R_SUCCESS) {
2824 return (result);
2825 }
2826 *obj = val.as_pointer;
2827 return (ISC_R_SUCCESS);
2828 }
2829
2830 const cfg_obj_t *
cfg_map_getname(const cfg_obj_t * mapobj)2831 cfg_map_getname(const cfg_obj_t *mapobj) {
2832 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2833 return (mapobj->value.map.id);
2834 }
2835
2836 unsigned int
cfg_map_count(const cfg_obj_t * mapobj)2837 cfg_map_count(const cfg_obj_t *mapobj) {
2838 const cfg_map_t *map;
2839
2840 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
2841
2842 map = &mapobj->value.map;
2843 return (isc_symtab_count(map->symtab));
2844 }
2845
2846 const char *
cfg_map_firstclause(const cfg_type_t * map,const void ** clauses,unsigned int * idx)2847 cfg_map_firstclause(const cfg_type_t *map, const void **clauses,
2848 unsigned int *idx) {
2849 cfg_clausedef_t *const *clauseset;
2850
2851 REQUIRE(map != NULL && map->rep == &cfg_rep_map);
2852 REQUIRE(idx != NULL);
2853 REQUIRE(clauses != NULL && *clauses == NULL);
2854
2855 clauseset = map->of;
2856 if (*clauseset == NULL) {
2857 return (NULL);
2858 }
2859 *clauses = *clauseset;
2860 *idx = 0;
2861 while ((*clauseset)[*idx].name == NULL) {
2862 *clauses = (*++clauseset);
2863 if (*clauses == NULL) {
2864 return (NULL);
2865 }
2866 }
2867 return ((*clauseset)[*idx].name);
2868 }
2869
2870 const char *
cfg_map_nextclause(const cfg_type_t * map,const void ** clauses,unsigned int * idx)2871 cfg_map_nextclause(const cfg_type_t *map, const void **clauses,
2872 unsigned int *idx) {
2873 cfg_clausedef_t *const *clauseset;
2874
2875 REQUIRE(map != NULL && map->rep == &cfg_rep_map);
2876 REQUIRE(idx != NULL);
2877 REQUIRE(clauses != NULL && *clauses != NULL);
2878
2879 clauseset = map->of;
2880 while (*clauseset != NULL && *clauseset != *clauses) {
2881 clauseset++;
2882 }
2883 INSIST(*clauseset == *clauses);
2884 (*idx)++;
2885 while ((*clauseset)[*idx].name == NULL) {
2886 *idx = 0;
2887 *clauses = (*++clauseset);
2888 if (*clauses == NULL) {
2889 return (NULL);
2890 }
2891 }
2892 return ((*clauseset)[*idx].name);
2893 }
2894
2895 /* Parse an arbitrary token, storing its raw text representation. */
2896 static isc_result_t
parse_token(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2897 parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2898 cfg_obj_t *obj = NULL;
2899 isc_result_t result;
2900 isc_region_t r;
2901
2902 UNUSED(type);
2903
2904 CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
2905 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
2906 if (pctx->token.type == isc_tokentype_eof) {
2907 cfg_ungettoken(pctx);
2908 result = ISC_R_EOF;
2909 goto cleanup;
2910 }
2911
2912 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
2913
2914 obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
2915 obj->value.string.length = r.length;
2916 memmove(obj->value.string.base, r.base, r.length);
2917 obj->value.string.base[r.length] = '\0';
2918 *ret = obj;
2919 return (result);
2920
2921 cleanup:
2922 if (obj != NULL) {
2923 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
2924 }
2925 return (result);
2926 }
2927
2928 cfg_type_t cfg_type_token = { "token", parse_token,
2929 cfg_print_ustring, cfg_doc_terminal,
2930 &cfg_rep_string, NULL };
2931
2932 /*
2933 * An unsupported option. This is just a list of tokens with balanced braces
2934 * ending in a semicolon.
2935 */
2936
2937 static isc_result_t
parse_unsupported(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)2938 parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2939 cfg_obj_t *listobj = NULL;
2940 isc_result_t result;
2941 int braces = 0;
2942
2943 CHECK(cfg_create_list(pctx, type, &listobj));
2944
2945 for (;;) {
2946 cfg_listelt_t *elt = NULL;
2947
2948 CHECK(cfg_peektoken(pctx, 0));
2949 if (pctx->token.type == isc_tokentype_special) {
2950 if (pctx->token.value.as_char == '{') {
2951 braces++;
2952 } else if (pctx->token.value.as_char == '}') {
2953 braces--;
2954 } else if (pctx->token.value.as_char == ';') {
2955 if (braces == 0) {
2956 break;
2957 }
2958 }
2959 }
2960 if (pctx->token.type == isc_tokentype_eof || braces < 0) {
2961 cfg_parser_error(pctx, CFG_LOG_NEAR,
2962 "unexpected token");
2963 result = ISC_R_UNEXPECTEDTOKEN;
2964 goto cleanup;
2965 }
2966
2967 CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
2968 ISC_LIST_APPEND(listobj->value.list, elt, link);
2969 }
2970 INSIST(braces == 0);
2971 *ret = listobj;
2972 return (ISC_R_SUCCESS);
2973
2974 cleanup:
2975 CLEANUP_OBJ(listobj);
2976 return (result);
2977 }
2978
2979 cfg_type_t cfg_type_unsupported = { "unsupported", parse_unsupported,
2980 cfg_print_spacelist, cfg_doc_terminal,
2981 &cfg_rep_list, NULL };
2982
2983 /*
2984 * Try interpreting the current token as a network address.
2985 *
2986 * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
2987 * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
2988 * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
2989 * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
2990 * and the IPv6 wildcard address otherwise.
2991 */
2992 static isc_result_t
token_addr(cfg_parser_t * pctx,unsigned int flags,isc_netaddr_t * na)2993 token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
2994 char *s;
2995 struct in_addr in4a;
2996 struct in6_addr in6a;
2997
2998 if (pctx->token.type != isc_tokentype_string) {
2999 return (ISC_R_UNEXPECTEDTOKEN);
3000 }
3001
3002 s = TOKEN_STRING(pctx);
3003 if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
3004 if ((flags & CFG_ADDR_V4OK) != 0) {
3005 isc_netaddr_any(na);
3006 return (ISC_R_SUCCESS);
3007 } else if ((flags & CFG_ADDR_V6OK) != 0) {
3008 isc_netaddr_any6(na);
3009 return (ISC_R_SUCCESS);
3010 } else {
3011 INSIST(0);
3012 ISC_UNREACHABLE();
3013 }
3014 } else {
3015 if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
3016 if (inet_pton(AF_INET, s, &in4a) == 1) {
3017 isc_netaddr_fromin(na, &in4a);
3018 return (ISC_R_SUCCESS);
3019 }
3020 }
3021 if ((flags & CFG_ADDR_V4PREFIXOK) != 0 && strlen(s) <= 15U) {
3022 char buf[64];
3023 int i;
3024
3025 strlcpy(buf, s, sizeof(buf));
3026 for (i = 0; i < 3; i++) {
3027 strlcat(buf, ".0", sizeof(buf));
3028 if (inet_pton(AF_INET, buf, &in4a) == 1) {
3029 isc_netaddr_fromin(na, &in4a);
3030 return (ISC_R_IPV4PREFIX);
3031 }
3032 }
3033 }
3034 if ((flags & CFG_ADDR_V6OK) != 0 && strlen(s) <= 127U) {
3035 char buf[128]; /* see lib/bind9/getaddresses.c */
3036 char *d; /* zone delimiter */
3037 uint32_t zone = 0; /* scope zone ID */
3038
3039 strlcpy(buf, s, sizeof(buf));
3040 d = strchr(buf, '%');
3041 if (d != NULL) {
3042 *d = '\0';
3043 }
3044
3045 if (inet_pton(AF_INET6, buf, &in6a) == 1) {
3046 if (d != NULL) {
3047 isc_result_t result;
3048
3049 result = isc_netscope_pton(
3050 AF_INET6, d + 1, &in6a, &zone);
3051 if (result != ISC_R_SUCCESS) {
3052 return (result);
3053 }
3054 }
3055
3056 isc_netaddr_fromin6(na, &in6a);
3057 isc_netaddr_setzone(na, zone);
3058 return (ISC_R_SUCCESS);
3059 }
3060 }
3061 }
3062 return (ISC_R_UNEXPECTEDTOKEN);
3063 }
3064
3065 isc_result_t
cfg_parse_rawaddr(cfg_parser_t * pctx,unsigned int flags,isc_netaddr_t * na)3066 cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
3067 isc_result_t result;
3068 const char *wild = "";
3069 const char *prefix = "";
3070
3071 REQUIRE(pctx != NULL);
3072 REQUIRE(na != NULL);
3073
3074 CHECK(cfg_gettoken(pctx, 0));
3075 result = token_addr(pctx, flags, na);
3076 if (result == ISC_R_UNEXPECTEDTOKEN) {
3077 if ((flags & CFG_ADDR_WILDOK) != 0) {
3078 wild = " or '*'";
3079 }
3080 if ((flags & CFG_ADDR_V4PREFIXOK) != 0) {
3081 wild = " or IPv4 prefix";
3082 }
3083 if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK) {
3084 cfg_parser_error(pctx, CFG_LOG_NEAR,
3085 "expected IPv4 address%s%s", prefix,
3086 wild);
3087 } else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK) {
3088 cfg_parser_error(pctx, CFG_LOG_NEAR,
3089 "expected IPv6 address%s%s", prefix,
3090 wild);
3091 } else {
3092 cfg_parser_error(pctx, CFG_LOG_NEAR,
3093 "expected IP address%s%s", prefix,
3094 wild);
3095 }
3096 }
3097 cleanup:
3098 return (result);
3099 }
3100
3101 bool
cfg_lookingat_netaddr(cfg_parser_t * pctx,unsigned int flags)3102 cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
3103 isc_result_t result;
3104 isc_netaddr_t na_dummy;
3105
3106 REQUIRE(pctx != NULL);
3107
3108 result = token_addr(pctx, flags, &na_dummy);
3109 return (result == ISC_R_SUCCESS || result == ISC_R_IPV4PREFIX);
3110 }
3111
3112 isc_result_t
cfg_parse_rawport(cfg_parser_t * pctx,unsigned int flags,in_port_t * port)3113 cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
3114 isc_result_t result;
3115
3116 REQUIRE(pctx != NULL);
3117 REQUIRE(port != NULL);
3118
3119 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
3120
3121 if ((flags & CFG_ADDR_WILDOK) != 0 &&
3122 pctx->token.type == isc_tokentype_string &&
3123 strcmp(TOKEN_STRING(pctx), "*") == 0)
3124 {
3125 *port = 0;
3126 return (ISC_R_SUCCESS);
3127 }
3128 if (pctx->token.type != isc_tokentype_number) {
3129 cfg_parser_error(pctx, CFG_LOG_NEAR,
3130 "expected port number or '*'");
3131 return (ISC_R_UNEXPECTEDTOKEN);
3132 }
3133 if (pctx->token.value.as_ulong >= 65536U) {
3134 cfg_parser_error(pctx, CFG_LOG_NEAR,
3135 "port number out of range");
3136 return (ISC_R_UNEXPECTEDTOKEN);
3137 }
3138 *port = (in_port_t)(pctx->token.value.as_ulong);
3139 return (ISC_R_SUCCESS);
3140 cleanup:
3141 return (result);
3142 }
3143
3144 void
cfg_print_rawaddr(cfg_printer_t * pctx,const isc_netaddr_t * na)3145 cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
3146 isc_result_t result;
3147 char text[128];
3148 isc_buffer_t buf;
3149
3150 REQUIRE(pctx != NULL);
3151 REQUIRE(na != NULL);
3152
3153 isc_buffer_init(&buf, text, sizeof(text));
3154 result = isc_netaddr_totext(na, &buf);
3155 RUNTIME_CHECK(result == ISC_R_SUCCESS);
3156 cfg_print_chars(pctx, isc_buffer_base(&buf),
3157 isc_buffer_usedlength(&buf));
3158 }
3159
3160 isc_result_t
cfg_parse_dscp(cfg_parser_t * pctx,isc_dscp_t * dscp)3161 cfg_parse_dscp(cfg_parser_t *pctx, isc_dscp_t *dscp) {
3162 isc_result_t result;
3163
3164 REQUIRE(pctx != NULL);
3165 REQUIRE(dscp != NULL);
3166
3167 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
3168
3169 if (pctx->token.type != isc_tokentype_number) {
3170 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
3171 return (ISC_R_UNEXPECTEDTOKEN);
3172 }
3173 if (pctx->token.value.as_ulong > 63U) {
3174 cfg_parser_error(pctx, CFG_LOG_NEAR, "dscp out of range");
3175 return (ISC_R_RANGE);
3176 }
3177 *dscp = (isc_dscp_t)(pctx->token.value.as_ulong);
3178 return (ISC_R_SUCCESS);
3179 cleanup:
3180 return (result);
3181 }
3182
3183 /* netaddr */
3184
3185 static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
3186 static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
3187 static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
3188 static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
3189 static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
3190
3191 static isc_result_t
parse_netaddr(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)3192 parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3193 isc_result_t result;
3194 cfg_obj_t *obj = NULL;
3195 isc_netaddr_t netaddr;
3196 unsigned int flags = *(const unsigned int *)type->of;
3197
3198 CHECK(cfg_create_obj(pctx, type, &obj));
3199 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
3200 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
3201 obj->value.sockaddrdscp.dscp = -1;
3202 *ret = obj;
3203 return (ISC_R_SUCCESS);
3204 cleanup:
3205 CLEANUP_OBJ(obj);
3206 return (result);
3207 }
3208
3209 static void
cfg_doc_netaddr(cfg_printer_t * pctx,const cfg_type_t * type)3210 cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
3211 const unsigned int *flagp = type->of;
3212 int n = 0;
3213 if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) {
3214 cfg_print_cstr(pctx, "( ");
3215 }
3216 if ((*flagp & CFG_ADDR_V4OK) != 0) {
3217 cfg_print_cstr(pctx, "<ipv4_address>");
3218 n++;
3219 }
3220 if ((*flagp & CFG_ADDR_V6OK) != 0) {
3221 if (n != 0) {
3222 cfg_print_cstr(pctx, " | ");
3223 }
3224 cfg_print_cstr(pctx, "<ipv6_address>");
3225 n++;
3226 }
3227 if ((*flagp & CFG_ADDR_WILDOK) != 0) {
3228 if (n != 0) {
3229 cfg_print_cstr(pctx, " | ");
3230 }
3231 cfg_print_cstr(pctx, "*");
3232 n++;
3233 POST(n);
3234 }
3235 if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK) {
3236 cfg_print_cstr(pctx, " )");
3237 }
3238 }
3239
3240 cfg_type_t cfg_type_netaddr = { "netaddr", parse_netaddr,
3241 cfg_print_sockaddr, cfg_doc_netaddr,
3242 &cfg_rep_sockaddr, &netaddr_flags };
3243
3244 cfg_type_t cfg_type_netaddr4 = { "netaddr4", parse_netaddr,
3245 cfg_print_sockaddr, cfg_doc_netaddr,
3246 &cfg_rep_sockaddr, &netaddr4_flags };
3247
3248 cfg_type_t cfg_type_netaddr4wild = { "netaddr4wild", parse_netaddr,
3249 cfg_print_sockaddr, cfg_doc_netaddr,
3250 &cfg_rep_sockaddr, &netaddr4wild_flags };
3251
3252 cfg_type_t cfg_type_netaddr6 = { "netaddr6", parse_netaddr,
3253 cfg_print_sockaddr, cfg_doc_netaddr,
3254 &cfg_rep_sockaddr, &netaddr6_flags };
3255
3256 cfg_type_t cfg_type_netaddr6wild = { "netaddr6wild", parse_netaddr,
3257 cfg_print_sockaddr, cfg_doc_netaddr,
3258 &cfg_rep_sockaddr, &netaddr6wild_flags };
3259
3260 /* netprefix */
3261
3262 isc_result_t
cfg_parse_netprefix(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)3263 cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
3264 cfg_obj_t **ret) {
3265 cfg_obj_t *obj = NULL;
3266 isc_result_t result;
3267 isc_netaddr_t netaddr;
3268 unsigned int addrlen = 0, prefixlen;
3269 bool expectprefix;
3270
3271 REQUIRE(pctx != NULL);
3272 REQUIRE(ret != NULL && *ret == NULL);
3273
3274 UNUSED(type);
3275
3276 result = cfg_parse_rawaddr(
3277 pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK | CFG_ADDR_V6OK,
3278 &netaddr);
3279 if (result != ISC_R_SUCCESS && result != ISC_R_IPV4PREFIX) {
3280 CHECK(result);
3281 }
3282 switch (netaddr.family) {
3283 case AF_INET:
3284 addrlen = 32;
3285 break;
3286 case AF_INET6:
3287 addrlen = 128;
3288 break;
3289 default:
3290 INSIST(0);
3291 ISC_UNREACHABLE();
3292 }
3293 expectprefix = (result == ISC_R_IPV4PREFIX);
3294 CHECK(cfg_peektoken(pctx, 0));
3295 if (pctx->token.type == isc_tokentype_special &&
3296 pctx->token.value.as_char == '/')
3297 {
3298 CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
3299 CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
3300 if (pctx->token.type != isc_tokentype_number) {
3301 cfg_parser_error(pctx, CFG_LOG_NEAR,
3302 "expected prefix length");
3303 return (ISC_R_UNEXPECTEDTOKEN);
3304 }
3305 prefixlen = pctx->token.value.as_ulong;
3306 if (prefixlen > addrlen) {
3307 cfg_parser_error(pctx, CFG_LOG_NOPREP,
3308 "invalid prefix length");
3309 return (ISC_R_RANGE);
3310 }
3311 result = isc_netaddr_prefixok(&netaddr, prefixlen);
3312 if (result != ISC_R_SUCCESS) {
3313 char buf[ISC_NETADDR_FORMATSIZE + 1];
3314 isc_netaddr_format(&netaddr, buf, sizeof(buf));
3315 cfg_parser_error(pctx, CFG_LOG_NOPREP,
3316 "'%s/%u': address/prefix length "
3317 "mismatch",
3318 buf, prefixlen);
3319 return (ISC_R_FAILURE);
3320 }
3321 } else {
3322 if (expectprefix) {
3323 cfg_parser_error(pctx, CFG_LOG_NEAR,
3324 "incomplete IPv4 address or prefix");
3325 return (ISC_R_FAILURE);
3326 }
3327 prefixlen = addrlen;
3328 }
3329 CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
3330 obj->value.netprefix.address = netaddr;
3331 obj->value.netprefix.prefixlen = prefixlen;
3332 *ret = obj;
3333 return (ISC_R_SUCCESS);
3334 cleanup:
3335 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
3336 return (result);
3337 }
3338
3339 static void
print_netprefix(cfg_printer_t * pctx,const cfg_obj_t * obj)3340 print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3341 const cfg_netprefix_t *p = &obj->value.netprefix;
3342
3343 cfg_print_rawaddr(pctx, &p->address);
3344 cfg_print_cstr(pctx, "/");
3345 cfg_print_rawuint(pctx, p->prefixlen);
3346 }
3347
3348 bool
cfg_obj_isnetprefix(const cfg_obj_t * obj)3349 cfg_obj_isnetprefix(const cfg_obj_t *obj) {
3350 REQUIRE(obj != NULL);
3351 return (obj->type->rep == &cfg_rep_netprefix);
3352 }
3353
3354 void
cfg_obj_asnetprefix(const cfg_obj_t * obj,isc_netaddr_t * netaddr,unsigned int * prefixlen)3355 cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
3356 unsigned int *prefixlen) {
3357 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
3358 REQUIRE(netaddr != NULL);
3359 REQUIRE(prefixlen != NULL);
3360
3361 *netaddr = obj->value.netprefix.address;
3362 *prefixlen = obj->value.netprefix.prefixlen;
3363 }
3364
3365 cfg_type_t cfg_type_netprefix = { "netprefix", cfg_parse_netprefix,
3366 print_netprefix, cfg_doc_terminal,
3367 &cfg_rep_netprefix, NULL };
3368
3369 static isc_result_t
parse_sockaddrsub(cfg_parser_t * pctx,const cfg_type_t * type,int flags,cfg_obj_t ** ret)3370 parse_sockaddrsub(cfg_parser_t *pctx, const cfg_type_t *type, int flags,
3371 cfg_obj_t **ret) {
3372 isc_result_t result;
3373 isc_netaddr_t netaddr;
3374 in_port_t port = 0;
3375 isc_dscp_t dscp = -1;
3376 cfg_obj_t *obj = NULL;
3377 int have_port = 0, have_dscp = 0;
3378
3379 CHECK(cfg_create_obj(pctx, type, &obj));
3380 CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
3381 for (;;) {
3382 CHECK(cfg_peektoken(pctx, 0));
3383 if (pctx->token.type == isc_tokentype_string) {
3384 if (strcasecmp(TOKEN_STRING(pctx), "port") == 0) {
3385 CHECK(cfg_gettoken(pctx, 0)); /* read "port" */
3386 CHECK(cfg_parse_rawport(pctx, flags, &port));
3387 ++have_port;
3388 } else if ((flags & CFG_ADDR_DSCPOK) != 0 &&
3389 strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
3390 {
3391 CHECK(cfg_gettoken(pctx, 0)); /* read "dscp" */
3392 CHECK(cfg_parse_dscp(pctx, &dscp));
3393 ++have_dscp;
3394 } else {
3395 break;
3396 }
3397 } else {
3398 break;
3399 }
3400 }
3401 if (have_port > 1) {
3402 cfg_parser_error(pctx, 0, "expected at most one port");
3403 result = ISC_R_UNEXPECTEDTOKEN;
3404 goto cleanup;
3405 }
3406
3407 if (have_dscp > 1) {
3408 cfg_parser_error(pctx, 0, "expected at most one dscp");
3409 result = ISC_R_UNEXPECTEDTOKEN;
3410 goto cleanup;
3411 }
3412 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
3413 obj->value.sockaddrdscp.dscp = dscp;
3414 *ret = obj;
3415 return (ISC_R_SUCCESS);
3416
3417 cleanup:
3418 CLEANUP_OBJ(obj);
3419 return (result);
3420 }
3421
3422 static unsigned int sockaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
3423 cfg_type_t cfg_type_sockaddr = { "sockaddr", cfg_parse_sockaddr,
3424 cfg_print_sockaddr, cfg_doc_sockaddr,
3425 &cfg_rep_sockaddr, &sockaddr_flags };
3426
3427 static unsigned int sockaddrdscp_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK |
3428 CFG_ADDR_DSCPOK;
3429 cfg_type_t cfg_type_sockaddrdscp = { "sockaddr", cfg_parse_sockaddr,
3430 cfg_print_sockaddr, cfg_doc_sockaddr,
3431 &cfg_rep_sockaddr, &sockaddrdscp_flags };
3432
3433 isc_result_t
cfg_parse_sockaddr(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)3434 cfg_parse_sockaddr(cfg_parser_t *pctx, const cfg_type_t *type,
3435 cfg_obj_t **ret) {
3436 const unsigned int *flagp;
3437
3438 REQUIRE(pctx != NULL);
3439 REQUIRE(type != NULL);
3440 REQUIRE(ret != NULL && *ret == NULL);
3441
3442 flagp = type->of;
3443
3444 return (parse_sockaddrsub(pctx, &cfg_type_sockaddr, *flagp, ret));
3445 }
3446
3447 void
cfg_print_sockaddr(cfg_printer_t * pctx,const cfg_obj_t * obj)3448 cfg_print_sockaddr(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3449 isc_netaddr_t netaddr;
3450 in_port_t port;
3451 char buf[ISC_NETADDR_FORMATSIZE];
3452
3453 REQUIRE(pctx != NULL);
3454 REQUIRE(obj != NULL);
3455
3456 isc_netaddr_fromsockaddr(&netaddr, &obj->value.sockaddr);
3457 isc_netaddr_format(&netaddr, buf, sizeof(buf));
3458 cfg_print_cstr(pctx, buf);
3459 port = isc_sockaddr_getport(&obj->value.sockaddr);
3460 if (port != 0) {
3461 cfg_print_cstr(pctx, " port ");
3462 cfg_print_rawuint(pctx, port);
3463 }
3464 if (obj->value.sockaddrdscp.dscp != -1) {
3465 cfg_print_cstr(pctx, " dscp ");
3466 cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
3467 }
3468 }
3469
3470 void
cfg_doc_sockaddr(cfg_printer_t * pctx,const cfg_type_t * type)3471 cfg_doc_sockaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
3472 const unsigned int *flagp;
3473 int n = 0;
3474
3475 REQUIRE(pctx != NULL);
3476 REQUIRE(type != NULL);
3477
3478 flagp = type->of;
3479
3480 cfg_print_cstr(pctx, "( ");
3481 if ((*flagp & CFG_ADDR_V4OK) != 0) {
3482 cfg_print_cstr(pctx, "<ipv4_address>");
3483 n++;
3484 }
3485 if ((*flagp & CFG_ADDR_V6OK) != 0) {
3486 if (n != 0) {
3487 cfg_print_cstr(pctx, " | ");
3488 }
3489 cfg_print_cstr(pctx, "<ipv6_address>");
3490 n++;
3491 }
3492 if ((*flagp & CFG_ADDR_WILDOK) != 0) {
3493 if (n != 0) {
3494 cfg_print_cstr(pctx, " | ");
3495 }
3496 cfg_print_cstr(pctx, "*");
3497 n++;
3498 POST(n);
3499 }
3500 cfg_print_cstr(pctx, " ) ");
3501 if ((*flagp & CFG_ADDR_WILDOK) != 0) {
3502 cfg_print_cstr(pctx, "[ port ( <integer> | * ) ]");
3503 } else {
3504 cfg_print_cstr(pctx, "[ port <integer> ]");
3505 }
3506 if ((*flagp & CFG_ADDR_DSCPOK) != 0) {
3507 cfg_print_cstr(pctx, " [ dscp <integer> ]");
3508 }
3509 }
3510
3511 bool
cfg_obj_issockaddr(const cfg_obj_t * obj)3512 cfg_obj_issockaddr(const cfg_obj_t *obj) {
3513 REQUIRE(obj != NULL);
3514 return (obj->type->rep == &cfg_rep_sockaddr);
3515 }
3516
3517 const isc_sockaddr_t *
cfg_obj_assockaddr(const cfg_obj_t * obj)3518 cfg_obj_assockaddr(const cfg_obj_t *obj) {
3519 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
3520 return (&obj->value.sockaddr);
3521 }
3522
3523 isc_dscp_t
cfg_obj_getdscp(const cfg_obj_t * obj)3524 cfg_obj_getdscp(const cfg_obj_t *obj) {
3525 REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_sockaddr);
3526 return (obj->value.sockaddrdscp.dscp);
3527 }
3528
3529 isc_result_t
cfg_gettoken(cfg_parser_t * pctx,int options)3530 cfg_gettoken(cfg_parser_t *pctx, int options) {
3531 isc_result_t result;
3532
3533 REQUIRE(pctx != NULL);
3534
3535 if (pctx->seen_eof) {
3536 return (ISC_R_SUCCESS);
3537 }
3538
3539 options |= (ISC_LEXOPT_EOF | ISC_LEXOPT_NOMORE);
3540
3541 redo:
3542 pctx->token.type = isc_tokentype_unknown;
3543 result = isc_lex_gettoken(pctx->lexer, options, &pctx->token);
3544 pctx->ungotten = false;
3545 pctx->line = isc_lex_getsourceline(pctx->lexer);
3546
3547 switch (result) {
3548 case ISC_R_SUCCESS:
3549 if (pctx->token.type == isc_tokentype_eof) {
3550 result = isc_lex_close(pctx->lexer);
3551 INSIST(result == ISC_R_NOMORE ||
3552 result == ISC_R_SUCCESS);
3553
3554 if (isc_lex_getsourcename(pctx->lexer) != NULL) {
3555 /*
3556 * Closed an included file, not the main file.
3557 */
3558 cfg_listelt_t *elt;
3559 elt = ISC_LIST_TAIL(
3560 pctx->open_files->value.list);
3561 INSIST(elt != NULL);
3562 ISC_LIST_UNLINK(pctx->open_files->value.list,
3563 elt, link);
3564 ISC_LIST_APPEND(pctx->closed_files->value.list,
3565 elt, link);
3566 goto redo;
3567 }
3568 pctx->seen_eof = true;
3569 }
3570 break;
3571
3572 case ISC_R_NOSPACE:
3573 /* More understandable than "ran out of space". */
3574 cfg_parser_error(pctx, CFG_LOG_NEAR, "token too big");
3575 break;
3576
3577 case ISC_R_IOERROR:
3578 cfg_parser_error(pctx, 0, "%s", isc_result_totext(result));
3579 break;
3580
3581 default:
3582 cfg_parser_error(pctx, CFG_LOG_NEAR, "%s",
3583 isc_result_totext(result));
3584 break;
3585 }
3586 return (result);
3587 }
3588
3589 void
cfg_ungettoken(cfg_parser_t * pctx)3590 cfg_ungettoken(cfg_parser_t *pctx) {
3591 REQUIRE(pctx != NULL);
3592
3593 if (pctx->seen_eof) {
3594 return;
3595 }
3596 isc_lex_ungettoken(pctx->lexer, &pctx->token);
3597 pctx->ungotten = true;
3598 }
3599
3600 isc_result_t
cfg_peektoken(cfg_parser_t * pctx,int options)3601 cfg_peektoken(cfg_parser_t *pctx, int options) {
3602 isc_result_t result;
3603
3604 REQUIRE(pctx != NULL);
3605
3606 CHECK(cfg_gettoken(pctx, options));
3607 cfg_ungettoken(pctx);
3608 cleanup:
3609 return (result);
3610 }
3611
3612 /*
3613 * Get a string token, accepting both the quoted and the unquoted form.
3614 * Log an error if the next token is not a string.
3615 */
3616 static isc_result_t
cfg_getstringtoken(cfg_parser_t * pctx)3617 cfg_getstringtoken(cfg_parser_t *pctx) {
3618 isc_result_t result;
3619
3620 result = cfg_gettoken(pctx, CFG_LEXOPT_QSTRING);
3621 if (result != ISC_R_SUCCESS) {
3622 return (result);
3623 }
3624
3625 if (pctx->token.type != isc_tokentype_string &&
3626 pctx->token.type != isc_tokentype_qstring)
3627 {
3628 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected string");
3629 return (ISC_R_UNEXPECTEDTOKEN);
3630 }
3631 return (ISC_R_SUCCESS);
3632 }
3633
3634 void
cfg_parser_error(cfg_parser_t * pctx,unsigned int flags,const char * fmt,...)3635 cfg_parser_error(cfg_parser_t *pctx, unsigned int flags, const char *fmt, ...) {
3636 va_list args;
3637
3638 REQUIRE(pctx != NULL);
3639 REQUIRE(fmt != NULL);
3640
3641 va_start(args, fmt);
3642 parser_complain(pctx, false, flags, fmt, args);
3643 va_end(args);
3644 pctx->errors++;
3645 }
3646
3647 void
cfg_parser_warning(cfg_parser_t * pctx,unsigned int flags,const char * fmt,...)3648 cfg_parser_warning(cfg_parser_t *pctx, unsigned int flags, const char *fmt,
3649 ...) {
3650 va_list args;
3651
3652 REQUIRE(pctx != NULL);
3653 REQUIRE(fmt != NULL);
3654
3655 va_start(args, fmt);
3656 parser_complain(pctx, true, flags, fmt, args);
3657 va_end(args);
3658 pctx->warnings++;
3659 }
3660
3661 #define MAX_LOG_TOKEN 30 /* How much of a token to quote in log messages. */
3662
3663 static bool
have_current_file(cfg_parser_t * pctx)3664 have_current_file(cfg_parser_t *pctx) {
3665 cfg_listelt_t *elt;
3666 if (pctx->open_files == NULL) {
3667 return (false);
3668 }
3669
3670 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
3671 if (elt == NULL) {
3672 return (false);
3673 }
3674
3675 return (true);
3676 }
3677
3678 static char *
current_file(cfg_parser_t * pctx)3679 current_file(cfg_parser_t *pctx) {
3680 static char none[] = "none";
3681 cfg_listelt_t *elt;
3682 cfg_obj_t *fileobj;
3683
3684 if (!have_current_file(pctx)) {
3685 return (none);
3686 }
3687
3688 elt = ISC_LIST_TAIL(pctx->open_files->value.list);
3689 if (elt == NULL) { /* shouldn't be possible, but... */
3690 return (none);
3691 }
3692
3693 fileobj = elt->obj;
3694 INSIST(fileobj->type == &cfg_type_qstring);
3695 return (fileobj->value.string.base);
3696 }
3697
3698 static void
parser_complain(cfg_parser_t * pctx,bool is_warning,unsigned int flags,const char * format,va_list args)3699 parser_complain(cfg_parser_t *pctx, bool is_warning, unsigned int flags,
3700 const char *format, va_list args) {
3701 char tokenbuf[MAX_LOG_TOKEN + 10];
3702 static char where[PATH_MAX + 100];
3703 static char message[2048];
3704 int level = ISC_LOG_ERROR;
3705 const char *prep = "";
3706 size_t len;
3707
3708 if (is_warning) {
3709 level = ISC_LOG_WARNING;
3710 }
3711
3712 where[0] = '\0';
3713 if (have_current_file(pctx)) {
3714 snprintf(where, sizeof(where), "%s:%u: ", current_file(pctx),
3715 pctx->line);
3716 } else if (pctx->buf_name != NULL) {
3717 snprintf(where, sizeof(where), "%s: ", pctx->buf_name);
3718 }
3719
3720 len = vsnprintf(message, sizeof(message), format, args);
3721 #define ELLIPSIS " ... "
3722 if (len >= sizeof(message)) {
3723 message[sizeof(message) - sizeof(ELLIPSIS)] = 0;
3724 strlcat(message, ELLIPSIS, sizeof(message));
3725 }
3726
3727 if ((flags & (CFG_LOG_NEAR | CFG_LOG_BEFORE | CFG_LOG_NOPREP)) != 0) {
3728 isc_region_t r;
3729
3730 if (pctx->ungotten) {
3731 (void)cfg_gettoken(pctx, 0);
3732 }
3733
3734 if (pctx->token.type == isc_tokentype_eof) {
3735 snprintf(tokenbuf, sizeof(tokenbuf), "end of file");
3736 } else if (pctx->token.type == isc_tokentype_unknown) {
3737 flags = 0;
3738 tokenbuf[0] = '\0';
3739 } else {
3740 isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
3741 if (r.length > MAX_LOG_TOKEN) {
3742 snprintf(tokenbuf, sizeof(tokenbuf),
3743 "'%.*s...'", MAX_LOG_TOKEN, r.base);
3744 } else {
3745 snprintf(tokenbuf, sizeof(tokenbuf), "'%.*s'",
3746 (int)r.length, r.base);
3747 }
3748 }
3749
3750 /* Choose a preposition. */
3751 if ((flags & CFG_LOG_NEAR) != 0) {
3752 prep = " near ";
3753 } else if ((flags & CFG_LOG_BEFORE) != 0) {
3754 prep = " before ";
3755 } else {
3756 prep = " ";
3757 }
3758 } else {
3759 tokenbuf[0] = '\0';
3760 }
3761 isc_log_write(pctx->lctx, CAT, MOD, level, "%s%s%s%s", where, message,
3762 prep, tokenbuf);
3763 }
3764
3765 void
cfg_obj_log(const cfg_obj_t * obj,isc_log_t * lctx,int level,const char * fmt,...)3766 cfg_obj_log(const cfg_obj_t *obj, isc_log_t *lctx, int level, const char *fmt,
3767 ...) {
3768 va_list ap;
3769 char msgbuf[2048];
3770
3771 REQUIRE(obj != NULL);
3772 REQUIRE(fmt != NULL);
3773
3774 if (!isc_log_wouldlog(lctx, level)) {
3775 return;
3776 }
3777
3778 va_start(ap, fmt);
3779 vsnprintf(msgbuf, sizeof(msgbuf), fmt, ap);
3780 va_end(ap);
3781
3782 if (obj->file != NULL) {
3783 isc_log_write(lctx, CAT, MOD, level, "%s:%u: %s", obj->file,
3784 obj->line, msgbuf);
3785 } else {
3786 isc_log_write(lctx, CAT, MOD, level, "%s", msgbuf);
3787 }
3788 }
3789
3790 const char *
cfg_obj_file(const cfg_obj_t * obj)3791 cfg_obj_file(const cfg_obj_t *obj) {
3792 REQUIRE(obj != NULL);
3793
3794 return (obj->file);
3795 }
3796
3797 unsigned int
cfg_obj_line(const cfg_obj_t * obj)3798 cfg_obj_line(const cfg_obj_t *obj) {
3799 REQUIRE(obj != NULL);
3800
3801 return (obj->line);
3802 }
3803
3804 isc_result_t
cfg_create_obj(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)3805 cfg_create_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3806 cfg_obj_t *obj;
3807
3808 REQUIRE(pctx != NULL);
3809 REQUIRE(type != NULL);
3810 REQUIRE(ret != NULL && *ret == NULL);
3811
3812 obj = isc_mem_get(pctx->mctx, sizeof(cfg_obj_t));
3813
3814 obj->type = type;
3815 obj->file = current_file(pctx);
3816 obj->line = pctx->line;
3817 obj->pctx = pctx;
3818
3819 isc_refcount_init(&obj->references, 1);
3820
3821 *ret = obj;
3822
3823 return (ISC_R_SUCCESS);
3824 }
3825
3826 static void
map_symtabitem_destroy(char * key,unsigned int type,isc_symvalue_t symval,void * userarg)3827 map_symtabitem_destroy(char *key, unsigned int type, isc_symvalue_t symval,
3828 void *userarg) {
3829 cfg_obj_t *obj = symval.as_pointer;
3830 cfg_parser_t *pctx = (cfg_parser_t *)userarg;
3831
3832 UNUSED(key);
3833 UNUSED(type);
3834
3835 cfg_obj_destroy(pctx, &obj);
3836 }
3837
3838 static isc_result_t
create_map(cfg_parser_t * pctx,const cfg_type_t * type,cfg_obj_t ** ret)3839 create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3840 isc_result_t result;
3841 isc_symtab_t *symtab = NULL;
3842 cfg_obj_t *obj = NULL;
3843
3844 CHECK(cfg_create_obj(pctx, type, &obj));
3845 CHECK(isc_symtab_create(pctx->mctx, 5, /* XXX */
3846 map_symtabitem_destroy, pctx, false, &symtab));
3847 obj->value.map.symtab = symtab;
3848 obj->value.map.id = NULL;
3849
3850 *ret = obj;
3851 return (ISC_R_SUCCESS);
3852
3853 cleanup:
3854 if (obj != NULL) {
3855 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
3856 }
3857 return (result);
3858 }
3859
3860 static void
free_map(cfg_parser_t * pctx,cfg_obj_t * obj)3861 free_map(cfg_parser_t *pctx, cfg_obj_t *obj) {
3862 CLEANUP_OBJ(obj->value.map.id);
3863 isc_symtab_destroy(&obj->value.map.symtab);
3864 }
3865
3866 bool
cfg_obj_istype(const cfg_obj_t * obj,const cfg_type_t * type)3867 cfg_obj_istype(const cfg_obj_t *obj, const cfg_type_t *type) {
3868 REQUIRE(obj != NULL);
3869 REQUIRE(type != NULL);
3870
3871 return (obj->type == type);
3872 }
3873
3874 /*
3875 * Destroy 'obj', a configuration object created in 'pctx'.
3876 */
3877 void
cfg_obj_destroy(cfg_parser_t * pctx,cfg_obj_t ** objp)3878 cfg_obj_destroy(cfg_parser_t *pctx, cfg_obj_t **objp) {
3879 REQUIRE(objp != NULL && *objp != NULL);
3880 REQUIRE(pctx != NULL);
3881
3882 cfg_obj_t *obj = *objp;
3883 *objp = NULL;
3884
3885 if (isc_refcount_decrement(&obj->references) == 1) {
3886 obj->type->rep->free(pctx, obj);
3887 isc_refcount_destroy(&obj->references);
3888 isc_mem_put(pctx->mctx, obj, sizeof(cfg_obj_t));
3889 }
3890 }
3891
3892 void
cfg_obj_attach(cfg_obj_t * src,cfg_obj_t ** dest)3893 cfg_obj_attach(cfg_obj_t *src, cfg_obj_t **dest) {
3894 REQUIRE(src != NULL);
3895 REQUIRE(dest != NULL && *dest == NULL);
3896
3897 isc_refcount_increment(&src->references);
3898 *dest = src;
3899 }
3900
3901 static void
free_noop(cfg_parser_t * pctx,cfg_obj_t * obj)3902 free_noop(cfg_parser_t *pctx, cfg_obj_t *obj) {
3903 UNUSED(pctx);
3904 UNUSED(obj);
3905 }
3906
3907 void
cfg_doc_obj(cfg_printer_t * pctx,const cfg_type_t * type)3908 cfg_doc_obj(cfg_printer_t *pctx, const cfg_type_t *type) {
3909 REQUIRE(pctx != NULL);
3910 REQUIRE(type != NULL);
3911
3912 type->doc(pctx, type);
3913 }
3914
3915 void
cfg_doc_terminal(cfg_printer_t * pctx,const cfg_type_t * type)3916 cfg_doc_terminal(cfg_printer_t *pctx, const cfg_type_t *type) {
3917 REQUIRE(pctx != NULL);
3918 REQUIRE(type != NULL);
3919
3920 cfg_print_cstr(pctx, "<");
3921 cfg_print_cstr(pctx, type->name);
3922 cfg_print_cstr(pctx, ">");
3923 }
3924
3925 void
cfg_print_grammar(const cfg_type_t * type,unsigned int flags,void (* f)(void * closure,const char * text,int textlen),void * closure)3926 cfg_print_grammar(const cfg_type_t *type, unsigned int flags,
3927 void (*f)(void *closure, const char *text, int textlen),
3928 void *closure) {
3929 cfg_printer_t pctx;
3930
3931 pctx.f = f;
3932 pctx.closure = closure;
3933 pctx.indent = 0;
3934 pctx.flags = flags;
3935 cfg_doc_obj(&pctx, type);
3936 }
3937
3938 isc_result_t
cfg_parser_mapadd(cfg_parser_t * pctx,cfg_obj_t * mapobj,cfg_obj_t * obj,const char * clausename)3939 cfg_parser_mapadd(cfg_parser_t *pctx, cfg_obj_t *mapobj, cfg_obj_t *obj,
3940 const char *clausename) {
3941 isc_result_t result = ISC_R_SUCCESS;
3942 const cfg_map_t *map;
3943 isc_symvalue_t symval;
3944 cfg_obj_t *destobj = NULL;
3945 cfg_listelt_t *elt = NULL;
3946 const cfg_clausedef_t *const *clauseset;
3947 const cfg_clausedef_t *clause;
3948
3949 REQUIRE(pctx != NULL);
3950 REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
3951 REQUIRE(obj != NULL);
3952 REQUIRE(clausename != NULL);
3953
3954 map = &mapobj->value.map;
3955
3956 clause = NULL;
3957 for (clauseset = map->clausesets; *clauseset != NULL; clauseset++) {
3958 for (clause = *clauseset; clause->name != NULL; clause++) {
3959 if (strcasecmp(clause->name, clausename) == 0) {
3960 goto breakout;
3961 }
3962 }
3963 }
3964
3965 breakout:
3966 if (clause == NULL || clause->name == NULL) {
3967 return (ISC_R_FAILURE);
3968 }
3969
3970 result = isc_symtab_lookup(map->symtab, clausename, 0, &symval);
3971 if (result == ISC_R_NOTFOUND) {
3972 if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
3973 CHECK(cfg_create_list(pctx, &cfg_type_implicitlist,
3974 &destobj));
3975 CHECK(create_listelt(pctx, &elt));
3976 cfg_obj_attach(obj, &elt->obj);
3977 ISC_LIST_APPEND(destobj->value.list, elt, link);
3978 symval.as_pointer = destobj;
3979 } else {
3980 symval.as_pointer = obj;
3981 }
3982
3983 CHECK(isc_symtab_define(map->symtab, clausename, 1, symval,
3984 isc_symexists_reject));
3985 } else {
3986 cfg_obj_t *destobj2 = symval.as_pointer;
3987
3988 INSIST(result == ISC_R_SUCCESS);
3989
3990 if (destobj2->type == &cfg_type_implicitlist) {
3991 CHECK(create_listelt(pctx, &elt));
3992 cfg_obj_attach(obj, &elt->obj);
3993 ISC_LIST_APPEND(destobj2->value.list, elt, link);
3994 } else {
3995 result = ISC_R_EXISTS;
3996 }
3997 }
3998
3999 destobj = NULL;
4000 elt = NULL;
4001
4002 cleanup:
4003 if (elt != NULL) {
4004 free_listelt(pctx, elt);
4005 }
4006 CLEANUP_OBJ(destobj);
4007
4008 return (result);
4009 }
4010
4011 isc_result_t
cfg_pluginlist_foreach(const cfg_obj_t * config,const cfg_obj_t * list,isc_log_t * lctx,pluginlist_cb_t * callback,void * callback_data)4012 cfg_pluginlist_foreach(const cfg_obj_t *config, const cfg_obj_t *list,
4013 isc_log_t *lctx, pluginlist_cb_t *callback,
4014 void *callback_data) {
4015 isc_result_t result = ISC_R_SUCCESS;
4016 const cfg_listelt_t *element;
4017
4018 REQUIRE(config != NULL);
4019 REQUIRE(callback != NULL);
4020
4021 for (element = cfg_list_first(list); element != NULL;
4022 element = cfg_list_next(element))
4023 {
4024 const cfg_obj_t *plugin = cfg_listelt_value(element);
4025 const cfg_obj_t *obj;
4026 const char *type, *library;
4027 const char *parameters = NULL;
4028
4029 /* Get the path to the plugin module. */
4030 obj = cfg_tuple_get(plugin, "type");
4031 type = cfg_obj_asstring(obj);
4032
4033 /* Only query plugins are supported currently. */
4034 if (strcasecmp(type, "query") != 0) {
4035 cfg_obj_log(obj, lctx, ISC_LOG_ERROR,
4036 "unsupported plugin type");
4037 return (ISC_R_FAILURE);
4038 }
4039
4040 library = cfg_obj_asstring(cfg_tuple_get(plugin, "library"));
4041
4042 obj = cfg_tuple_get(plugin, "parameters");
4043 if (obj != NULL && cfg_obj_isstring(obj)) {
4044 parameters = cfg_obj_asstring(obj);
4045 }
4046
4047 result = callback(config, obj, library, parameters,
4048 callback_data);
4049 if (result != ISC_R_SUCCESS) {
4050 break;
4051 }
4052 }
4053
4054 return (result);
4055 }
4056