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