namedconf.c revision 1.1.1.1 1 /* $NetBSD: namedconf.c,v 1.1.1.1 2018/08/12 12:08:28 christos Exp $ */
2
3 /*
4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public
7 * License, v. 2.0. If a copy of the MPL was not distributed with this
8 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * See the COPYRIGHT file distributed with this work for additional
11 * information regarding copyright ownership.
12 */
13
14 /*! \file */
15
16 #include <config.h>
17
18 #include <stdlib.h>
19 #include <string.h>
20
21 #include <isc/lex.h>
22 #include <isc/mem.h>
23 #include <isc/result.h>
24 #include <isc/string.h>
25 #include <isc/util.h>
26
27 #include <dns/ttl.h>
28 #include <dns/result.h>
29
30 #include <isccfg/cfg.h>
31 #include <isccfg/grammar.h>
32 #include <isccfg/log.h>
33 #include <isccfg/namedconf.h>
34
35 #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
36
37 /*% Check a return value. */
38 #define CHECK(op) \
39 do { result = (op); \
40 if (result != ISC_R_SUCCESS) goto cleanup; \
41 } while (0)
42
43 /*% Clean up a configuration object if non-NULL. */
44 #define CLEANUP_OBJ(obj) \
45 do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
46
47
48 /*%
49 * Forward declarations of static functions.
50 */
51
52 static isc_result_t
53 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
54 const cfg_type_t *othertype, cfg_obj_t **ret);
55
56 static void
57 doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *enumtype,
58 const cfg_type_t *othertype);
59
60 static isc_result_t
61 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
62
63 static isc_result_t
64 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
65 cfg_obj_t **ret);
66
67 static isc_result_t
68 parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type,
69 cfg_obj_t **ret);
70 static void
71 print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj);
72
73 static void
74 doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type);
75
76 static void
77 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj);
78
79 static void
80 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
81
82 static void
83 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
84
85 #ifdef HAVE_GEOIP
86 static isc_result_t
87 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
88
89 static void
90 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj);
91
92 static void
93 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type);
94 #endif /* HAVE_GEOIP */
95
96 static cfg_type_t cfg_type_acl;
97 static cfg_type_t cfg_type_addrmatchelt;
98 static cfg_type_t cfg_type_bracketed_aml;
99 static cfg_type_t cfg_type_bracketed_dscpsockaddrlist;
100 static cfg_type_t cfg_type_bracketed_namesockaddrkeylist;
101 static cfg_type_t cfg_type_bracketed_sockaddrlist;
102 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist;
103 static cfg_type_t cfg_type_controls;
104 static cfg_type_t cfg_type_controls_sockaddr;
105 static cfg_type_t cfg_type_destinationlist;
106 static cfg_type_t cfg_type_dialuptype;
107 static cfg_type_t cfg_type_dlz;
108 static cfg_type_t cfg_type_dnstap;
109 static cfg_type_t cfg_type_dnstapoutput;
110 static cfg_type_t cfg_type_dyndb;
111 static cfg_type_t cfg_type_filter_aaaa;
112 static cfg_type_t cfg_type_ixfrdifftype;
113 static cfg_type_t cfg_type_key;
114 static cfg_type_t cfg_type_logfile;
115 static cfg_type_t cfg_type_logging;
116 static cfg_type_t cfg_type_logseverity;
117 static cfg_type_t cfg_type_logsuffix;
118 static cfg_type_t cfg_type_logversions;
119 static cfg_type_t cfg_type_masterselement;
120 static cfg_type_t cfg_type_maxttl;
121 static cfg_type_t cfg_type_minimal;
122 static cfg_type_t cfg_type_nameportiplist;
123 static cfg_type_t cfg_type_negated;
124 static cfg_type_t cfg_type_notifytype;
125 static cfg_type_t cfg_type_optional_allow;
126 static cfg_type_t cfg_type_optional_class;
127 static cfg_type_t cfg_type_optional_dscp;
128 static cfg_type_t cfg_type_optional_facility;
129 static cfg_type_t cfg_type_optional_keyref;
130 static cfg_type_t cfg_type_optional_port;
131 static cfg_type_t cfg_type_optional_uint32;
132 static cfg_type_t cfg_type_options;
133 static cfg_type_t cfg_type_portiplist;
134 static cfg_type_t cfg_type_printtime;
135 static cfg_type_t cfg_type_querysource4;
136 static cfg_type_t cfg_type_querysource6;
137 static cfg_type_t cfg_type_querysource;
138 static cfg_type_t cfg_type_server;
139 static cfg_type_t cfg_type_server_key_kludge;
140 static cfg_type_t cfg_type_size;
141 static cfg_type_t cfg_type_sizenodefault;
142 static cfg_type_t cfg_type_sizeorpercent;
143 static cfg_type_t cfg_type_sizeval;
144 static cfg_type_t cfg_type_sockaddr4wild;
145 static cfg_type_t cfg_type_sockaddr6wild;
146 static cfg_type_t cfg_type_statschannels;
147 static cfg_type_t cfg_type_ttlval;
148 static cfg_type_t cfg_type_view;
149 static cfg_type_t cfg_type_viewopts;
150 static cfg_type_t cfg_type_zone;
151
152 /*% tkey-dhkey */
153
154 static cfg_tuplefielddef_t tkey_dhkey_fields[] = {
155 { "name", &cfg_type_qstring, 0 },
156 { "keyid", &cfg_type_uint32, 0 },
157 { NULL, NULL, 0 }
158 };
159
160 static cfg_type_t cfg_type_tkey_dhkey = {
161 "tkey-dhkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
162 &cfg_rep_tuple, tkey_dhkey_fields
163 };
164
165 /*% listen-on */
166
167 static cfg_tuplefielddef_t listenon_fields[] = {
168 { "port", &cfg_type_optional_port, 0 },
169 { "dscp", &cfg_type_optional_dscp, 0 },
170 { "acl", &cfg_type_bracketed_aml, 0 },
171 { NULL, NULL, 0 }
172 };
173
174 static cfg_type_t cfg_type_listenon = {
175 "listenon", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
176 &cfg_rep_tuple, listenon_fields
177 };
178
179 /*% acl */
180
181 static cfg_tuplefielddef_t acl_fields[] = {
182 { "name", &cfg_type_astring, 0 },
183 { "value", &cfg_type_bracketed_aml, 0 },
184 { NULL, NULL, 0 }
185 };
186
187 static cfg_type_t cfg_type_acl = {
188 "acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
189 &cfg_rep_tuple, acl_fields
190 };
191
192 /*% masters */
193 static cfg_tuplefielddef_t masters_fields[] = {
194 { "name", &cfg_type_astring, 0 },
195 { "port", &cfg_type_optional_port, 0 },
196 { "dscp", &cfg_type_optional_dscp, 0 },
197 { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
198 { NULL, NULL, 0 }
199 };
200
201 static cfg_type_t cfg_type_masters = {
202 "masters", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
203 &cfg_rep_tuple, masters_fields
204 };
205
206 /*%
207 * "sockaddrkeylist", a list of socket addresses with optional keys
208 * and an optional default port, as used in the masters option.
209 * E.g.,
210 * "port 1234 { mymasters; 10.0.0.1 key foo; 1::2 port 69; }"
211 */
212
213 static cfg_tuplefielddef_t namesockaddrkey_fields[] = {
214 { "masterselement", &cfg_type_masterselement, 0 },
215 { "key", &cfg_type_optional_keyref, 0 },
216 { NULL, NULL, 0 },
217 };
218
219 static cfg_type_t cfg_type_namesockaddrkey = {
220 "namesockaddrkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
221 &cfg_rep_tuple, namesockaddrkey_fields
222 };
223
224 static cfg_type_t cfg_type_bracketed_namesockaddrkeylist = {
225 "bracketed_namesockaddrkeylist", cfg_parse_bracketed_list,
226 cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list,
227 &cfg_type_namesockaddrkey
228 };
229
230 static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = {
231 { "port", &cfg_type_optional_port, 0 },
232 { "dscp", &cfg_type_optional_dscp, 0 },
233 { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
234 { NULL, NULL, 0 }
235 };
236 static cfg_type_t cfg_type_namesockaddrkeylist = {
237 "sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
238 &cfg_rep_tuple, namesockaddrkeylist_fields
239 };
240
241 /*%
242 * A list of socket addresses with an optional default port, as used
243 * in the 'listen-on' option. E.g., "{ 10.0.0.1; 1::2 port 69; }"
244 */
245 static cfg_tuplefielddef_t portiplist_fields[] = {
246 { "port", &cfg_type_optional_port, 0 },
247 { "dscp", &cfg_type_optional_dscp, 0 },
248 { "addresses", &cfg_type_bracketed_dscpsockaddrlist, 0 },
249 { NULL, NULL, 0 }
250 };
251 static cfg_type_t cfg_type_portiplist = {
252 "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
253 &cfg_rep_tuple, portiplist_fields
254 };
255
256 /*%
257 * A public key, as in the "pubkey" statement.
258 */
259 static cfg_tuplefielddef_t pubkey_fields[] = {
260 { "flags", &cfg_type_uint32, 0 },
261 { "protocol", &cfg_type_uint32, 0 },
262 { "algorithm", &cfg_type_uint32, 0 },
263 { "key", &cfg_type_qstring, 0 },
264 { NULL, NULL, 0 }
265 };
266 static cfg_type_t cfg_type_pubkey = {
267 "pubkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
268 &cfg_rep_tuple, pubkey_fields
269 };
270
271 /*%
272 * A list of RR types, used in grant statements.
273 * Note that the old parser allows quotes around the RR type names.
274 */
275 static cfg_type_t cfg_type_rrtypelist = {
276 "rrtypelist", cfg_parse_spacelist, cfg_print_spacelist,
277 cfg_doc_terminal, &cfg_rep_list, &cfg_type_astring
278 };
279
280 static const char *mode_enums[] = { "deny", "grant", NULL };
281 static cfg_type_t cfg_type_mode = {
282 "mode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
283 &cfg_rep_string, &mode_enums
284 };
285
286 static isc_result_t
287 parse_matchtype(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
288 isc_result_t result;
289
290 CHECK(cfg_peektoken(pctx, 0));
291 if (pctx->token.type == isc_tokentype_string &&
292 strcasecmp(TOKEN_STRING(pctx), "zonesub") == 0) {
293 pctx->flags |= CFG_PCTX_SKIP;
294 }
295 return (cfg_parse_enum(pctx, type, ret));
296
297 cleanup:
298 return (result);
299 }
300
301 static isc_result_t
302 parse_matchname(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
303 isc_result_t result;
304 cfg_obj_t *obj = NULL;
305
306 if ((pctx->flags & CFG_PCTX_SKIP) != 0) {
307 pctx->flags &= ~CFG_PCTX_SKIP;
308 CHECK(cfg_parse_void(pctx, NULL, &obj));
309 } else
310 result = cfg_parse_astring(pctx, type, &obj);
311
312 *ret = obj;
313 cleanup:
314 return (result);
315 }
316
317 static void
318 doc_matchname(cfg_printer_t *pctx, const cfg_type_t *type) {
319 cfg_print_cstr(pctx, "[ ");
320 cfg_doc_obj(pctx, type->of);
321 cfg_print_cstr(pctx, " ]");
322 }
323
324 static const char *matchtype_enums[] = {
325 "6to4-self", "external", "krb5-self", "krb5-subdomain", "ms-self",
326 "ms-subdomain", "name", "self", "selfsub", "selfwild", "subdomain",
327 "tcp-self", "wildcard", "zonesub", NULL
328 };
329
330 static cfg_type_t cfg_type_matchtype = {
331 "matchtype", parse_matchtype, cfg_print_ustring,
332 cfg_doc_enum, &cfg_rep_string, &matchtype_enums
333 };
334
335 static cfg_type_t cfg_type_matchname = {
336 "optional_matchname", parse_matchname, cfg_print_ustring,
337 &doc_matchname, &cfg_rep_tuple, &cfg_type_ustring
338 };
339
340 /*%
341 * A grant statement, used in the update policy.
342 */
343 static cfg_tuplefielddef_t grant_fields[] = {
344 { "mode", &cfg_type_mode, 0 },
345 { "identity", &cfg_type_astring, 0 }, /* domain name */
346 { "matchtype", &cfg_type_matchtype, 0 },
347 { "name", &cfg_type_matchname, 0 }, /* domain name */
348 { "types", &cfg_type_rrtypelist, 0 },
349 { NULL, NULL, 0 }
350 };
351 static cfg_type_t cfg_type_grant = {
352 "grant", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
353 &cfg_rep_tuple, grant_fields
354 };
355
356 static cfg_type_t cfg_type_updatepolicy = {
357 "update_policy", parse_updatepolicy, print_updatepolicy,
358 doc_updatepolicy, &cfg_rep_list, &cfg_type_grant
359 };
360
361 static isc_result_t
362 parse_updatepolicy(cfg_parser_t *pctx, const cfg_type_t *type,
363 cfg_obj_t **ret)
364 {
365 isc_result_t result;
366 CHECK(cfg_gettoken(pctx, 0));
367 if (pctx->token.type == isc_tokentype_special &&
368 pctx->token.value.as_char == '{') {
369 cfg_ungettoken(pctx);
370 return (cfg_parse_bracketed_list(pctx, type, ret));
371 }
372
373 if (pctx->token.type == isc_tokentype_string &&
374 strcasecmp(TOKEN_STRING(pctx), "local") == 0) {
375 cfg_obj_t *obj = NULL;
376 CHECK(cfg_create_obj(pctx, &cfg_type_ustring, &obj));
377 obj->value.string.length = strlen("local");
378 obj->value.string.base = isc_mem_get(pctx->mctx,
379 obj->value.string.length + 1);
380 if (obj->value.string.base == NULL) {
381 isc_mem_put(pctx->mctx, obj, sizeof(*obj));
382 return (ISC_R_NOMEMORY);
383 }
384 memmove(obj->value.string.base, "local", 5);
385 obj->value.string.base[5] = '\0';
386 *ret = obj;
387 return (ISC_R_SUCCESS);
388 }
389
390 cfg_ungettoken(pctx);
391 return (ISC_R_UNEXPECTEDTOKEN);
392
393 cleanup:
394 return (result);
395 }
396
397 static void
398 print_updatepolicy(cfg_printer_t *pctx, const cfg_obj_t *obj) {
399 if (cfg_obj_isstring(obj))
400 cfg_print_ustring(pctx, obj);
401 else
402 cfg_print_bracketed_list(pctx, obj);
403 }
404
405 static void
406 doc_updatepolicy(cfg_printer_t *pctx, const cfg_type_t *type) {
407 cfg_print_cstr(pctx, "( local | { ");
408 cfg_doc_obj(pctx, type->of);
409 cfg_print_cstr(pctx, "; ... }");
410 }
411
412 /*%
413 * A view statement.
414 */
415 static cfg_tuplefielddef_t view_fields[] = {
416 { "name", &cfg_type_astring, 0 },
417 { "class", &cfg_type_optional_class, 0 },
418 { "options", &cfg_type_viewopts, 0 },
419 { NULL, NULL, 0 }
420 };
421 static cfg_type_t cfg_type_view = {
422 "view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
423 &cfg_rep_tuple, view_fields
424 };
425
426 /*%
427 * A zone statement.
428 */
429 static cfg_tuplefielddef_t zone_fields[] = {
430 { "name", &cfg_type_astring, 0 },
431 { "class", &cfg_type_optional_class, 0 },
432 { "options", &cfg_type_zoneopts, 0 },
433 { NULL, NULL, 0 }
434 };
435 static cfg_type_t cfg_type_zone = {
436 "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
437 &cfg_rep_tuple, zone_fields
438 };
439
440 /*%
441 * A "category" clause in the "logging" statement.
442 */
443 static cfg_tuplefielddef_t category_fields[] = {
444 { "name", &cfg_type_astring, 0 },
445 { "destinations", &cfg_type_destinationlist,0 },
446 { NULL, NULL, 0 }
447 };
448 static cfg_type_t cfg_type_category = {
449 "category", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
450 &cfg_rep_tuple, category_fields
451 };
452
453
454 /*%
455 * A dnssec key, as used in the "trusted-keys" statement.
456 */
457 static cfg_tuplefielddef_t dnsseckey_fields[] = {
458 { "name", &cfg_type_astring, 0 },
459 { "flags", &cfg_type_uint32, 0 },
460 { "protocol", &cfg_type_uint32, 0 },
461 { "algorithm", &cfg_type_uint32, 0 },
462 { "key", &cfg_type_qstring, 0 },
463 { NULL, NULL, 0 }
464 };
465 static cfg_type_t cfg_type_dnsseckey = {
466 "dnsseckey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
467 &cfg_rep_tuple, dnsseckey_fields
468 };
469
470 /*%
471 * A managed key initialization specifier, as used in the
472 * "managed-keys" statement.
473 */
474 static cfg_tuplefielddef_t managedkey_fields[] = {
475 { "name", &cfg_type_astring, 0 },
476 { "init", &cfg_type_ustring, 0 }, /* must be literal "initial-key" */
477 { "flags", &cfg_type_uint32, 0 },
478 { "protocol", &cfg_type_uint32, 0 },
479 { "algorithm", &cfg_type_uint32, 0 },
480 { "key", &cfg_type_qstring, 0 },
481 { NULL, NULL, 0 }
482 };
483 static cfg_type_t cfg_type_managedkey = {
484 "managedkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
485 &cfg_rep_tuple, managedkey_fields
486 };
487
488 static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring };
489
490 static cfg_type_t cfg_type_optional_wild_class = {
491 "optional_wild_class", parse_optional_keyvalue, print_keyvalue,
492 doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw
493 };
494
495 static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring };
496
497 static cfg_type_t cfg_type_optional_wild_type = {
498 "optional_wild_type", parse_optional_keyvalue,
499 print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw
500 };
501
502 static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring };
503
504 static cfg_type_t cfg_type_optional_wild_name = {
505 "optional_wild_name", parse_optional_keyvalue, print_keyvalue,
506 doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw
507 };
508
509 /*%
510 * An rrset ordering element.
511 */
512 static cfg_tuplefielddef_t rrsetorderingelement_fields[] = {
513 { "class", &cfg_type_optional_wild_class, 0 },
514 { "type", &cfg_type_optional_wild_type, 0 },
515 { "name", &cfg_type_optional_wild_name, 0 },
516 { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */
517 { "ordering", &cfg_type_ustring, 0 },
518 { NULL, NULL, 0 }
519 };
520 static cfg_type_t cfg_type_rrsetorderingelement = {
521 "rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple,
522 cfg_doc_tuple, &cfg_rep_tuple, rrsetorderingelement_fields
523 };
524
525 /*%
526 * A global or view "check-names" option. Note that the zone
527 * "check-names" option has a different syntax.
528 */
529
530 static const char *checktype_enums[] = { "master", "slave", "response", NULL };
531 static cfg_type_t cfg_type_checktype = {
532 "checktype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
533 &cfg_rep_string, &checktype_enums
534 };
535
536 static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL };
537 static cfg_type_t cfg_type_checkmode = {
538 "checkmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
539 &cfg_rep_string, &checkmode_enums
540 };
541
542 static const char *warn_enums[] = { "warn", "ignore", NULL };
543 static cfg_type_t cfg_type_warn = {
544 "warn", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
545 &cfg_rep_string, &warn_enums
546 };
547
548 static cfg_tuplefielddef_t checknames_fields[] = {
549 { "type", &cfg_type_checktype, 0 },
550 { "mode", &cfg_type_checkmode, 0 },
551 { NULL, NULL, 0 }
552 };
553
554 static cfg_type_t cfg_type_checknames = {
555 "checknames", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
556 &cfg_rep_tuple, checknames_fields
557 };
558
559 static cfg_type_t cfg_type_bracketed_dscpsockaddrlist = {
560 "bracketed_sockaddrlist", cfg_parse_bracketed_list,
561 cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list,
562 &cfg_type_sockaddrdscp
563 };
564
565 static cfg_type_t cfg_type_bracketed_sockaddrlist = {
566 "bracketed_sockaddrlist", cfg_parse_bracketed_list,
567 cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list,
568 &cfg_type_sockaddr
569 };
570
571 static const char *autodnssec_enums[] = {
572 "allow", "maintain", "off", NULL
573 };
574 static cfg_type_t cfg_type_autodnssec = {
575 "autodnssec", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
576 &cfg_rep_string, &autodnssec_enums
577 };
578
579 static const char *dnssecupdatemode_enums[] = {
580 "maintain", "no-resign", NULL
581 };
582 static cfg_type_t cfg_type_dnssecupdatemode = {
583 "dnssecupdatemode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
584 &cfg_rep_string, &dnssecupdatemode_enums
585 };
586
587 static const char *updatemethods_enums[] = {
588 "date", "increment", "unixtime", NULL
589 };
590 static cfg_type_t cfg_type_updatemethod = {
591 "updatemethod", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
592 &cfg_rep_string, &updatemethods_enums
593 };
594
595 /*
596 * zone-statistics: full, terse, or none.
597 *
598 * for backward compatibility, we also support boolean values.
599 * yes represents "full", no represents "terse". in the future we
600 * may change no to mean "none".
601 */
602 static const char *zonestat_enums[] = { "full", "terse", "none", NULL };
603 static isc_result_t
604 parse_zonestat(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
605 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
606 }
607 static void
608 doc_zonestat(cfg_printer_t *pctx, const cfg_type_t *type) {
609 doc_enum_or_other(pctx, type, &cfg_type_boolean);
610 }
611 static cfg_type_t cfg_type_zonestat = {
612 "zonestat", parse_zonestat, cfg_print_ustring, doc_zonestat,
613 &cfg_rep_string, zonestat_enums
614 };
615
616 static cfg_type_t cfg_type_rrsetorder = {
617 "rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list,
618 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_rrsetorderingelement
619 };
620
621 static keyword_type_t dscp_kw = { "dscp", &cfg_type_uint32 };
622
623 static cfg_type_t cfg_type_optional_dscp = {
624 "optional_dscp", parse_optional_keyvalue, print_keyvalue,
625 doc_optional_keyvalue, &cfg_rep_uint32, &dscp_kw
626 };
627
628 static keyword_type_t port_kw = { "port", &cfg_type_uint32 };
629
630 static cfg_type_t cfg_type_optional_port = {
631 "optional_port", parse_optional_keyvalue, print_keyvalue,
632 doc_optional_keyvalue, &cfg_rep_uint32, &port_kw
633 };
634
635 /*% A list of keys, as in the "key" clause of the controls statement. */
636 static cfg_type_t cfg_type_keylist = {
637 "keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
638 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring
639 };
640
641 /*% A list of dnssec keys, as in "trusted-keys" */
642 static cfg_type_t cfg_type_dnsseckeys = {
643 "dnsseckeys", cfg_parse_bracketed_list, cfg_print_bracketed_list,
644 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_dnsseckey
645 };
646
647 /*%
648 * A list of managed key entries, as in "trusted-keys". Currently
649 * (9.7.0) this has a format similar to dnssec keys, except the keyname
650 * is followed by the keyword "initial-key". In future releases, this
651 * keyword may take other values indicating different methods for the
652 * key to be initialized.
653 */
654
655 static cfg_type_t cfg_type_managedkeys = {
656 "managedkeys", cfg_parse_bracketed_list, cfg_print_bracketed_list,
657 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_managedkey
658 };
659
660 static const char *forwardtype_enums[] = { "first", "only", NULL };
661 static cfg_type_t cfg_type_forwardtype = {
662 "forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
663 &cfg_rep_string, &forwardtype_enums
664 };
665
666 static const char *zonetype_enums[] = {
667 "delegation-only", "forward", "hint", "master", "redirect",
668 "slave", "static-stub", "stub", NULL
669 };
670 static cfg_type_t cfg_type_zonetype = {
671 "zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
672 &cfg_rep_string, &zonetype_enums
673 };
674
675 static const char *loglevel_enums[] = {
676 "critical", "error", "warning", "notice", "info", "dynamic", NULL
677 };
678 static cfg_type_t cfg_type_loglevel = {
679 "loglevel", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
680 &cfg_rep_string, &loglevel_enums
681 };
682
683 static const char *transferformat_enums[] = {
684 "many-answers", "one-answer", NULL
685 };
686 static cfg_type_t cfg_type_transferformat = {
687 "transferformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
688 &cfg_rep_string, &transferformat_enums
689 };
690
691 /*%
692 * The special keyword "none", as used in the pid-file option.
693 */
694
695 static void
696 print_none(cfg_printer_t *pctx, const cfg_obj_t *obj) {
697 UNUSED(obj);
698 cfg_print_cstr(pctx, "none");
699 }
700
701 static cfg_type_t cfg_type_none = {
702 "none", NULL, print_none, NULL, &cfg_rep_void, NULL
703 };
704
705 /*%
706 * A quoted string or the special keyword "none". Used in the pid-file option.
707 */
708 static isc_result_t
709 parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type,
710 cfg_obj_t **ret)
711 {
712 isc_result_t result;
713
714 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
715 if (pctx->token.type == isc_tokentype_string &&
716 strcasecmp(TOKEN_STRING(pctx), "none") == 0)
717 return (cfg_create_obj(pctx, &cfg_type_none, ret));
718 cfg_ungettoken(pctx);
719 return (cfg_parse_qstring(pctx, type, ret));
720 cleanup:
721 return (result);
722 }
723
724 static void
725 doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) {
726 UNUSED(type);
727 cfg_print_cstr(pctx, "( <quoted_string> | none )");
728 }
729
730 static cfg_type_t cfg_type_qstringornone = {
731 "qstringornone", parse_qstringornone, NULL, doc_qstringornone,
732 NULL, NULL
733 };
734
735 /*%
736 * A boolean ("yes" or "no"), or the special keyword "auto".
737 * Used in the dnssec-validation option.
738 */
739 static void
740 print_auto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
741 UNUSED(obj);
742 cfg_print_cstr(pctx, "auto");
743 }
744
745 static cfg_type_t cfg_type_auto = {
746 "auto", NULL, print_auto, NULL, &cfg_rep_void, NULL
747 };
748
749 static isc_result_t
750 parse_boolorauto(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
751 isc_result_t result;
752
753 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
754 if (pctx->token.type == isc_tokentype_string &&
755 strcasecmp(TOKEN_STRING(pctx), "auto") == 0)
756 return (cfg_create_obj(pctx, &cfg_type_auto, ret));
757 cfg_ungettoken(pctx);
758 return (cfg_parse_boolean(pctx, type, ret));
759 cleanup:
760 return (result);
761 }
762
763 static void
764 print_boolorauto(cfg_printer_t *pctx, const cfg_obj_t *obj) {
765 if (obj->type->rep == &cfg_rep_void)
766 cfg_print_cstr(pctx, "auto");
767 else if (obj->value.boolean)
768 cfg_print_cstr(pctx, "yes");
769 else
770 cfg_print_cstr(pctx, "no");
771 }
772
773 static void
774 doc_boolorauto(cfg_printer_t *pctx, const cfg_type_t *type) {
775 UNUSED(type);
776 cfg_print_cstr(pctx, "( yes | no | auto )");
777 }
778
779 static cfg_type_t cfg_type_boolorauto = {
780 "boolorauto", parse_boolorauto, print_boolorauto,
781 doc_boolorauto, NULL, NULL
782 };
783
784 /*%
785 * keyword hostname
786 */
787 static void
788 print_hostname(cfg_printer_t *pctx, const cfg_obj_t *obj) {
789 UNUSED(obj);
790 cfg_print_cstr(pctx, "hostname");
791 }
792
793 static cfg_type_t cfg_type_hostname = {
794 "hostname", NULL, print_hostname, NULL, &cfg_rep_boolean, NULL
795 };
796
797 /*%
798 * "server-id" argument.
799 */
800
801 static isc_result_t
802 parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type,
803 cfg_obj_t **ret)
804 {
805 isc_result_t result;
806 CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
807 if (pctx->token.type == isc_tokentype_string &&
808 strcasecmp(TOKEN_STRING(pctx), "none") == 0)
809 return (cfg_create_obj(pctx, &cfg_type_none, ret));
810 if (pctx->token.type == isc_tokentype_string &&
811 strcasecmp(TOKEN_STRING(pctx), "hostname") == 0) {
812 result = cfg_create_obj(pctx, &cfg_type_hostname, ret);
813 if (result == ISC_R_SUCCESS)
814 (*ret)->value.boolean = ISC_TRUE;
815 return (result);
816 }
817 cfg_ungettoken(pctx);
818 return (cfg_parse_qstring(pctx, type, ret));
819 cleanup:
820 return (result);
821 }
822
823 static void
824 doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) {
825 UNUSED(type);
826 cfg_print_cstr(pctx, "( <quoted_string> | none | hostname )");
827 }
828
829 static cfg_type_t cfg_type_serverid = {
830 "serverid", parse_serverid, NULL, doc_serverid, NULL, NULL };
831
832 /*%
833 * Port list.
834 */
835 static void
836 print_porttuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
837 cfg_print_cstr(pctx, "range ");
838 cfg_print_tuple(pctx, obj);
839 }
840 static cfg_tuplefielddef_t porttuple_fields[] = {
841 { "loport", &cfg_type_uint32, 0 },
842 { "hiport", &cfg_type_uint32, 0 },
843 { NULL, NULL, 0 }
844 };
845 static cfg_type_t cfg_type_porttuple = {
846 "porttuple", cfg_parse_tuple, print_porttuple, cfg_doc_tuple,
847 &cfg_rep_tuple, porttuple_fields
848 };
849
850 static isc_result_t
851 parse_port(cfg_parser_t *pctx, cfg_obj_t **ret) {
852 isc_result_t result;
853
854 CHECK(cfg_parse_uint32(pctx, NULL, ret));
855 if ((*ret)->value.uint32 > 0xffff) {
856 cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port");
857 cfg_obj_destroy(pctx, ret);
858 result = ISC_R_RANGE;
859 }
860
861 cleanup:
862 return (result);
863 }
864
865 static isc_result_t
866 parse_portrange(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
867 isc_result_t result;
868 cfg_obj_t *obj = NULL;
869
870 UNUSED(type);
871
872 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
873 if (pctx->token.type == isc_tokentype_number)
874 CHECK(parse_port(pctx, ret));
875 else {
876 CHECK(cfg_gettoken(pctx, 0));
877 if (pctx->token.type != isc_tokentype_string ||
878 strcasecmp(TOKEN_STRING(pctx), "range") != 0) {
879 cfg_parser_error(pctx, CFG_LOG_NEAR,
880 "expected integer or 'range'");
881 return (ISC_R_UNEXPECTEDTOKEN);
882 }
883 CHECK(cfg_create_tuple(pctx, &cfg_type_porttuple, &obj));
884 CHECK(parse_port(pctx, &obj->value.tuple[0]));
885 CHECK(parse_port(pctx, &obj->value.tuple[1]));
886 if (obj->value.tuple[0]->value.uint32 >
887 obj->value.tuple[1]->value.uint32) {
888 cfg_parser_error(pctx, CFG_LOG_NOPREP,
889 "low port '%u' must not be larger "
890 "than high port",
891 obj->value.tuple[0]->value.uint32);
892 result = ISC_R_RANGE;
893 goto cleanup;
894 }
895 *ret = obj;
896 obj = NULL;
897 }
898
899 cleanup:
900 if (obj != NULL)
901 cfg_obj_destroy(pctx, &obj);
902 return (result);
903 }
904
905 static cfg_type_t cfg_type_portrange = {
906 "portrange", parse_portrange, NULL, cfg_doc_terminal,
907 NULL, NULL
908 };
909
910 static cfg_type_t cfg_type_bracketed_portlist = {
911 "bracketed_sockaddrlist", cfg_parse_bracketed_list,
912 cfg_print_bracketed_list, cfg_doc_bracketed_list,
913 &cfg_rep_list, &cfg_type_portrange
914 };
915
916 static const char *cookiealg_enums[] = { "aes", "sha1", "sha256", NULL };
917 static cfg_type_t cfg_type_cookiealg = {
918 "cookiealg", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
919 &cfg_rep_string, &cookiealg_enums
920 };
921
922 /*%
923 * fetch-quota-params
924 */
925
926 static cfg_tuplefielddef_t fetchquota_fields[] = {
927 { "frequency", &cfg_type_uint32, 0 },
928 { "low", &cfg_type_fixedpoint, 0 },
929 { "high", &cfg_type_fixedpoint, 0 },
930 { "discount", &cfg_type_fixedpoint, 0 },
931 { NULL, NULL, 0 }
932 };
933
934 static cfg_type_t cfg_type_fetchquota = {
935 "fetchquota", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
936 &cfg_rep_tuple, fetchquota_fields
937 };
938
939 /*%
940 * fetches-per-server or fetches-per-zone
941 */
942
943 static const char *response_enums[] = { "drop", "fail", NULL };
944
945 static isc_result_t
946 parse_optional_enum(cfg_parser_t *pctx, const cfg_type_t *type,
947 cfg_obj_t **ret)
948 {
949 return (parse_enum_or_other(pctx, type, &cfg_type_void, ret));
950 }
951
952 static void
953 doc_optional_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
954 UNUSED(type);
955 cfg_print_cstr(pctx, "[ ");
956 cfg_doc_enum(pctx, type);
957 cfg_print_cstr(pctx, " ]");
958 }
959
960 static cfg_type_t cfg_type_responsetype = {
961 "responsetype", parse_optional_enum, cfg_print_ustring,
962 doc_optional_enum, &cfg_rep_string, response_enums
963 };
964
965 static cfg_tuplefielddef_t fetchesper_fields[] = {
966 { "fetches", &cfg_type_uint32, 0 },
967 { "response", &cfg_type_responsetype, 0 },
968 { NULL, NULL, 0 }
969 };
970
971 static cfg_type_t cfg_type_fetchesper = {
972 "fetchesper", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
973 &cfg_rep_tuple, fetchesper_fields
974 };
975
976 /*%
977 * Clauses that can be found within the top level of the named.conf
978 * file only.
979 */
980 static cfg_clausedef_t
981 namedconf_clauses[] = {
982 { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
983 { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
984 { "logging", &cfg_type_logging, 0 },
985 { "lwres", &cfg_type_bracketed_text,
986 CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
987 { "masters", &cfg_type_masters, CFG_CLAUSEFLAG_MULTI },
988 { "options", &cfg_type_options, 0 },
989 { "statistics-channels", &cfg_type_statschannels,
990 CFG_CLAUSEFLAG_MULTI },
991 { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
992 { NULL, NULL, 0 }
993 };
994
995 /*%
996 * Clauses that can occur at the top level or in the view
997 * statement, but not in the options block.
998 */
999 static cfg_clausedef_t
1000 namedconf_or_view_clauses[] = {
1001 { "dlz", &cfg_type_dlz, CFG_CLAUSEFLAG_MULTI },
1002 { "dyndb", &cfg_type_dyndb, CFG_CLAUSEFLAG_MULTI },
1003 { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
1004 { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI },
1005 { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
1006 { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
1007 { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
1008 { NULL, NULL, 0 }
1009 };
1010
1011 /*%
1012 * Clauses that can occur in the bind.keys file.
1013 */
1014 static cfg_clausedef_t
1015 bindkeys_clauses[] = {
1016 { "managed-keys", &cfg_type_managedkeys, CFG_CLAUSEFLAG_MULTI },
1017 { "trusted-keys", &cfg_type_dnsseckeys, CFG_CLAUSEFLAG_MULTI },
1018 { NULL, NULL, 0 }
1019 };
1020
1021 static const char *fstrm_model_enums[] = { "mpsc", "spsc", NULL };
1022 static cfg_type_t cfg_type_fstrm_model = {
1023 "model", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
1024 &cfg_rep_string, &fstrm_model_enums
1025 };
1026
1027 /*%
1028 * Clauses that can be found within the 'options' statement.
1029 */
1030 static cfg_clausedef_t
1031 options_clauses[] = {
1032 { "answer-cookie", &cfg_type_boolean, 0 },
1033 { "automatic-interface-scan", &cfg_type_boolean, 0 },
1034 { "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
1035 { "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
1036 { "bindkeys-file", &cfg_type_qstring, 0 },
1037 { "blackhole", &cfg_type_bracketed_aml, 0 },
1038 { "cookie-algorithm", &cfg_type_cookiealg, 0 },
1039 { "cookie-secret", &cfg_type_sstring, CFG_CLAUSEFLAG_MULTI },
1040 { "coresize", &cfg_type_size, 0 },
1041 { "datasize", &cfg_type_size, 0 },
1042 { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1043 { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
1044 #ifdef HAVE_DNSTAP
1045 { "dnstap-output", &cfg_type_dnstapoutput, 0 },
1046 { "dnstap-identity", &cfg_type_serverid, 0 },
1047 { "dnstap-version", &cfg_type_qstringornone, 0 },
1048 #else
1049 { "dnstap-output", &cfg_type_dnstapoutput,
1050 CFG_CLAUSEFLAG_NOTCONFIGURED },
1051 { "dnstap-identity", &cfg_type_serverid,
1052 CFG_CLAUSEFLAG_NOTCONFIGURED },
1053 { "dnstap-version", &cfg_type_qstringornone,
1054 CFG_CLAUSEFLAG_NOTCONFIGURED },
1055 #endif
1056 { "dscp", &cfg_type_uint32, 0 },
1057 { "dump-file", &cfg_type_qstring, 0 },
1058 { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1059 { "files", &cfg_type_size, 0 },
1060 { "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
1061 #ifdef HAVE_DNSTAP
1062 { "fstrm-set-buffer-hint", &cfg_type_uint32, 0 },
1063 { "fstrm-set-flush-timeout", &cfg_type_uint32, 0 },
1064 { "fstrm-set-input-queue-size", &cfg_type_uint32, 0 },
1065 { "fstrm-set-output-notify-threshold", &cfg_type_uint32, 0 },
1066 { "fstrm-set-output-queue-model", &cfg_type_fstrm_model, 0 },
1067 { "fstrm-set-output-queue-size", &cfg_type_uint32, 0 },
1068 { "fstrm-set-reopen-interval", &cfg_type_uint32, 0 },
1069 #else
1070 { "fstrm-set-buffer-hint", &cfg_type_uint32,
1071 CFG_CLAUSEFLAG_NOTCONFIGURED },
1072 { "fstrm-set-flush-timeout", &cfg_type_uint32,
1073 CFG_CLAUSEFLAG_NOTCONFIGURED },
1074 { "fstrm-set-input-queue-size", &cfg_type_uint32,
1075 CFG_CLAUSEFLAG_NOTCONFIGURED },
1076 { "fstrm-set-output-notify-threshold", &cfg_type_uint32,
1077 CFG_CLAUSEFLAG_NOTCONFIGURED },
1078 { "fstrm-set-output-queue-model", &cfg_type_fstrm_model,
1079 CFG_CLAUSEFLAG_NOTCONFIGURED },
1080 { "fstrm-set-output-queue-size", &cfg_type_uint32,
1081 CFG_CLAUSEFLAG_NOTCONFIGURED },
1082 { "fstrm-set-reopen-interval", &cfg_type_uint32,
1083 CFG_CLAUSEFLAG_NOTCONFIGURED },
1084 #endif /* HAVE_DNSTAP */
1085 #ifdef HAVE_GEOIP
1086 { "geoip-directory", &cfg_type_qstringornone, 0 },
1087 { "geoip-use-ecs", &cfg_type_boolean, 0 },
1088 #else
1089 { "geoip-directory", &cfg_type_qstringornone,
1090 CFG_CLAUSEFLAG_NOTCONFIGURED },
1091 { "geoip-use-ecs", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
1092 #endif /* HAVE_GEOIP */
1093 { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1094 { "heartbeat-interval", &cfg_type_uint32, 0 },
1095 { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP },
1096 { "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
1097 { "hostname", &cfg_type_qstringornone, 0 },
1098 { "interface-interval", &cfg_type_uint32, 0 },
1099 { "keep-response-order", &cfg_type_bracketed_aml, 0 },
1100 { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
1101 { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
1102 { "lock-file", &cfg_type_qstringornone, 0 },
1103 { "managed-keys-directory", &cfg_type_qstring, 0 },
1104 { "match-mapped-addresses", &cfg_type_boolean, 0 },
1105 { "max-rsa-exponent-size", &cfg_type_uint32, 0 },
1106 { "memstatistics", &cfg_type_boolean, 0 },
1107 { "memstatistics-file", &cfg_type_qstring, 0 },
1108 { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1109 { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
1110 { "notify-rate", &cfg_type_uint32, 0 },
1111 { "pid-file", &cfg_type_qstringornone, 0 },
1112 { "port", &cfg_type_uint32, 0 },
1113 { "querylog", &cfg_type_boolean, 0 },
1114 { "random-device", &cfg_type_qstringornone, 0 },
1115 { "recursing-file", &cfg_type_qstring, 0 },
1116 { "recursive-clients", &cfg_type_uint32, 0 },
1117 { "reserved-sockets", &cfg_type_uint32, 0 },
1118 { "secroots-file", &cfg_type_qstring, 0 },
1119 { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
1120 { "serial-query-rate", &cfg_type_uint32, 0 },
1121 { "server-id", &cfg_type_serverid, 0 },
1122 { "session-keyalg", &cfg_type_astring, 0 },
1123 { "session-keyfile", &cfg_type_qstringornone, 0 },
1124 { "session-keyname", &cfg_type_astring, 0 },
1125 { "sit-secret", &cfg_type_sstring, CFG_CLAUSEFLAG_OBSOLETE },
1126 { "stacksize", &cfg_type_size, 0 },
1127 { "startup-notify-rate", &cfg_type_uint32, 0 },
1128 { "statistics-file", &cfg_type_qstring, 0 },
1129 { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI },
1130 { "tcp-advertised-timeout", &cfg_type_uint32, 0 },
1131 { "tcp-clients", &cfg_type_uint32, 0 },
1132 { "tcp-idle-timeout", &cfg_type_uint32, 0 },
1133 { "tcp-initial-timeout", &cfg_type_uint32, 0 },
1134 { "tcp-keepalive-timeout", &cfg_type_uint32, 0 },
1135 { "tcp-listen-queue", &cfg_type_uint32, 0 },
1136 { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 },
1137 { "tkey-domain", &cfg_type_qstring, 0 },
1138 { "tkey-gssapi-credential", &cfg_type_qstring, 0 },
1139 { "tkey-gssapi-keytab", &cfg_type_qstring, 0 },
1140 { "transfer-message-size", &cfg_type_uint32, 0 },
1141 { "transfers-in", &cfg_type_uint32, 0 },
1142 { "transfers-out", &cfg_type_uint32, 0 },
1143 { "transfers-per-ns", &cfg_type_uint32, 0 },
1144 { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1145 { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1146 { "use-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1147 { "use-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
1148 { "use-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
1149 { "version", &cfg_type_qstringornone, 0 },
1150 { NULL, NULL, 0 }
1151 };
1152
1153 static cfg_type_t cfg_type_namelist = {
1154 "namelist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1155 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_qstring
1156 };
1157
1158 static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
1159
1160 static cfg_type_t cfg_type_optional_exclude = {
1161 "optional_exclude", parse_optional_keyvalue, print_keyvalue,
1162 doc_optional_keyvalue, &cfg_rep_list, &exclude_kw
1163 };
1164
1165 static keyword_type_t exceptionnames_kw = {
1166 "except-from", &cfg_type_namelist
1167 };
1168
1169 static cfg_type_t cfg_type_optional_exceptionnames = {
1170 "optional_allow", parse_optional_keyvalue, print_keyvalue,
1171 doc_optional_keyvalue, &cfg_rep_list, &exceptionnames_kw
1172 };
1173
1174 static cfg_tuplefielddef_t denyaddresses_fields[] = {
1175 { "acl", &cfg_type_bracketed_aml, 0 },
1176 { "except-from", &cfg_type_optional_exceptionnames, 0 },
1177 { NULL, NULL, 0 }
1178 };
1179
1180 static cfg_type_t cfg_type_denyaddresses = {
1181 "denyaddresses", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1182 &cfg_rep_tuple, denyaddresses_fields
1183 };
1184
1185 static cfg_tuplefielddef_t denyaliases_fields[] = {
1186 { "name", &cfg_type_namelist, 0 },
1187 { "except-from", &cfg_type_optional_exceptionnames, 0 },
1188 { NULL, NULL, 0 }
1189 };
1190
1191 static cfg_type_t cfg_type_denyaliases = {
1192 "denyaliases", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1193 &cfg_rep_tuple, denyaliases_fields
1194 };
1195
1196 static cfg_type_t cfg_type_algorithmlist = {
1197 "algorithmlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1198 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring
1199 };
1200
1201 static cfg_tuplefielddef_t disablealgorithm_fields[] = {
1202 { "name", &cfg_type_astring, 0 },
1203 { "algorithms", &cfg_type_algorithmlist, 0 },
1204 { NULL, NULL, 0 }
1205 };
1206
1207 static cfg_type_t cfg_type_disablealgorithm = {
1208 "disablealgorithm", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1209 &cfg_rep_tuple, disablealgorithm_fields
1210 };
1211
1212 static cfg_type_t cfg_type_dsdigestlist = {
1213 "dsdigestlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1214 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring
1215 };
1216
1217 static cfg_tuplefielddef_t disabledsdigest_fields[] = {
1218 { "name", &cfg_type_astring, 0 },
1219 { "digests", &cfg_type_dsdigestlist, 0 },
1220 { NULL, NULL, 0 }
1221 };
1222
1223 static cfg_type_t cfg_type_disabledsdigest = {
1224 "disabledsdigest", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1225 &cfg_rep_tuple, disabledsdigest_fields
1226 };
1227
1228 static cfg_tuplefielddef_t mustbesecure_fields[] = {
1229 { "name", &cfg_type_astring, 0 },
1230 { "value", &cfg_type_boolean, 0 },
1231 { NULL, NULL, 0 }
1232 };
1233
1234 static cfg_type_t cfg_type_mustbesecure = {
1235 "mustbesecure", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1236 &cfg_rep_tuple, mustbesecure_fields
1237 };
1238
1239 static const char *masterformat_enums[] = { "map", "raw", "text", NULL };
1240 static cfg_type_t cfg_type_masterformat = {
1241 "masterformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
1242 &cfg_rep_string, &masterformat_enums
1243 };
1244
1245 static const char *masterstyle_enums[] = { "full", "relative", NULL };
1246 static cfg_type_t cfg_type_masterstyle = {
1247 "masterstyle", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
1248 &cfg_rep_string, &masterstyle_enums
1249 };
1250
1251 static keyword_type_t blocksize_kw = { "block-size", &cfg_type_uint32 };
1252
1253 static cfg_type_t cfg_type_blocksize = {
1254 "blocksize", parse_keyvalue, print_keyvalue,
1255 doc_keyvalue, &cfg_rep_uint32, &blocksize_kw
1256 };
1257
1258 static cfg_tuplefielddef_t resppadding_fields[] = {
1259 { "acl", &cfg_type_bracketed_aml, 0 },
1260 { "block-size", &cfg_type_blocksize, 0 },
1261 { NULL, NULL, 0 }
1262 };
1263
1264 static cfg_type_t cfg_type_resppadding = {
1265 "resppadding", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1266 &cfg_rep_tuple, resppadding_fields
1267 };
1268
1269 /*%
1270 * dnstap {
1271 * <message type> [query | response] ;
1272 * ...
1273 * }
1274 *
1275 * ... where message type is one of: client, resolver, auth, forwarder, all
1276 */
1277 static const char *dnstap_types[] = {
1278 "all", "auth", "client", "forwarder", "resolver", NULL
1279 };
1280
1281 static const char *dnstap_modes[] = { "query", "response", NULL };
1282
1283 static cfg_type_t cfg_type_dnstap_type = {
1284 "dnstap_type", cfg_parse_enum, cfg_print_ustring,
1285 cfg_doc_enum, &cfg_rep_string, dnstap_types
1286 };
1287
1288 static cfg_type_t cfg_type_dnstap_mode = {
1289 "dnstap_mode", parse_optional_enum, cfg_print_ustring,
1290 doc_optional_enum, &cfg_rep_string, dnstap_modes
1291 };
1292
1293 static cfg_tuplefielddef_t dnstap_fields[] = {
1294 { "type", &cfg_type_dnstap_type, 0 },
1295 { "mode", &cfg_type_dnstap_mode, 0 },
1296 { NULL, NULL, 0 }
1297 };
1298
1299 static cfg_type_t cfg_type_dnstap_entry = {
1300 "dnstap_value", cfg_parse_tuple, cfg_print_tuple,
1301 cfg_doc_tuple, &cfg_rep_tuple, dnstap_fields
1302 };
1303
1304 static cfg_type_t cfg_type_dnstap = {
1305 "dnstap", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1306 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_dnstap_entry
1307 };
1308
1309 /*%
1310 * dnstap-output
1311 */
1312 static isc_result_t
1313 parse_dtout(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1314 isc_result_t result;
1315 cfg_obj_t *obj = NULL;
1316 const cfg_tuplefielddef_t *fields = type->of;
1317
1318 CHECK(cfg_create_tuple(pctx, type, &obj));
1319
1320 /* Parse the mandatory "mode" and "path" fields */
1321 CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1322 CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1]));
1323
1324 /* Parse "versions" and "size" fields in any order. */
1325 for (;;) {
1326 CHECK(cfg_peektoken(pctx, 0));
1327 if (pctx->token.type == isc_tokentype_string) {
1328 CHECK(cfg_gettoken(pctx, 0));
1329 if (strcasecmp(TOKEN_STRING(pctx),
1330 "size") == 0 &&
1331 obj->value.tuple[2] == NULL)
1332 {
1333 CHECK(cfg_parse_obj(pctx, fields[2].type,
1334 &obj->value.tuple[2]));
1335 } else if (strcasecmp(TOKEN_STRING(pctx),
1336 "versions") == 0 &&
1337 obj->value.tuple[3] == NULL)
1338 {
1339 CHECK(cfg_parse_obj(pctx, fields[3].type,
1340 &obj->value.tuple[3]));
1341 } else if (strcasecmp(TOKEN_STRING(pctx),
1342 "suffix") == 0 &&
1343 obj->value.tuple[4] == NULL) {
1344 CHECK(cfg_parse_obj(pctx, fields[4].type,
1345 &obj->value.tuple[4]));
1346 } else {
1347 cfg_parser_error(pctx, CFG_LOG_NEAR,
1348 "unexpected token");
1349 result = ISC_R_UNEXPECTEDTOKEN;
1350 goto cleanup;
1351 }
1352 } else {
1353 break;
1354 }
1355 }
1356
1357 /* Create void objects for missing optional values. */
1358 if (obj->value.tuple[2] == NULL)
1359 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
1360 if (obj->value.tuple[3] == NULL)
1361 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[3]));
1362 if (obj->value.tuple[4] == NULL)
1363 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[4]));
1364
1365 *ret = obj;
1366 return (ISC_R_SUCCESS);
1367
1368 cleanup:
1369 CLEANUP_OBJ(obj);
1370 return (result);
1371 }
1372
1373 static void
1374 print_dtout(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1375 cfg_print_obj(pctx, obj->value.tuple[0]); /* mode */
1376 cfg_print_obj(pctx, obj->value.tuple[1]); /* file */
1377 if (obj->value.tuple[2]->type->print != cfg_print_void) {
1378 cfg_print_cstr(pctx, " size ");
1379 cfg_print_obj(pctx, obj->value.tuple[2]);
1380 }
1381 if (obj->value.tuple[3]->type->print != cfg_print_void) {
1382 cfg_print_cstr(pctx, " versions ");
1383 cfg_print_obj(pctx, obj->value.tuple[3]);
1384 }
1385 if (obj->value.tuple[4]->type->print != cfg_print_void) {
1386 cfg_print_cstr(pctx, " suffix ");
1387 cfg_print_obj(pctx, obj->value.tuple[4]);
1388 }
1389 }
1390
1391
1392 static void
1393 doc_dtout(cfg_printer_t *pctx, const cfg_type_t *type) {
1394 UNUSED(type);
1395 cfg_print_cstr(pctx, "( file | unix ) <quoted_string>");
1396 cfg_print_cstr(pctx, " ");
1397 cfg_print_cstr(pctx, "[ size ( unlimited | <size> ) ]");
1398 cfg_print_cstr(pctx, " ");
1399 cfg_print_cstr(pctx, "[ versions ( unlimited | <integer> ) ]");
1400 cfg_print_cstr(pctx, " ");
1401 cfg_print_cstr(pctx, "[ suffix ( increment | timestamp ) ]");
1402 }
1403
1404 static const char *dtoutmode_enums[] = { "file", "unix", NULL };
1405 static cfg_type_t cfg_type_dtmode = {
1406 "dtmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
1407 &cfg_rep_string, &dtoutmode_enums
1408 };
1409
1410 static cfg_tuplefielddef_t dtout_fields[] = {
1411 { "mode", &cfg_type_dtmode, 0 },
1412 { "path", &cfg_type_qstring, 0 },
1413 { "size", &cfg_type_sizenodefault, 0 },
1414 { "versions", &cfg_type_logversions, 0 },
1415 { "suffix", &cfg_type_logsuffix, 0 },
1416 { NULL, NULL, 0 }
1417 };
1418
1419 static cfg_type_t cfg_type_dnstapoutput = {
1420 "dnstapoutput", parse_dtout, print_dtout, doc_dtout,
1421 &cfg_rep_tuple, dtout_fields
1422 };
1423
1424 /*%
1425 * response-policy {
1426 * zone <string> [ policy (given|disabled|passthru|drop|tcp-only|
1427 * nxdomain|nodata|cname <domain> ) ]
1428 * [ recursive-only yes|no ] [ log yes|no ]
1429 * [ max-policy-ttl number ]
1430 * [ nsip-enable yes|no ] [ nsdname-enable yes|no ];
1431 * } [ recursive-only yes|no ] [ max-policy-ttl number ]
1432 * [ min-update-interval number ]
1433 * [ break-dnssec yes|no ] [ min-ns-dots number ]
1434 * [ qname-wait-recurse yes|no ]
1435 * [ nsip-enable yes|no ] [ nsdname-enable yes|no ]
1436 * [ dnsrps-enable yes|no ]
1437 * [ dnsrps-options { DNSRPS configuration string } ];
1438 */
1439
1440 static void
1441 doc_rpz_policy(cfg_printer_t *pctx, const cfg_type_t *type) {
1442 const char * const *p;
1443 /*
1444 * This is cfg_doc_enum() without the trailing " )".
1445 */
1446 cfg_print_cstr(pctx, "( ");
1447 for (p = type->of; *p != NULL; p++) {
1448 cfg_print_cstr(pctx, *p);
1449 if (p[1] != NULL)
1450 cfg_print_cstr(pctx, " | ");
1451 }
1452 }
1453
1454 static void
1455 doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) {
1456 cfg_doc_terminal(pctx, type);
1457 cfg_print_cstr(pctx, " )");
1458 }
1459
1460 /*
1461 * Parse
1462 * given|disabled|passthru|drop|tcp-only|nxdomain|nodata|cname <domain>
1463 */
1464 static isc_result_t
1465 cfg_parse_rpz_policy(cfg_parser_t *pctx, const cfg_type_t *type,
1466 cfg_obj_t **ret)
1467 {
1468 isc_result_t result;
1469 cfg_obj_t *obj = NULL;
1470 const cfg_tuplefielddef_t *fields;
1471
1472 CHECK(cfg_create_tuple(pctx, type, &obj));
1473
1474 fields = type->of;
1475 CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1476 /*
1477 * parse cname domain only after "policy cname"
1478 */
1479 if (strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[0])) != 0) {
1480 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
1481 } else {
1482 CHECK(cfg_parse_obj(pctx, fields[1].type,
1483 &obj->value.tuple[1]));
1484 }
1485
1486 *ret = obj;
1487 return (ISC_R_SUCCESS);
1488
1489 cleanup:
1490 CLEANUP_OBJ(obj);
1491 return (result);
1492 }
1493
1494 /*
1495 * Parse a tuple consisting of any kind of required field followed
1496 * by 2 or more optional keyvalues that can be in any order.
1497 */
1498 static isc_result_t
1499 cfg_parse_kv_tuple(cfg_parser_t *pctx, const cfg_type_t *type,
1500 cfg_obj_t **ret)
1501 {
1502 const cfg_tuplefielddef_t *fields, *f;
1503 cfg_obj_t *obj = NULL;
1504 int fn;
1505 isc_result_t result;
1506
1507 CHECK(cfg_create_tuple(pctx, type, &obj));
1508
1509 /*
1510 * The zone first field is required and always first.
1511 */
1512 fields = type->of;
1513 CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1514
1515 for (;;) {
1516 CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1517 if (pctx->token.type != isc_tokentype_string)
1518 break;
1519
1520 for (fn = 1, f = &fields[1]; ; ++fn, ++f) {
1521 if (f->name == NULL) {
1522 cfg_parser_error(pctx, 0, "unexpected '%s'",
1523 TOKEN_STRING(pctx));
1524 result = ISC_R_UNEXPECTEDTOKEN;
1525 goto cleanup;
1526 }
1527 if (obj->value.tuple[fn] == NULL &&
1528 strcasecmp(f->name, TOKEN_STRING(pctx)) == 0)
1529 break;
1530 }
1531
1532 CHECK(cfg_gettoken(pctx, 0));
1533 CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[fn]));
1534 }
1535
1536 for (fn = 1, f = &fields[1]; f->name != NULL; ++fn, ++f) {
1537 if (obj->value.tuple[fn] == NULL)
1538 CHECK(cfg_parse_void(pctx, NULL,
1539 &obj->value.tuple[fn]));
1540 }
1541
1542 *ret = obj;
1543 return (ISC_R_SUCCESS);
1544
1545 cleanup:
1546 CLEANUP_OBJ(obj);
1547 return (result);
1548 }
1549
1550 static void
1551 cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1552 unsigned int i;
1553 const cfg_tuplefielddef_t *fields, *f;
1554 const cfg_obj_t *fieldobj;
1555
1556 fields = obj->type->of;
1557 for (f = fields, i = 0; f->name != NULL; f++, i++) {
1558 fieldobj = obj->value.tuple[i];
1559 if (fieldobj->type->print == cfg_print_void)
1560 continue;
1561 if (i != 0) {
1562 cfg_print_cstr(pctx, " ");
1563 cfg_print_cstr(pctx, f->name);
1564 cfg_print_cstr(pctx, " ");
1565 }
1566 cfg_print_obj(pctx, fieldobj);
1567 }
1568 }
1569
1570 static void
1571 cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
1572 const cfg_tuplefielddef_t *fields, *f;
1573
1574 fields = type->of;
1575 for (f = fields; f->name != NULL; f++) {
1576 if (f != fields) {
1577 cfg_print_cstr(pctx, " [ ");
1578 cfg_print_cstr(pctx, f->name);
1579 if (f->type->doc != cfg_doc_void)
1580 cfg_print_cstr(pctx, " ");
1581 }
1582 cfg_doc_obj(pctx, f->type);
1583 if (f != fields)
1584 cfg_print_cstr(pctx, " ]");
1585 }
1586 }
1587
1588 static keyword_type_t zone_kw = {"zone", &cfg_type_qstring};
1589 static cfg_type_t cfg_type_rpz_zone = {
1590 "zone", parse_keyvalue, print_keyvalue,
1591 doc_keyvalue, &cfg_rep_string,
1592 &zone_kw
1593 };
1594 /*
1595 * "no-op" is an obsolete equivalent of "passthru".
1596 */
1597 static const char *rpz_policies[] = {
1598 "cname", "disabled", "drop", "given", "no-op", "nodata",
1599 "nxdomain", "passthru", "tcp-only", NULL
1600 };
1601 static cfg_type_t cfg_type_rpz_policy_name = {
1602 "policy name", cfg_parse_enum, cfg_print_ustring,
1603 doc_rpz_policy, &cfg_rep_string,
1604 &rpz_policies
1605 };
1606 static cfg_type_t cfg_type_rpz_cname = {
1607 "quoted_string", cfg_parse_astring, NULL,
1608 doc_rpz_cname, &cfg_rep_string,
1609 NULL
1610 };
1611 static cfg_tuplefielddef_t rpz_policy_fields[] = {
1612 { "policy name", &cfg_type_rpz_policy_name, 0 },
1613 { "cname", &cfg_type_rpz_cname, 0 },
1614 { NULL, NULL, 0 }
1615 };
1616 static cfg_type_t cfg_type_rpz_policy = {
1617 "policy tuple", cfg_parse_rpz_policy,
1618 cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1619 rpz_policy_fields
1620 };
1621 static cfg_tuplefielddef_t rpz_zone_fields[] = {
1622 { "zone name", &cfg_type_rpz_zone, 0 },
1623 { "log", &cfg_type_boolean, 0 },
1624 { "max-policy-ttl", &cfg_type_uint32, 0 },
1625 { "min-update-interval", &cfg_type_uint32, 0 },
1626 { "policy", &cfg_type_rpz_policy, 0 },
1627 { "recursive-only", &cfg_type_boolean, 0 },
1628 { "nsip-enable", &cfg_type_boolean, 0 },
1629 { "nsdname-enable", &cfg_type_boolean, 0 },
1630 { NULL, NULL, 0 }
1631 };
1632 static cfg_type_t cfg_type_rpz_tuple = {
1633 "rpz tuple", cfg_parse_kv_tuple,
1634 cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple,
1635 rpz_zone_fields
1636 };
1637 static cfg_type_t cfg_type_rpz_list = {
1638 "zone list", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1639 cfg_doc_bracketed_list, &cfg_rep_list,
1640 &cfg_type_rpz_tuple
1641 };
1642 static cfg_tuplefielddef_t rpz_fields[] = {
1643 { "zone list", &cfg_type_rpz_list, 0 },
1644 { "break-dnssec", &cfg_type_boolean, 0 },
1645 { "max-policy-ttl", &cfg_type_uint32, 0 },
1646 { "min-update-interval", &cfg_type_uint32, 0 },
1647 { "min-ns-dots", &cfg_type_uint32, 0 },
1648 { "nsip-wait-recurse", &cfg_type_boolean, 0 },
1649 { "qname-wait-recurse", &cfg_type_boolean, 0 },
1650 { "recursive-only", &cfg_type_boolean, 0 },
1651 { "nsip-enable", &cfg_type_boolean, 0 },
1652 { "nsdname-enable", &cfg_type_boolean, 0 },
1653 #ifdef USE_DNSRPS
1654 { "dnsrps-enable", &cfg_type_boolean, 0 },
1655 { "dnsrps-options", &cfg_type_bracketed_text, 0 },
1656 #else
1657 { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
1658 { "dnsrps-options", &cfg_type_bracketed_text,
1659 CFG_CLAUSEFLAG_NOTCONFIGURED },
1660 #endif
1661 { NULL, NULL, 0 }
1662 };
1663 static cfg_type_t cfg_type_rpz = {
1664 "rpz", cfg_parse_kv_tuple,
1665 cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple,
1666 rpz_fields
1667 };
1668
1669 /*
1670 * Catalog zones
1671 */
1672 static cfg_type_t cfg_type_catz_zone = {
1673 "zone", parse_keyvalue, print_keyvalue,
1674 doc_keyvalue, &cfg_rep_string,
1675 &zone_kw
1676 };
1677
1678 static cfg_tuplefielddef_t catz_zone_fields[] = {
1679 { "zone name", &cfg_type_catz_zone, 0 },
1680 { "default-masters", &cfg_type_namesockaddrkeylist, 0 },
1681 { "zone-directory", &cfg_type_qstring, 0 },
1682 { "in-memory", &cfg_type_boolean, 0 },
1683 { "min-update-interval", &cfg_type_uint32, 0 },
1684 { NULL, NULL, 0 }
1685 };
1686 static cfg_type_t cfg_type_catz_tuple = {
1687 "catz tuple", cfg_parse_kv_tuple,
1688 cfg_print_kv_tuple, cfg_doc_kv_tuple, &cfg_rep_tuple,
1689 catz_zone_fields
1690 };
1691 static cfg_type_t cfg_type_catz_list = {
1692 "zone list", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1693 cfg_doc_bracketed_list, &cfg_rep_list,
1694 &cfg_type_catz_tuple
1695 };
1696 static cfg_tuplefielddef_t catz_fields[] = {
1697 { "zone list", &cfg_type_catz_list, 0 },
1698 { NULL, NULL, 0 }
1699 };
1700 static cfg_type_t cfg_type_catz = {
1701 "catz", cfg_parse_kv_tuple, cfg_print_kv_tuple,
1702 cfg_doc_kv_tuple, &cfg_rep_tuple, catz_fields
1703 };
1704
1705 /*
1706 * rate-limit
1707 */
1708 static cfg_clausedef_t rrl_clauses[] = {
1709 { "all-per-second", &cfg_type_uint32, 0 },
1710 { "errors-per-second", &cfg_type_uint32, 0 },
1711 { "exempt-clients", &cfg_type_bracketed_aml, 0 },
1712 { "ipv4-prefix-length", &cfg_type_uint32, 0 },
1713 { "ipv6-prefix-length", &cfg_type_uint32, 0 },
1714 { "log-only", &cfg_type_boolean, 0 },
1715 { "max-table-size", &cfg_type_uint32, 0 },
1716 { "min-table-size", &cfg_type_uint32, 0 },
1717 { "nodata-per-second", &cfg_type_uint32, 0 },
1718 { "nxdomains-per-second", &cfg_type_uint32, 0 },
1719 { "qps-scale", &cfg_type_uint32, 0 },
1720 { "referrals-per-second", &cfg_type_uint32, 0 },
1721 { "responses-per-second", &cfg_type_uint32, 0 },
1722 { "slip", &cfg_type_uint32, 0 },
1723 { "window", &cfg_type_uint32, 0 },
1724 { NULL, NULL, 0 }
1725 };
1726
1727 static cfg_clausedef_t *rrl_clausesets[] = {
1728 rrl_clauses, NULL
1729 };
1730
1731 static cfg_type_t cfg_type_rrl = {
1732 "rate-limit", cfg_parse_map, cfg_print_map, cfg_doc_map,
1733 &cfg_rep_map, rrl_clausesets
1734 };
1735
1736 /*%
1737 * dnssec-lookaside
1738 */
1739
1740 static void
1741 print_lookaside(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1742 const cfg_obj_t *domain = obj->value.tuple[0];
1743
1744 if (domain->value.string.length == 4 &&
1745 strncmp(domain->value.string.base, "auto", 4) == 0)
1746 cfg_print_cstr(pctx, "auto");
1747 else
1748 cfg_print_tuple(pctx, obj);
1749 }
1750
1751 static void
1752 doc_lookaside(cfg_printer_t *pctx, const cfg_type_t *type) {
1753 UNUSED(type);
1754 cfg_print_cstr(pctx, "( <string> trust-anchor <string> | auto | no )");
1755 }
1756
1757 static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring };
1758
1759 static cfg_type_t cfg_type_optional_trustanchor = {
1760 "optional_trustanchor", parse_optional_keyvalue, print_keyvalue,
1761 doc_keyvalue, &cfg_rep_string, &trustanchor_kw
1762 };
1763
1764 static cfg_tuplefielddef_t lookaside_fields[] = {
1765 { "domain", &cfg_type_astring, 0 },
1766 { "trust-anchor", &cfg_type_optional_trustanchor, 0 },
1767 { NULL, NULL, 0 }
1768 };
1769
1770 static cfg_type_t cfg_type_lookaside = {
1771 "lookaside", cfg_parse_tuple, print_lookaside, doc_lookaside,
1772 &cfg_rep_tuple, lookaside_fields
1773 };
1774
1775 static isc_result_t
1776 parse_optional_uint32(cfg_parser_t *pctx, const cfg_type_t *type,
1777 cfg_obj_t **ret)
1778 {
1779 isc_result_t result;
1780 UNUSED(type);
1781
1782 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
1783 if (pctx->token.type == isc_tokentype_number) {
1784 CHECK(cfg_parse_obj(pctx, &cfg_type_uint32, ret));
1785 } else {
1786 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1787 }
1788 cleanup:
1789 return (result);
1790 }
1791
1792 static void
1793 doc_optional_uint32(cfg_printer_t *pctx, const cfg_type_t *type) {
1794 UNUSED(type);
1795 cfg_print_cstr(pctx, "[ <integer> ]");
1796 }
1797
1798 static cfg_type_t cfg_type_optional_uint32 = {
1799 "optional_uint32", parse_optional_uint32, NULL, doc_optional_uint32,
1800 NULL, NULL
1801 };
1802
1803 static cfg_tuplefielddef_t prefetch_fields[] = {
1804 { "trigger", &cfg_type_uint32, 0 },
1805 { "eligible", &cfg_type_optional_uint32, 0 },
1806 { NULL, NULL, 0 }
1807 };
1808
1809 static cfg_type_t cfg_type_prefetch = {
1810 "prefetch", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1811 &cfg_rep_tuple, prefetch_fields
1812 };
1813 /*
1814 * DNS64.
1815 */
1816 static cfg_clausedef_t
1817 dns64_clauses[] = {
1818 { "break-dnssec", &cfg_type_boolean, 0 },
1819 { "clients", &cfg_type_bracketed_aml, 0 },
1820 { "exclude", &cfg_type_bracketed_aml, 0 },
1821 { "mapped", &cfg_type_bracketed_aml, 0 },
1822 { "recursive-only", &cfg_type_boolean, 0 },
1823 { "suffix", &cfg_type_netaddr6, 0 },
1824 { NULL, NULL, 0 },
1825 };
1826
1827 static cfg_clausedef_t *
1828 dns64_clausesets[] = {
1829 dns64_clauses,
1830 NULL
1831 };
1832
1833 static cfg_type_t cfg_type_dns64 = {
1834 "dns64", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map,
1835 &cfg_rep_map, dns64_clausesets
1836 };
1837
1838 /*%
1839 * Clauses that can be found within the 'view' statement,
1840 * with defaults in the 'options' statement.
1841 */
1842
1843 static cfg_clausedef_t
1844 view_clauses[] = {
1845 { "acache-cleaning-interval", &cfg_type_uint32,
1846 CFG_CLAUSEFLAG_OBSOLETE },
1847 { "acache-enable", &cfg_type_boolean,
1848 CFG_CLAUSEFLAG_OBSOLETE },
1849 { "additional-from-auth", &cfg_type_boolean,
1850 CFG_CLAUSEFLAG_OBSOLETE },
1851 { "additional-from-cache", &cfg_type_boolean,
1852 CFG_CLAUSEFLAG_OBSOLETE },
1853 { "allow-new-zones", &cfg_type_boolean, 0 },
1854 { "allow-query-cache", &cfg_type_bracketed_aml, 0 },
1855 { "allow-query-cache-on", &cfg_type_bracketed_aml, 0 },
1856 { "allow-recursion", &cfg_type_bracketed_aml, 0 },
1857 { "allow-recursion-on", &cfg_type_bracketed_aml, 0 },
1858 { "allow-v6-synthesis", &cfg_type_bracketed_aml,
1859 CFG_CLAUSEFLAG_OBSOLETE },
1860 { "attach-cache", &cfg_type_astring, 0 },
1861 { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
1862 { "cache-file", &cfg_type_qstring, 0 },
1863 { "catalog-zones", &cfg_type_catz, 0 },
1864 { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
1865 { "cleaning-interval", &cfg_type_uint32, 0 },
1866 { "clients-per-query", &cfg_type_uint32, 0 },
1867 { "deny-answer-addresses", &cfg_type_denyaddresses, 0 },
1868 { "deny-answer-aliases", &cfg_type_denyaliases, 0 },
1869 { "disable-algorithms", &cfg_type_disablealgorithm,
1870 CFG_CLAUSEFLAG_MULTI },
1871 { "disable-ds-digests", &cfg_type_disabledsdigest,
1872 CFG_CLAUSEFLAG_MULTI },
1873 { "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
1874 { "dns64", &cfg_type_dns64, CFG_CLAUSEFLAG_MULTI },
1875 { "dns64-contact", &cfg_type_astring, 0 },
1876 { "dns64-server", &cfg_type_astring, 0 },
1877 #ifdef USE_DNSRPS
1878 { "dnsrps-enable", &cfg_type_boolean, 0 },
1879 { "dnsrps-options", &cfg_type_bracketed_text, 0 },
1880 #else
1881 { "dnsrps-enable", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTCONFIGURED },
1882 { "dnsrps-options", &cfg_type_bracketed_text,
1883 CFG_CLAUSEFLAG_NOTCONFIGURED },
1884 #endif
1885 { "dnssec-accept-expired", &cfg_type_boolean, 0 },
1886 { "dnssec-enable", &cfg_type_boolean, 0 },
1887 { "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI },
1888 { "dnssec-must-be-secure", &cfg_type_mustbesecure,
1889 CFG_CLAUSEFLAG_MULTI },
1890 { "dnssec-validation", &cfg_type_boolorauto, 0 },
1891 #ifdef HAVE_DNSTAP
1892 { "dnstap", &cfg_type_dnstap, 0 },
1893 #else
1894 { "dnstap", &cfg_type_dnstap, CFG_CLAUSEFLAG_NOTCONFIGURED },
1895 #endif /* HAVE_DNSTAP */
1896 { "dual-stack-servers", &cfg_type_nameportiplist, 0 },
1897 { "edns-udp-size", &cfg_type_uint32, 0 },
1898 { "empty-contact", &cfg_type_astring, 0 },
1899 { "empty-server", &cfg_type_astring, 0 },
1900 { "empty-zones-enable", &cfg_type_boolean, 0 },
1901 { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1902 { "fetch-quota-params", &cfg_type_fetchquota, 0 },
1903 { "fetches-per-server", &cfg_type_fetchesper, 0 },
1904 { "fetches-per-zone", &cfg_type_fetchesper, 0 },
1905 { "filter-aaaa", &cfg_type_bracketed_aml, 0 },
1906 { "filter-aaaa-on-v4", &cfg_type_filter_aaaa, 0 },
1907 { "filter-aaaa-on-v6", &cfg_type_filter_aaaa, 0 },
1908 { "glue-cache", &cfg_type_boolean, 0 },
1909 { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
1910 { "lame-ttl", &cfg_type_ttlval, 0 },
1911 #ifdef HAVE_LMDB
1912 { "lmdb-mapsize", &cfg_type_sizeval, 0 },
1913 #else
1914 { "lmdb-mapsize", &cfg_type_sizeval, CFG_CLAUSEFLAG_NOOP },
1915 #endif
1916 { "max-acache-size", &cfg_type_sizenodefault,
1917 CFG_CLAUSEFLAG_OBSOLETE },
1918 { "max-cache-size", &cfg_type_sizeorpercent, 0 },
1919 { "max-cache-ttl", &cfg_type_uint32, 0 },
1920 { "max-clients-per-query", &cfg_type_uint32, 0 },
1921 { "max-ncache-ttl", &cfg_type_uint32, 0 },
1922 { "max-recursion-depth", &cfg_type_uint32, 0 },
1923 { "max-recursion-queries", &cfg_type_uint32, 0 },
1924 { "max-stale-ttl", &cfg_type_ttlval, 0 },
1925 { "max-udp-size", &cfg_type_uint32, 0 },
1926 { "message-compression", &cfg_type_boolean, 0 },
1927 { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
1928 { "minimal-any", &cfg_type_boolean, 0 },
1929 { "minimal-responses", &cfg_type_minimal, 0 },
1930 { "new-zones-directory", &cfg_type_qstring, 0 },
1931 { "no-case-compress", &cfg_type_bracketed_aml, 0 },
1932 { "nocookie-udp-size", &cfg_type_uint32, 0 },
1933 { "nosit-udp-size", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
1934 { "nta-lifetime", &cfg_type_ttlval, 0 },
1935 { "nta-recheck", &cfg_type_ttlval, 0 },
1936 { "nxdomain-redirect", &cfg_type_astring, 0 },
1937 { "preferred-glue", &cfg_type_astring, 0 },
1938 { "prefetch", &cfg_type_prefetch, 0 },
1939 { "provide-ixfr", &cfg_type_boolean, 0 },
1940 /*
1941 * Note that the query-source option syntax is different
1942 * from the other -source options.
1943 */
1944 { "query-source", &cfg_type_querysource4, 0 },
1945 { "query-source-v6", &cfg_type_querysource6, 0 },
1946 { "queryport-pool-ports", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
1947 { "queryport-pool-updateinterval", &cfg_type_uint32,
1948 CFG_CLAUSEFLAG_OBSOLETE },
1949 { "rate-limit", &cfg_type_rrl, 0 },
1950 { "recursion", &cfg_type_boolean, 0 },
1951 { "request-nsid", &cfg_type_boolean, 0 },
1952 { "request-sit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1953 { "require-server-cookie", &cfg_type_boolean, 0 },
1954 { "resolver-nonbackoff-tries", &cfg_type_uint32, 0 },
1955 { "resolver-query-timeout", &cfg_type_uint32, 0 },
1956 { "resolver-retry-interval", &cfg_type_uint32, 0 },
1957 { "response-padding", &cfg_type_resppadding, 0 },
1958 { "response-policy", &cfg_type_rpz, 0 },
1959 { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
1960 { "root-delegation-only", &cfg_type_optional_exclude, 0 },
1961 { "root-key-sentinel", &cfg_type_boolean, 0 },
1962 { "rrset-order", &cfg_type_rrsetorder, 0 },
1963 { "send-cookie", &cfg_type_boolean, 0 },
1964 { "servfail-ttl", &cfg_type_ttlval, 0 },
1965 { "sortlist", &cfg_type_bracketed_aml, 0 },
1966 { "stale-answer-enable", &cfg_type_boolean, 0 },
1967 { "stale-answer-ttl", &cfg_type_ttlval, 0 },
1968 { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
1969 { "synth-from-dnssec", &cfg_type_boolean, 0 },
1970 { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
1971 { "transfer-format", &cfg_type_transferformat, 0 },
1972 { "trust-anchor-telemetry", &cfg_type_boolean,
1973 CFG_CLAUSEFLAG_EXPERIMENTAL },
1974 { "use-queryport-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1975 { "v6-bias", &cfg_type_uint32, 0 },
1976 { "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
1977 { NULL, NULL, 0 }
1978 };
1979
1980 /*%
1981 * Clauses that can be found within the 'view' statement only.
1982 */
1983 static cfg_clausedef_t
1984 view_only_clauses[] = {
1985 { "match-clients", &cfg_type_bracketed_aml, 0 },
1986 { "match-destinations", &cfg_type_bracketed_aml, 0 },
1987 { "match-recursive-only", &cfg_type_boolean, 0 },
1988 { NULL, NULL, 0 }
1989 };
1990
1991 /*%
1992 * Sig-validity-interval.
1993 */
1994
1995 static cfg_tuplefielddef_t validityinterval_fields[] = {
1996 { "validity", &cfg_type_uint32, 0 },
1997 { "re-sign", &cfg_type_optional_uint32, 0 },
1998 { NULL, NULL, 0 }
1999 };
2000
2001 static cfg_type_t cfg_type_validityinterval = {
2002 "validityinterval", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2003 &cfg_rep_tuple, validityinterval_fields
2004 };
2005
2006 /*%
2007 * Clauses that can be found in a 'zone' statement,
2008 * with defaults in the 'view' or 'options' statement.
2009 *
2010 * Note: CFG_ZONE_* options indicate in which zone types this clause is
2011 * legal.
2012 */
2013 static cfg_clausedef_t
2014 zone_clauses[] = {
2015 { "allow-notify", &cfg_type_bracketed_aml,
2016 CFG_ZONE_SLAVE
2017 },
2018 { "allow-query", &cfg_type_bracketed_aml,
2019 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2020 CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB
2021 },
2022 { "allow-query-on", &cfg_type_bracketed_aml,
2023 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2024 CFG_ZONE_REDIRECT | CFG_ZONE_STATICSTUB
2025 },
2026 { "allow-transfer", &cfg_type_bracketed_aml,
2027 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2028 },
2029 { "allow-update", &cfg_type_bracketed_aml,
2030 CFG_ZONE_MASTER
2031 },
2032 { "allow-update-forwarding", &cfg_type_bracketed_aml,
2033 CFG_ZONE_SLAVE
2034 },
2035 { "also-notify", &cfg_type_namesockaddrkeylist,
2036 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2037 },
2038 { "alt-transfer-source", &cfg_type_sockaddr4wild,
2039 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2040 },
2041 { "alt-transfer-source-v6", &cfg_type_sockaddr6wild,
2042 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2043 },
2044 { "auto-dnssec", &cfg_type_autodnssec,
2045 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2046 },
2047 { "check-dup-records", &cfg_type_checkmode,
2048 CFG_ZONE_MASTER
2049 },
2050 { "check-integrity", &cfg_type_boolean,
2051 CFG_ZONE_MASTER
2052 },
2053 { "check-mx", &cfg_type_checkmode,
2054 CFG_ZONE_MASTER
2055 },
2056 { "check-mx-cname", &cfg_type_checkmode,
2057 CFG_ZONE_MASTER
2058 },
2059 { "check-sibling", &cfg_type_boolean,
2060 CFG_ZONE_MASTER
2061 },
2062 { "check-spf", &cfg_type_warn,
2063 CFG_ZONE_MASTER
2064 },
2065 { "check-srv-cname", &cfg_type_checkmode,
2066 CFG_ZONE_MASTER
2067 },
2068 { "check-wildcard", &cfg_type_boolean,
2069 CFG_ZONE_MASTER
2070 },
2071 { "dialup", &cfg_type_dialuptype,
2072 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB
2073 },
2074 { "dnssec-dnskey-kskonly", &cfg_type_boolean,
2075 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2076 },
2077 { "dnssec-loadkeys-interval", &cfg_type_uint32,
2078 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2079 },
2080 { "dnssec-secure-to-insecure", &cfg_type_boolean,
2081 CFG_ZONE_MASTER
2082 },
2083 { "dnssec-update-mode", &cfg_type_dnssecupdatemode,
2084 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2085 },
2086 { "forward", &cfg_type_forwardtype,
2087 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2088 CFG_ZONE_STATICSTUB | CFG_ZONE_FORWARD
2089 },
2090 { "forwarders", &cfg_type_portiplist,
2091 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2092 CFG_ZONE_STATICSTUB | CFG_ZONE_FORWARD
2093 },
2094 { "inline-signing", &cfg_type_boolean,
2095 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2096 },
2097 { "key-directory", &cfg_type_qstring,
2098 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2099 },
2100 { "maintain-ixfr-base", &cfg_type_boolean,
2101 CFG_CLAUSEFLAG_OBSOLETE
2102 },
2103 { "masterfile-format", &cfg_type_masterformat,
2104 CFG_ZONE_MASTER | CFG_ZONE_SLAVE |
2105 CFG_ZONE_STUB | CFG_ZONE_REDIRECT
2106 },
2107 { "masterfile-style", &cfg_type_masterstyle,
2108 CFG_ZONE_MASTER | CFG_ZONE_SLAVE |
2109 CFG_ZONE_STUB | CFG_ZONE_REDIRECT
2110 },
2111 { "max-ixfr-log-size", &cfg_type_size,
2112 CFG_CLAUSEFLAG_OBSOLETE
2113 },
2114 { "max-journal-size", &cfg_type_size,
2115 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2116 },
2117 { "max-records", &cfg_type_uint32,
2118 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2119 CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT
2120 },
2121 { "max-refresh-time", &cfg_type_uint32,
2122 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2123 },
2124 { "max-retry-time", &cfg_type_uint32,
2125 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2126 },
2127 { "max-transfer-idle-in", &cfg_type_uint32,
2128 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2129 },
2130 { "max-transfer-idle-out", &cfg_type_uint32,
2131 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2132 },
2133 { "max-transfer-time-in", &cfg_type_uint32,
2134 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2135 },
2136 { "max-transfer-time-out", &cfg_type_uint32,
2137 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2138 },
2139 { "max-zone-ttl", &cfg_type_maxttl,
2140 CFG_ZONE_MASTER | CFG_ZONE_REDIRECT
2141 },
2142 { "min-refresh-time", &cfg_type_uint32,
2143 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2144 },
2145 { "min-retry-time", &cfg_type_uint32,
2146 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2147 },
2148 { "multi-master", &cfg_type_boolean,
2149 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2150 },
2151 { "notify", &cfg_type_notifytype,
2152 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2153 },
2154 { "notify-delay", &cfg_type_uint32,
2155 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2156 },
2157 { "notify-source", &cfg_type_sockaddr4wild,
2158 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2159 },
2160 { "notify-source-v6", &cfg_type_sockaddr6wild,
2161 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2162 },
2163 { "notify-to-soa", &cfg_type_boolean,
2164 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2165 },
2166 { "nsec3-test-zone", &cfg_type_boolean,
2167 CFG_CLAUSEFLAG_TESTONLY |
2168 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2169 },
2170 { "request-expire", &cfg_type_boolean,
2171 CFG_ZONE_SLAVE
2172 },
2173 { "request-ixfr", &cfg_type_boolean,
2174 CFG_ZONE_SLAVE
2175 },
2176 { "serial-update-method", &cfg_type_updatemethod,
2177 CFG_ZONE_MASTER
2178 },
2179 { "sig-signing-nodes", &cfg_type_uint32,
2180 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2181 },
2182 { "sig-signing-signatures", &cfg_type_uint32,
2183 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2184 },
2185 { "sig-signing-type", &cfg_type_uint32,
2186 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2187 },
2188 { "sig-validity-interval", &cfg_type_validityinterval,
2189 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2190 },
2191 { "transfer-source", &cfg_type_sockaddr4wild,
2192 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2193 },
2194 { "transfer-source-v6", &cfg_type_sockaddr6wild,
2195 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2196 },
2197 { "try-tcp-refresh", &cfg_type_boolean,
2198 CFG_ZONE_SLAVE
2199 },
2200 { "update-check-ksk", &cfg_type_boolean,
2201 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2202 },
2203 { "use-alt-transfer-source", &cfg_type_boolean,
2204 CFG_ZONE_SLAVE | CFG_ZONE_STUB
2205 },
2206 { "zero-no-soa-ttl", &cfg_type_boolean,
2207 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2208 },
2209 { "zone-statistics", &cfg_type_zonestat,
2210 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2211 CFG_ZONE_STATICSTUB | CFG_ZONE_REDIRECT
2212 },
2213 { NULL, NULL, 0 }
2214 };
2215
2216 /*%
2217 * Clauses that can be found in a 'zone' statement only.
2218 *
2219 * Note: CFG_ZONE_* options indicate in which zone types this clause is
2220 * legal.
2221 */
2222 static cfg_clausedef_t
2223 zone_only_clauses[] = {
2224 /*
2225 * Note that the format of the check-names option is different between
2226 * the zone options and the global/view options. Ugh.
2227 */
2228 { "type", &cfg_type_zonetype,
2229 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2230 CFG_ZONE_STATICSTUB | CFG_ZONE_DELEGATION | CFG_ZONE_HINT |
2231 CFG_ZONE_REDIRECT | CFG_ZONE_FORWARD
2232 },
2233 { "check-names", &cfg_type_checkmode,
2234 CFG_ZONE_MASTER | CFG_ZONE_SLAVE |
2235 CFG_ZONE_HINT | CFG_ZONE_STUB
2236 },
2237 { "database", &cfg_type_astring,
2238 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB
2239 },
2240 { "delegation-only", &cfg_type_boolean,
2241 CFG_ZONE_HINT | CFG_ZONE_STUB | CFG_ZONE_FORWARD
2242 },
2243 { "dlz", &cfg_type_astring,
2244 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_REDIRECT
2245 },
2246 { "file", &cfg_type_qstring,
2247 CFG_ZONE_MASTER | CFG_ZONE_SLAVE | CFG_ZONE_STUB |
2248 CFG_ZONE_HINT | CFG_ZONE_REDIRECT
2249 },
2250 { "in-view", &cfg_type_astring,
2251 CFG_ZONE_INVIEW
2252 },
2253 { "ixfr-base", &cfg_type_qstring,
2254 CFG_CLAUSEFLAG_OBSOLETE
2255 },
2256 { "ixfr-from-differences", &cfg_type_boolean,
2257 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2258 },
2259 { "ixfr-tmp-file", &cfg_type_qstring,
2260 CFG_CLAUSEFLAG_OBSOLETE
2261 },
2262 { "journal", &cfg_type_qstring,
2263 CFG_ZONE_MASTER | CFG_ZONE_SLAVE
2264 },
2265 { "masters", &cfg_type_namesockaddrkeylist,
2266 CFG_ZONE_SLAVE | CFG_ZONE_STUB | CFG_ZONE_REDIRECT
2267 },
2268 { "pubkey", &cfg_type_pubkey,
2269 CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE
2270 },
2271 { "server-addresses", &cfg_type_bracketed_sockaddrlist,
2272 CFG_ZONE_STATICSTUB
2273 },
2274 { "server-names", &cfg_type_namelist,
2275 CFG_ZONE_STATICSTUB
2276 },
2277 { "update-policy", &cfg_type_updatepolicy,
2278 CFG_ZONE_MASTER
2279 },
2280 { NULL, NULL, 0 }
2281 };
2282
2283 /*% The top-level named.conf syntax. */
2284
2285 static cfg_clausedef_t *
2286 namedconf_clausesets[] = {
2287 namedconf_clauses,
2288 namedconf_or_view_clauses,
2289 NULL
2290 };
2291 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
2292 "namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2293 &cfg_rep_map, namedconf_clausesets
2294 };
2295
2296 /*% The bind.keys syntax (trusted-keys/managed-keys only). */
2297 static cfg_clausedef_t *
2298 bindkeys_clausesets[] = {
2299 bindkeys_clauses,
2300 NULL
2301 };
2302 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_bindkeys = {
2303 "bindkeys", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2304 &cfg_rep_map, bindkeys_clausesets
2305 };
2306
2307 /*% The "options" statement syntax. */
2308
2309 static cfg_clausedef_t *
2310 options_clausesets[] = {
2311 options_clauses,
2312 view_clauses,
2313 zone_clauses,
2314 NULL
2315 };
2316 static cfg_type_t cfg_type_options = {
2317 "options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
2318 options_clausesets
2319 };
2320
2321 /*% The "view" statement syntax. */
2322
2323 static cfg_clausedef_t *
2324 view_clausesets[] = {
2325 view_only_clauses,
2326 namedconf_or_view_clauses,
2327 view_clauses,
2328 zone_clauses,
2329 NULL
2330 };
2331
2332 static cfg_type_t cfg_type_viewopts = {
2333 "view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
2334 view_clausesets
2335 };
2336
2337 /*% The "zone" statement syntax. */
2338
2339 static cfg_clausedef_t *
2340 zone_clausesets[] = {
2341 zone_only_clauses,
2342 zone_clauses,
2343 NULL
2344 };
2345 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_zoneopts = {
2346 "zoneopts", cfg_parse_map, cfg_print_map,
2347 cfg_doc_map, &cfg_rep_map, zone_clausesets };
2348
2349 /*% The "dynamically loadable zones" statement syntax. */
2350
2351 static cfg_clausedef_t
2352 dlz_clauses[] = {
2353 { "database", &cfg_type_astring, 0 },
2354 { "search", &cfg_type_boolean, 0 },
2355 { NULL, NULL, 0 }
2356 };
2357 static cfg_clausedef_t *
2358 dlz_clausesets[] = {
2359 dlz_clauses,
2360 NULL
2361 };
2362 static cfg_type_t cfg_type_dlz = {
2363 "dlz", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
2364 &cfg_rep_map, dlz_clausesets
2365 };
2366
2367 /*%
2368 * The "dyndb" statement syntax.
2369 */
2370
2371 static cfg_tuplefielddef_t dyndb_fields[] = {
2372 { "name", &cfg_type_astring, 0 },
2373 { "library", &cfg_type_qstring, 0 },
2374 { "parameters", &cfg_type_bracketed_text, 0 },
2375 { NULL, NULL, 0 }
2376 };
2377
2378 static cfg_type_t cfg_type_dyndb = {
2379 "dyndb", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2380 &cfg_rep_tuple, dyndb_fields
2381 };
2382
2383 /*%
2384 * Clauses that can be found within the 'key' statement.
2385 */
2386 static cfg_clausedef_t
2387 key_clauses[] = {
2388 { "algorithm", &cfg_type_astring, 0 },
2389 { "secret", &cfg_type_sstring, 0 },
2390 { NULL, NULL, 0 }
2391 };
2392
2393 static cfg_clausedef_t *
2394 key_clausesets[] = {
2395 key_clauses,
2396 NULL
2397 };
2398 static cfg_type_t cfg_type_key = {
2399 "key", cfg_parse_named_map, cfg_print_map,
2400 cfg_doc_map, &cfg_rep_map, key_clausesets
2401 };
2402
2403
2404 /*%
2405 * Clauses that can be found in a 'server' statement.
2406 */
2407 static cfg_clausedef_t
2408 server_clauses[] = {
2409 { "bogus", &cfg_type_boolean, 0 },
2410 { "edns", &cfg_type_boolean, 0 },
2411 { "edns-udp-size", &cfg_type_uint32, 0 },
2412 { "edns-version", &cfg_type_uint32, 0 },
2413 { "keys", &cfg_type_server_key_kludge, 0 },
2414 { "max-udp-size", &cfg_type_uint32, 0 },
2415 { "notify-source", &cfg_type_sockaddr4wild, 0 },
2416 { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
2417 { "padding", &cfg_type_uint32, 0 },
2418 { "provide-ixfr", &cfg_type_boolean, 0 },
2419 { "query-source", &cfg_type_querysource4, 0 },
2420 { "query-source-v6", &cfg_type_querysource6, 0 },
2421 { "request-expire", &cfg_type_boolean, 0 },
2422 { "request-ixfr", &cfg_type_boolean, 0 },
2423 { "request-nsid", &cfg_type_boolean, 0 },
2424 { "request-sit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2425 { "send-cookie", &cfg_type_boolean, 0 },
2426 { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
2427 { "tcp-keepalive", &cfg_type_boolean, 0 },
2428 { "tcp-only", &cfg_type_boolean, 0 },
2429 { "transfer-format", &cfg_type_transferformat, 0 },
2430 { "transfer-source", &cfg_type_sockaddr4wild, 0 },
2431 { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
2432 { "transfers", &cfg_type_uint32, 0 },
2433 { NULL, NULL, 0 }
2434 };
2435 static cfg_clausedef_t *
2436 server_clausesets[] = {
2437 server_clauses,
2438 NULL
2439 };
2440 static cfg_type_t cfg_type_server = {
2441 "server", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map,
2442 &cfg_rep_map, server_clausesets
2443 };
2444
2445 /*%
2446 * Clauses that can be found in a 'channel' clause in the
2447 * 'logging' statement.
2448 *
2449 * These have some additional constraints that need to be
2450 * checked after parsing:
2451 * - There must exactly one of file/syslog/null/stderr
2452 */
2453
2454 static const char *printtime_enums[] = {
2455 "iso8601", "iso8601-utc", "local", NULL
2456 };
2457 static isc_result_t
2458 parse_printtime(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2459 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2460 }
2461 static void
2462 doc_printtime(cfg_printer_t *pctx, const cfg_type_t *type) {
2463 doc_enum_or_other(pctx, type, &cfg_type_boolean);
2464 }
2465 static cfg_type_t cfg_type_printtime = {
2466 "printtime", parse_printtime, cfg_print_ustring, doc_printtime,
2467 &cfg_rep_string, printtime_enums
2468 };
2469
2470 static cfg_clausedef_t
2471 channel_clauses[] = {
2472 /* Destinations. We no longer require these to be first. */
2473 { "file", &cfg_type_logfile, 0 },
2474 { "syslog", &cfg_type_optional_facility, 0 },
2475 { "null", &cfg_type_void, 0 },
2476 { "stderr", &cfg_type_void, 0 },
2477 /* Options. We now accept these for the null channel, too. */
2478 { "severity", &cfg_type_logseverity, 0 },
2479 { "print-time", &cfg_type_printtime, 0 },
2480 { "print-severity", &cfg_type_boolean, 0 },
2481 { "print-category", &cfg_type_boolean, 0 },
2482 { "buffered", &cfg_type_boolean, 0 },
2483 { NULL, NULL, 0 }
2484 };
2485 static cfg_clausedef_t *
2486 channel_clausesets[] = {
2487 channel_clauses,
2488 NULL
2489 };
2490 static cfg_type_t cfg_type_channel = {
2491 "channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
2492 &cfg_rep_map, channel_clausesets
2493 };
2494
2495 /*% A list of log destination, used in the "category" clause. */
2496 static cfg_type_t cfg_type_destinationlist = {
2497 "destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
2498 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring
2499 };
2500
2501 /*%
2502 * Clauses that can be found in a 'logging' statement.
2503 */
2504 static cfg_clausedef_t logging_clauses[] = {
2505 { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
2506 { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
2507 { NULL, NULL, 0 }
2508 };
2509 static cfg_clausedef_t * logging_clausesets[] = {
2510 logging_clauses, NULL
2511 };
2512 static cfg_type_t cfg_type_logging = {
2513 "logging", cfg_parse_map, cfg_print_map, cfg_doc_map,
2514 &cfg_rep_map, logging_clausesets
2515 };
2516
2517 /*%
2518 * For parsing an 'addzone' statement
2519 */
2520 static cfg_tuplefielddef_t addzone_fields[] = {
2521 { "name", &cfg_type_astring, 0 },
2522 { "class", &cfg_type_optional_class, 0 },
2523 { "view", &cfg_type_optional_class, 0 },
2524 { "options", &cfg_type_zoneopts, 0 },
2525 { NULL, NULL, 0 }
2526 };
2527 static cfg_type_t cfg_type_addzone = {
2528 "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2529 &cfg_rep_tuple, addzone_fields
2530 };
2531
2532 static cfg_clausedef_t
2533 addzoneconf_clauses[] = {
2534 { "zone", &cfg_type_addzone, CFG_CLAUSEFLAG_MULTI },
2535 { NULL, NULL, 0 }
2536 };
2537
2538 static cfg_clausedef_t *
2539 addzoneconf_clausesets[] = {
2540 addzoneconf_clauses,
2541 NULL
2542 };
2543
2544 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_addzoneconf = {
2545 "addzoneconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
2546 &cfg_rep_map, addzoneconf_clausesets
2547 };
2548
2549 static isc_result_t
2550 parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
2551 char *endp;
2552 unsigned int len;
2553 isc_uint64_t value;
2554 isc_uint64_t unit;
2555
2556 value = isc_string_touint64(str, &endp, 10);
2557 if (*endp == 0) {
2558 *valuep = value;
2559 return (ISC_R_SUCCESS);
2560 }
2561
2562 len = strlen(str);
2563 if (len < 2 || endp[1] != '\0')
2564 return (ISC_R_FAILURE);
2565
2566 switch (str[len - 1]) {
2567 case 'k':
2568 case 'K':
2569 unit = 1024;
2570 break;
2571 case 'm':
2572 case 'M':
2573 unit = 1024 * 1024;
2574 break;
2575 case 'g':
2576 case 'G':
2577 unit = 1024 * 1024 * 1024;
2578 break;
2579 default:
2580 return (ISC_R_FAILURE);
2581 }
2582 if (value > ISC_UINT64_MAX / unit)
2583 return (ISC_R_FAILURE);
2584 *valuep = value * unit;
2585 return (ISC_R_SUCCESS);
2586 }
2587
2588 static isc_result_t
2589 parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2590 isc_result_t result;
2591 cfg_obj_t *obj = NULL;
2592 isc_uint64_t val;
2593
2594 UNUSED(type);
2595
2596 CHECK(cfg_gettoken(pctx, 0));
2597 if (pctx->token.type != isc_tokentype_string) {
2598 result = ISC_R_UNEXPECTEDTOKEN;
2599 goto cleanup;
2600 }
2601 CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
2602
2603 CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
2604 obj->value.uint64 = val;
2605 *ret = obj;
2606 return (ISC_R_SUCCESS);
2607
2608 cleanup:
2609 cfg_parser_error(pctx, CFG_LOG_NEAR,
2610 "expected integer and optional unit");
2611 return (result);
2612 }
2613
2614 static isc_result_t
2615 parse_sizeval_percent(cfg_parser_t *pctx, const cfg_type_t *type,
2616 cfg_obj_t **ret)
2617 {
2618 char *endp;
2619 isc_result_t result;
2620 cfg_obj_t *obj = NULL;
2621 isc_uint64_t val;
2622 isc_uint64_t percent;
2623
2624 UNUSED(type);
2625
2626 CHECK(cfg_gettoken(pctx, 0));
2627 if (pctx->token.type != isc_tokentype_string) {
2628 result = ISC_R_UNEXPECTEDTOKEN;
2629 goto cleanup;
2630 }
2631
2632 percent = isc_string_touint64(TOKEN_STRING(pctx), &endp, 10);
2633
2634 if (*endp == '%' && *(endp+1) == 0) {
2635 CHECK(cfg_create_obj(pctx, &cfg_type_percentage, &obj));
2636 obj->value.uint32 = (isc_uint32_t)percent;
2637 *ret = obj;
2638 return (ISC_R_SUCCESS);
2639 } else {
2640 CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
2641 CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
2642 obj->value.uint64 = val;
2643 *ret = obj;
2644 return (ISC_R_SUCCESS);
2645 }
2646
2647 cleanup:
2648 cfg_parser_error(pctx, CFG_LOG_NEAR,
2649 "expected integer and optional unit or percent");
2650 return (result);
2651 }
2652
2653 static void
2654 doc_sizeval_percent(cfg_printer_t *pctx, const cfg_type_t *type) {
2655
2656 UNUSED(type);
2657
2658 cfg_print_cstr(pctx, "( ");
2659 cfg_doc_terminal(pctx, &cfg_type_size);
2660 cfg_print_cstr(pctx, " | ");
2661 cfg_doc_terminal(pctx, &cfg_type_percentage);
2662 cfg_print_cstr(pctx, " )");
2663 }
2664
2665 /*%
2666 * A size value (number + optional unit).
2667 */
2668 static cfg_type_t cfg_type_sizeval = {
2669 "sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal,
2670 &cfg_rep_uint64, NULL
2671 };
2672
2673 /*%
2674 * A size, "unlimited", or "default".
2675 */
2676
2677 static isc_result_t
2678 parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2679 return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
2680 }
2681
2682 static void
2683 doc_size(cfg_printer_t *pctx, const cfg_type_t *type) {
2684 doc_enum_or_other(pctx, type, &cfg_type_sizeval);
2685 }
2686
2687 static const char *size_enums[] = { "default", "unlimited", NULL };
2688 static cfg_type_t cfg_type_size = {
2689 "size", parse_size, cfg_print_ustring, doc_size,
2690 &cfg_rep_string, size_enums
2691 };
2692
2693 /*%
2694 * A size or "unlimited", but not "default".
2695 */
2696 static const char *sizenodefault_enums[] = { "unlimited", NULL };
2697 static cfg_type_t cfg_type_sizenodefault = {
2698 "size_no_default", parse_size, cfg_print_ustring, doc_size,
2699 &cfg_rep_string, sizenodefault_enums
2700 };
2701
2702 /*%
2703 * A size in absolute values or percents.
2704 */
2705 static cfg_type_t cfg_type_sizeval_percent = {
2706 "sizeval_percent", parse_sizeval_percent, cfg_print_ustring,
2707 doc_sizeval_percent, &cfg_rep_string, NULL
2708 };
2709
2710 /*%
2711 * A size in absolute values or percents, or "unlimited", or "default"
2712 */
2713
2714 static isc_result_t
2715 parse_size_or_percent(cfg_parser_t *pctx, const cfg_type_t *type,
2716 cfg_obj_t **ret)
2717 {
2718 return (parse_enum_or_other(pctx, type, &cfg_type_sizeval_percent,
2719 ret));
2720 }
2721
2722 static void
2723 doc_parse_size_or_percent(cfg_printer_t *pctx, const cfg_type_t *type) {
2724 doc_enum_or_other(pctx, type, &cfg_type_sizeval_percent);
2725 }
2726
2727 static const char *sizeorpercent_enums[] = { "default", "unlimited", NULL };
2728 static cfg_type_t cfg_type_sizeorpercent = {
2729 "size_or_percent", parse_size_or_percent, cfg_print_ustring,
2730 doc_parse_size_or_percent, &cfg_rep_string, sizeorpercent_enums
2731 };
2732
2733 /*%
2734 * optional_keyvalue
2735 */
2736 static isc_result_t
2737 parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
2738 isc_boolean_t optional, cfg_obj_t **ret)
2739 {
2740 isc_result_t result;
2741 cfg_obj_t *obj = NULL;
2742 const keyword_type_t *kw = type->of;
2743
2744 CHECK(cfg_peektoken(pctx, 0));
2745 if (pctx->token.type == isc_tokentype_string &&
2746 strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) {
2747 CHECK(cfg_gettoken(pctx, 0));
2748 CHECK(kw->type->parse(pctx, kw->type, &obj));
2749 obj->type = type; /* XXX kludge */
2750 } else {
2751 if (optional) {
2752 CHECK(cfg_parse_void(pctx, NULL, &obj));
2753 } else {
2754 cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'",
2755 kw->name);
2756 result = ISC_R_UNEXPECTEDTOKEN;
2757 goto cleanup;
2758 }
2759 }
2760 *ret = obj;
2761 cleanup:
2762 return (result);
2763 }
2764
2765 static isc_result_t
2766 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
2767 const cfg_type_t *othertype, cfg_obj_t **ret)
2768 {
2769 isc_result_t result;
2770 CHECK(cfg_peektoken(pctx, 0));
2771 if (pctx->token.type == isc_tokentype_string &&
2772 cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) {
2773 CHECK(cfg_parse_enum(pctx, enumtype, ret));
2774 } else {
2775 CHECK(cfg_parse_obj(pctx, othertype, ret));
2776 }
2777 cleanup:
2778 return (result);
2779 }
2780
2781 static void
2782 doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *enumtype,
2783 const cfg_type_t *othertype)
2784 {
2785 const char * const *p;
2786 isc_boolean_t first = ISC_TRUE;
2787
2788 /*
2789 * If othertype is cfg_type_void, it means that enumtype is
2790 * optional.
2791 */
2792
2793 if (othertype == &cfg_type_void)
2794 cfg_print_cstr(pctx, "[ ");
2795 cfg_print_cstr(pctx, "( ");
2796 for (p = enumtype->of; *p != NULL; p++) {
2797 if (!first)
2798 cfg_print_cstr(pctx, " | ");
2799 first = ISC_FALSE;
2800 cfg_print_cstr(pctx, *p);
2801 }
2802 if (othertype == &cfg_type_sizeval_percent) {
2803 if (!first)
2804 cfg_print_cstr(pctx, " | ");
2805 cfg_doc_terminal(pctx, &cfg_type_sizeval);
2806 cfg_print_cstr(pctx, " | ");
2807 cfg_doc_terminal(pctx, &cfg_type_percentage);
2808 } else if (othertype != &cfg_type_void) {
2809 if (!first)
2810 cfg_print_cstr(pctx, " | ");
2811 cfg_doc_terminal(pctx, othertype);
2812 }
2813 cfg_print_cstr(pctx, " )");
2814 if (othertype == &cfg_type_void)
2815 cfg_print_cstr(pctx, " ]");
2816 }
2817
2818 static isc_result_t
2819 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2820 return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret));
2821 }
2822
2823 static isc_result_t
2824 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
2825 cfg_obj_t **ret)
2826 {
2827 return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret));
2828 }
2829
2830 static void
2831 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
2832 const keyword_type_t *kw = obj->type->of;
2833 cfg_print_cstr(pctx, kw->name);
2834 cfg_print_cstr(pctx, " ");
2835 kw->type->print(pctx, obj);
2836 }
2837
2838 static void
2839 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
2840 const keyword_type_t *kw = type->of;
2841 cfg_print_cstr(pctx, kw->name);
2842 cfg_print_cstr(pctx, " ");
2843 cfg_doc_obj(pctx, kw->type);
2844 }
2845
2846 static void
2847 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
2848 const keyword_type_t *kw = type->of;
2849 cfg_print_cstr(pctx, "[ ");
2850 cfg_print_cstr(pctx, kw->name);
2851 cfg_print_cstr(pctx, " ");
2852 cfg_doc_obj(pctx, kw->type);
2853 cfg_print_cstr(pctx, " ]");
2854 }
2855
2856 static const char *dialup_enums[] = {
2857 "notify", "notify-passive", "passive", "refresh", NULL
2858 };
2859 static isc_result_t
2860 parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type,
2861 cfg_obj_t **ret)
2862 {
2863 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2864 }
2865 static void
2866 doc_dialup_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2867 doc_enum_or_other(pctx, type, &cfg_type_boolean);
2868 }
2869 static cfg_type_t cfg_type_dialuptype = {
2870 "dialuptype", parse_dialup_type, cfg_print_ustring, doc_dialup_type,
2871 &cfg_rep_string, dialup_enums
2872 };
2873
2874 static const char *notify_enums[] = { "explicit", "master-only", NULL };
2875 static isc_result_t
2876 parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type,
2877 cfg_obj_t **ret)
2878 {
2879 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2880 }
2881 static void
2882 doc_notify_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2883 doc_enum_or_other(pctx, type, &cfg_type_boolean);
2884 }
2885 static cfg_type_t cfg_type_notifytype = {
2886 "notifytype", parse_notify_type, cfg_print_ustring, doc_notify_type,
2887 &cfg_rep_string, notify_enums,
2888 };
2889
2890 static const char *minimal_enums[] = { "no-auth", "no-auth-recursive", NULL };
2891 static isc_result_t
2892 parse_minimal(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2893 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2894 }
2895 static void
2896 doc_minimal(cfg_printer_t *pctx, const cfg_type_t *type) {
2897 doc_enum_or_other(pctx, type, &cfg_type_boolean);
2898 }
2899 static cfg_type_t cfg_type_minimal = {
2900 "mimimal", parse_minimal, cfg_print_ustring, doc_minimal,
2901 &cfg_rep_string, minimal_enums,
2902 };
2903
2904 static const char *ixfrdiff_enums[] = { "master", "slave", NULL };
2905 static isc_result_t
2906 parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type,
2907 cfg_obj_t **ret)
2908 {
2909 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2910 }
2911 static void
2912 doc_ixfrdiff_type(cfg_printer_t *pctx, const cfg_type_t *type) {
2913 doc_enum_or_other(pctx, type, &cfg_type_boolean);
2914 }
2915 static cfg_type_t cfg_type_ixfrdifftype = {
2916 "ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring, doc_ixfrdiff_type,
2917 &cfg_rep_string, ixfrdiff_enums,
2918 };
2919
2920 static const char *filter_aaaa_enums[] = { "break-dnssec", NULL };
2921 static isc_result_t
2922 parse_filter_aaaa(cfg_parser_t *pctx, const cfg_type_t *type,
2923 cfg_obj_t **ret) {
2924 return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
2925 }
2926 static void
2927 doc_filter_aaaa(cfg_printer_t *pctx, const cfg_type_t *type) {
2928 doc_enum_or_other(pctx, type, &cfg_type_boolean);
2929 }
2930 static cfg_type_t cfg_type_filter_aaaa = {
2931 "filter_aaaa", parse_filter_aaaa, cfg_print_ustring,
2932 doc_filter_aaaa, &cfg_rep_string, filter_aaaa_enums,
2933 };
2934
2935 static keyword_type_t key_kw = { "key", &cfg_type_astring };
2936
2937 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
2938 "keyref", parse_keyvalue, print_keyvalue, doc_keyvalue,
2939 &cfg_rep_string, &key_kw
2940 };
2941
2942 static cfg_type_t cfg_type_optional_keyref = {
2943 "optional_keyref", parse_optional_keyvalue, print_keyvalue,
2944 doc_optional_keyvalue, &cfg_rep_string, &key_kw
2945 };
2946
2947 #ifdef HAVE_GEOIP
2948 /*
2949 * "geoip" ACL element:
2950 * geoip [ db <database> ] search-type <string>
2951 */
2952 static const char *geoiptype_enums[] = {
2953 "area", "areacode", "asnum", "city", "continent", "country",
2954 "country3", "countryname", "domain", "isp", "metro", "metrocode",
2955 "netspeed", "org", "postal", "postalcode", "region", "regionname",
2956 "timezone", "tz", NULL
2957 };
2958 static cfg_type_t cfg_type_geoiptype = {
2959 "geoiptype", cfg_parse_enum, cfg_print_ustring,
2960 cfg_doc_enum, &cfg_rep_string, &geoiptype_enums
2961 };
2962
2963 static const char *geoipdb_enums[] = {
2964 "asnum", "city", "country", "domain", "isp", "netspeed",
2965 "org", "region", NULL
2966 };
2967 static cfg_type_t cfg_type_geoipdb = {
2968 "geoipdb", cfg_parse_enum, cfg_print_ustring,
2969 cfg_doc_enum, &cfg_rep_string, &geoipdb_enums
2970 };
2971
2972 static cfg_tuplefielddef_t geoip_fields[] = {
2973 { "negated", &cfg_type_void, 0 },
2974 { "db", &cfg_type_geoipdb, 0 },
2975 { "subtype", &cfg_type_geoiptype, 0 },
2976 { "search", &cfg_type_astring, 0 },
2977 { NULL, NULL, 0 }
2978 };
2979
2980 static cfg_type_t cfg_type_geoip = {
2981 "geoip", parse_geoip, print_geoip, doc_geoip,
2982 &cfg_rep_tuple, geoip_fields
2983 };
2984
2985 static isc_result_t
2986 parse_geoip(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
2987 isc_result_t result;
2988 cfg_obj_t *obj = NULL;
2989 const cfg_tuplefielddef_t *fields = type->of;
2990
2991 CHECK(cfg_create_tuple(pctx, type, &obj));
2992 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[0]));
2993
2994 /* Parse the optional "db" field. */
2995 CHECK(cfg_peektoken(pctx, 0));
2996 if (pctx->token.type == isc_tokentype_string) {
2997 CHECK(cfg_gettoken(pctx, 0));
2998 if (strcasecmp(TOKEN_STRING(pctx), "db") == 0 &&
2999 obj->value.tuple[1] == NULL) {
3000 CHECK(cfg_parse_obj(pctx, fields[1].type,
3001 &obj->value.tuple[1]));
3002 } else {
3003 CHECK(cfg_parse_void(pctx, NULL,
3004 &obj->value.tuple[1]));
3005 cfg_ungettoken(pctx);
3006 }
3007 }
3008
3009 CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2]));
3010 CHECK(cfg_parse_obj(pctx, fields[3].type, &obj->value.tuple[3]));
3011
3012 *ret = obj;
3013 return (ISC_R_SUCCESS);
3014
3015 cleanup:
3016 CLEANUP_OBJ(obj);
3017 return (result);
3018 }
3019
3020 static void
3021 print_geoip(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3022 if (obj->value.tuple[1]->type->print != cfg_print_void) {
3023 cfg_print_cstr(pctx, " db ");
3024 cfg_print_obj(pctx, obj->value.tuple[1]);
3025 }
3026 cfg_print_obj(pctx, obj->value.tuple[2]);
3027 cfg_print_obj(pctx, obj->value.tuple[3]);
3028 }
3029
3030 static void
3031 doc_geoip(cfg_printer_t *pctx, const cfg_type_t *type) {
3032 UNUSED(type);
3033 cfg_print_cstr(pctx, "[ db ");
3034 cfg_doc_enum(pctx, &cfg_type_geoipdb);
3035 cfg_print_cstr(pctx, " ]");
3036 cfg_print_cstr(pctx, " ");
3037 cfg_doc_enum(pctx, &cfg_type_geoiptype);
3038 cfg_print_cstr(pctx, " ");
3039 cfg_print_cstr(pctx, "<quoted_string>");
3040 }
3041 #endif /* HAVE_GEOIP */
3042
3043 /*%
3044 * An EDNS client subnet address
3045 */
3046
3047 static keyword_type_t ecs_kw = { "ecs", &cfg_type_netprefix };
3048 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_ecsprefix = {
3049 "edns_client_subnet", parse_keyvalue, print_keyvalue, doc_keyvalue,
3050 &cfg_rep_netprefix, &ecs_kw
3051 };
3052
3053 /*%
3054 * A "controls" statement is represented as a map with the multivalued
3055 * "inet" and "unix" clauses.
3056 */
3057
3058 static keyword_type_t controls_allow_kw = {
3059 "allow", &cfg_type_bracketed_aml };
3060
3061 static cfg_type_t cfg_type_controls_allow = {
3062 "controls_allow", parse_keyvalue,
3063 print_keyvalue, doc_keyvalue,
3064 &cfg_rep_list, &controls_allow_kw
3065 };
3066
3067 static keyword_type_t controls_keys_kw = {
3068 "keys", &cfg_type_keylist
3069 };
3070
3071 static cfg_type_t cfg_type_controls_keys = {
3072 "controls_keys", parse_optional_keyvalue,
3073 print_keyvalue, doc_optional_keyvalue,
3074 &cfg_rep_list, &controls_keys_kw
3075 };
3076
3077 static keyword_type_t controls_readonly_kw = {
3078 "read-only", &cfg_type_boolean
3079 };
3080
3081 static cfg_type_t cfg_type_controls_readonly = {
3082 "controls_readonly", parse_optional_keyvalue,
3083 print_keyvalue, doc_optional_keyvalue,
3084 &cfg_rep_boolean, &controls_readonly_kw
3085 };
3086
3087 static cfg_tuplefielddef_t inetcontrol_fields[] = {
3088 { "address", &cfg_type_controls_sockaddr, 0 },
3089 { "allow", &cfg_type_controls_allow, 0 },
3090 { "keys", &cfg_type_controls_keys, 0 },
3091 { "read-only", &cfg_type_controls_readonly, 0 },
3092 { NULL, NULL, 0 }
3093 };
3094
3095 static cfg_type_t cfg_type_inetcontrol = {
3096 "inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3097 &cfg_rep_tuple, inetcontrol_fields
3098 };
3099
3100 static keyword_type_t controls_perm_kw = {
3101 "perm", &cfg_type_uint32
3102 };
3103
3104 static cfg_type_t cfg_type_controls_perm = {
3105 "controls_perm", parse_keyvalue,
3106 print_keyvalue, doc_keyvalue,
3107 &cfg_rep_uint32, &controls_perm_kw
3108 };
3109
3110 static keyword_type_t controls_owner_kw = {
3111 "owner", &cfg_type_uint32
3112 };
3113
3114 static cfg_type_t cfg_type_controls_owner = {
3115 "controls_owner", parse_keyvalue,
3116 print_keyvalue, doc_keyvalue,
3117 &cfg_rep_uint32, &controls_owner_kw
3118 };
3119
3120 static keyword_type_t controls_group_kw = {
3121 "group", &cfg_type_uint32
3122 };
3123
3124 static cfg_type_t cfg_type_controls_group = {
3125 "controls_allow", parse_keyvalue,
3126 print_keyvalue, doc_keyvalue,
3127 &cfg_rep_uint32, &controls_group_kw
3128 };
3129
3130 static cfg_tuplefielddef_t unixcontrol_fields[] = {
3131 { "path", &cfg_type_qstring, 0 },
3132 { "perm", &cfg_type_controls_perm, 0 },
3133 { "owner", &cfg_type_controls_owner, 0 },
3134 { "group", &cfg_type_controls_group, 0 },
3135 { "keys", &cfg_type_controls_keys, 0 },
3136 { "read-only", &cfg_type_controls_readonly, 0 },
3137 { NULL, NULL, 0 }
3138 };
3139
3140 static cfg_type_t cfg_type_unixcontrol = {
3141 "unixcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3142 &cfg_rep_tuple, unixcontrol_fields
3143 };
3144
3145 static cfg_clausedef_t
3146 controls_clauses[] = {
3147 { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
3148 { "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI },
3149 { NULL, NULL, 0 }
3150 };
3151
3152 static cfg_clausedef_t *
3153 controls_clausesets[] = {
3154 controls_clauses,
3155 NULL
3156 };
3157 static cfg_type_t cfg_type_controls = {
3158 "controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
3159 &controls_clausesets
3160 };
3161
3162 /*%
3163 * A "statistics-channels" statement is represented as a map with the
3164 * multivalued "inet" clauses.
3165 */
3166 static void
3167 doc_optional_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
3168 const keyword_type_t *kw = type->of;
3169 cfg_print_cstr(pctx, "[ ");
3170 cfg_print_cstr(pctx, kw->name);
3171 cfg_print_cstr(pctx, " ");
3172 cfg_doc_obj(pctx, kw->type);
3173 cfg_print_cstr(pctx, " ]");
3174 }
3175
3176 static cfg_type_t cfg_type_optional_allow = {
3177 "optional_allow", parse_optional_keyvalue, print_keyvalue,
3178 doc_optional_bracketed_list, &cfg_rep_list, &controls_allow_kw
3179 };
3180
3181 static cfg_tuplefielddef_t statserver_fields[] = {
3182 { "address", &cfg_type_controls_sockaddr, 0 }, /* reuse controls def */
3183 { "allow", &cfg_type_optional_allow, 0 },
3184 { NULL, NULL, 0 }
3185 };
3186
3187 static cfg_type_t cfg_type_statschannel = {
3188 "statschannel", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3189 &cfg_rep_tuple, statserver_fields
3190 };
3191
3192 static cfg_clausedef_t
3193 statservers_clauses[] = {
3194 { "inet", &cfg_type_statschannel, CFG_CLAUSEFLAG_MULTI },
3195 { NULL, NULL, 0 }
3196 };
3197
3198 static cfg_clausedef_t *
3199 statservers_clausesets[] = {
3200 statservers_clauses,
3201 NULL
3202 };
3203
3204 static cfg_type_t cfg_type_statschannels = {
3205 "statistics-channels", cfg_parse_map, cfg_print_map, cfg_doc_map,
3206 &cfg_rep_map, &statservers_clausesets
3207 };
3208
3209 /*%
3210 * An optional class, as used in view and zone statements.
3211 */
3212 static isc_result_t
3213 parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type,
3214 cfg_obj_t **ret)
3215 {
3216 isc_result_t result;
3217 UNUSED(type);
3218 CHECK(cfg_peektoken(pctx, 0));
3219 if (pctx->token.type == isc_tokentype_string)
3220 CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret));
3221 else
3222 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
3223 cleanup:
3224 return (result);
3225 }
3226
3227 static void
3228 doc_optional_class(cfg_printer_t *pctx, const cfg_type_t *type) {
3229 UNUSED(type);
3230 cfg_print_cstr(pctx, "[ <class> ]");
3231 }
3232
3233 static cfg_type_t cfg_type_optional_class = {
3234 "optional_class", parse_optional_class, NULL, doc_optional_class,
3235 NULL, NULL
3236 };
3237
3238 static isc_result_t
3239 parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type,
3240 cfg_obj_t **ret)
3241 {
3242 isc_result_t result;
3243 cfg_obj_t *obj = NULL;
3244 isc_netaddr_t netaddr;
3245 in_port_t port = 0;
3246 isc_dscp_t dscp = -1;
3247 unsigned int have_address = 0;
3248 unsigned int have_port = 0;
3249 unsigned int have_dscp = 0;
3250 const unsigned int *flagp = type->of;
3251
3252 if ((*flagp & CFG_ADDR_V4OK) != 0)
3253 isc_netaddr_any(&netaddr);
3254 else if ((*flagp & CFG_ADDR_V6OK) != 0)
3255 isc_netaddr_any6(&netaddr);
3256 else
3257 INSIST(0);
3258
3259 for (;;) {
3260 CHECK(cfg_peektoken(pctx, 0));
3261 if (pctx->token.type == isc_tokentype_string) {
3262 if (strcasecmp(TOKEN_STRING(pctx),
3263 "address") == 0)
3264 {
3265 /* read "address" */
3266 CHECK(cfg_gettoken(pctx, 0));
3267 CHECK(cfg_parse_rawaddr(pctx, *flagp,
3268 &netaddr));
3269 have_address++;
3270 } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
3271 {
3272 /* read "port" */
3273 CHECK(cfg_gettoken(pctx, 0));
3274 CHECK(cfg_parse_rawport(pctx,
3275 CFG_ADDR_WILDOK,
3276 &port));
3277 have_port++;
3278 } else if (strcasecmp(TOKEN_STRING(pctx), "dscp") == 0)
3279 {
3280 /* read "dscp" */
3281 CHECK(cfg_gettoken(pctx, 0));
3282 CHECK(cfg_parse_dscp(pctx, &dscp));
3283 have_dscp++;
3284 } else if (have_port == 0 && have_dscp == 0 &&
3285 have_address == 0)
3286 {
3287 return (cfg_parse_sockaddr(pctx, type, ret));
3288 } else {
3289 cfg_parser_error(pctx, CFG_LOG_NEAR,
3290 "expected 'address', 'port', "
3291 "or 'dscp'");
3292 return (ISC_R_UNEXPECTEDTOKEN);
3293 }
3294 } else
3295 break;
3296 }
3297 if (have_address > 1 || have_port > 1 ||
3298 have_address + have_port == 0) {
3299 cfg_parser_error(pctx, 0, "expected one address and/or port");
3300 return (ISC_R_UNEXPECTEDTOKEN);
3301 }
3302
3303 if (have_dscp > 1) {
3304 cfg_parser_error(pctx, 0, "expected at most one dscp");
3305 return (ISC_R_UNEXPECTEDTOKEN);
3306 }
3307
3308 CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj));
3309 isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
3310 obj->value.sockaddrdscp.dscp = dscp;
3311 *ret = obj;
3312 return (ISC_R_SUCCESS);
3313
3314 cleanup:
3315 cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
3316 CLEANUP_OBJ(obj);
3317 return (result);
3318 }
3319
3320 static void
3321 print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3322 isc_netaddr_t na;
3323 isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
3324 cfg_print_cstr(pctx, "address ");
3325 cfg_print_rawaddr(pctx, &na);
3326 cfg_print_cstr(pctx, " port ");
3327 cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
3328 if (obj->value.sockaddrdscp.dscp != -1) {
3329 cfg_print_cstr(pctx, " dscp ");
3330 cfg_print_rawuint(pctx, obj->value.sockaddrdscp.dscp);
3331 }
3332 }
3333
3334 static void
3335 doc_querysource(cfg_printer_t *pctx, const cfg_type_t *type) {
3336 const unsigned int *flagp = type->of;
3337
3338 cfg_print_cstr(pctx, "( ( [ address ] ( ");
3339 if (*flagp & CFG_ADDR_V4OK)
3340 cfg_print_cstr(pctx, "<ipv4_address>");
3341 else if (*flagp & CFG_ADDR_V6OK)
3342 cfg_print_cstr(pctx, "<ipv6_address>");
3343 else
3344 INSIST(0);
3345 cfg_print_cstr(pctx, " | * ) [ port ( <integer> | * ) ] ) | "
3346 "( [ [ address ] ( ");
3347 if (*flagp & CFG_ADDR_V4OK)
3348 cfg_print_cstr(pctx, "<ipv4_address>");
3349 else if (*flagp & CFG_ADDR_V6OK)
3350 cfg_print_cstr(pctx, "<ipv6_address>");
3351 else
3352 INSIST(0);
3353 cfg_print_cstr(pctx, " | * ) ] port ( <integer> | * ) ) )"
3354 " [ dscp <integer> ]");
3355 }
3356
3357 static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK |
3358 CFG_ADDR_DSCPOK;
3359 static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK |
3360 CFG_ADDR_DSCPOK;
3361
3362 static cfg_type_t cfg_type_querysource4 = {
3363 "querysource4", parse_querysource, NULL, doc_querysource,
3364 NULL, &sockaddr4wild_flags
3365 };
3366
3367 static cfg_type_t cfg_type_querysource6 = {
3368 "querysource6", parse_querysource, NULL, doc_querysource,
3369 NULL, &sockaddr6wild_flags
3370 };
3371
3372 static cfg_type_t cfg_type_querysource = {
3373 "querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL
3374 };
3375
3376 /*% addrmatchelt */
3377
3378 static isc_result_t
3379 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type,
3380 cfg_obj_t **ret)
3381 {
3382 isc_result_t result;
3383 UNUSED(type);
3384
3385 CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3386
3387 if (pctx->token.type == isc_tokentype_string ||
3388 pctx->token.type == isc_tokentype_qstring) {
3389 if (pctx->token.type == isc_tokentype_string &&
3390 (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) {
3391 CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
3392 } else if (pctx->token.type == isc_tokentype_string &&
3393 (strcasecmp(TOKEN_STRING(pctx), "ecs") == 0)) {
3394 CHECK(cfg_parse_obj(pctx, &cfg_type_ecsprefix, ret));
3395 } else if (pctx->token.type == isc_tokentype_string &&
3396 (strcasecmp(TOKEN_STRING(pctx), "geoip") == 0)) {
3397 #ifdef HAVE_GEOIP
3398 CHECK(cfg_gettoken(pctx, 0));
3399 CHECK(cfg_parse_obj(pctx, &cfg_type_geoip, ret));
3400 #else
3401 cfg_parser_error(pctx, CFG_LOG_NEAR, "'geoip' "
3402 "not supported in this build");
3403 return (ISC_R_UNEXPECTEDTOKEN);
3404 #endif
3405 } else {
3406 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK |
3407 CFG_ADDR_V4PREFIXOK |
3408 CFG_ADDR_V6OK))
3409 {
3410 CHECK(cfg_parse_netprefix(pctx, NULL, ret));
3411 } else {
3412 CHECK(cfg_parse_astring(pctx, NULL, ret));
3413 }
3414 }
3415 } else if (pctx->token.type == isc_tokentype_special) {
3416 if (pctx->token.value.as_char == '{') {
3417 /* Nested match list. */
3418 CHECK(cfg_parse_obj(pctx,
3419 &cfg_type_bracketed_aml, ret));
3420 } else if (pctx->token.value.as_char == '!') {
3421 CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
3422 CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
3423 } else {
3424 goto bad;
3425 }
3426 } else {
3427 bad:
3428 cfg_parser_error(pctx, CFG_LOG_NEAR,
3429 "expected IP match list element");
3430 return (ISC_R_UNEXPECTEDTOKEN);
3431 }
3432 cleanup:
3433 return (result);
3434 }
3435
3436 /*%
3437 * A negated address match list element (like "! 10.0.0.1").
3438 * Somewhat sneakily, the caller is expected to parse the
3439 * "!", but not to print it.
3440 */
3441
3442 static cfg_tuplefielddef_t negated_fields[] = {
3443 { "negated", &cfg_type_addrmatchelt, 0 },
3444 { NULL, NULL, 0 }
3445 };
3446
3447 static void
3448 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3449 cfg_print_cstr(pctx, "!");
3450 cfg_print_tuple(pctx, obj);
3451 }
3452
3453 static cfg_type_t cfg_type_negated = {
3454 "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple,
3455 &negated_fields
3456 };
3457
3458 /*% An address match list element */
3459
3460 static cfg_type_t cfg_type_addrmatchelt = {
3461 "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal,
3462 NULL, NULL
3463 };
3464
3465 /*% A bracketed address match list */
3466
3467 static cfg_type_t cfg_type_bracketed_aml = {
3468 "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list,
3469 cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt
3470 };
3471
3472 /*%
3473 * The socket address syntax in the "controls" statement is silly.
3474 * It allows both socket address families, but also allows "*",
3475 * whis is gratuitously interpreted as the IPv4 wildcard address.
3476 */
3477 static unsigned int controls_sockaddr_flags =
3478 CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
3479 static cfg_type_t cfg_type_controls_sockaddr = {
3480 "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr,
3481 cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags
3482 };
3483
3484 /*%
3485 * Handle the special kludge syntax of the "keys" clause in the "server"
3486 * statement, which takes a single key with or without braces and semicolon.
3487 */
3488 static isc_result_t
3489 parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type,
3490 cfg_obj_t **ret)
3491 {
3492 isc_result_t result;
3493 isc_boolean_t braces = ISC_FALSE;
3494 UNUSED(type);
3495
3496 /* Allow opening brace. */
3497 CHECK(cfg_peektoken(pctx, 0));
3498 if (pctx->token.type == isc_tokentype_special &&
3499 pctx->token.value.as_char == '{') {
3500 CHECK(cfg_gettoken(pctx, 0));
3501 braces = ISC_TRUE;
3502 }
3503
3504 CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
3505
3506 if (braces) {
3507 /* Skip semicolon if present. */
3508 CHECK(cfg_peektoken(pctx, 0));
3509 if (pctx->token.type == isc_tokentype_special &&
3510 pctx->token.value.as_char == ';')
3511 CHECK(cfg_gettoken(pctx, 0));
3512
3513 CHECK(cfg_parse_special(pctx, '}'));
3514 }
3515 cleanup:
3516 return (result);
3517 }
3518 static cfg_type_t cfg_type_server_key_kludge = {
3519 "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal,
3520 NULL, NULL
3521 };
3522
3523
3524 /*%
3525 * An optional logging facility.
3526 */
3527
3528 static isc_result_t
3529 parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type,
3530 cfg_obj_t **ret)
3531 {
3532 isc_result_t result;
3533 UNUSED(type);
3534
3535 CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3536 if (pctx->token.type == isc_tokentype_string ||
3537 pctx->token.type == isc_tokentype_qstring) {
3538 CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
3539 } else {
3540 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
3541 }
3542 cleanup:
3543 return (result);
3544 }
3545
3546 static void
3547 doc_optional_facility(cfg_printer_t *pctx, const cfg_type_t *type) {
3548 UNUSED(type);
3549 cfg_print_cstr(pctx, "[ <syslog_facility> ]");
3550 }
3551
3552 static cfg_type_t cfg_type_optional_facility = {
3553 "optional_facility", parse_optional_facility, NULL,
3554 doc_optional_facility, NULL, NULL
3555 };
3556
3557
3558 /*%
3559 * A log severity. Return as a string, except "debug N",
3560 * which is returned as a keyword object.
3561 */
3562
3563 static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
3564 static cfg_type_t cfg_type_debuglevel = {
3565 "debuglevel", parse_keyvalue,
3566 print_keyvalue, doc_keyvalue,
3567 &cfg_rep_uint32, &debug_kw
3568 };
3569
3570 static isc_result_t
3571 parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type,
3572 cfg_obj_t **ret)
3573 {
3574 isc_result_t result;
3575 UNUSED(type);
3576
3577 CHECK(cfg_peektoken(pctx, 0));
3578 if (pctx->token.type == isc_tokentype_string &&
3579 strcasecmp(TOKEN_STRING(pctx), "debug") == 0) {
3580 CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
3581 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
3582 if (pctx->token.type == isc_tokentype_number) {
3583 CHECK(cfg_parse_uint32(pctx, NULL, ret));
3584 } else {
3585 /*
3586 * The debug level is optional and defaults to 1.
3587 * This makes little sense, but we support it for
3588 * compatibility with BIND 8.
3589 */
3590 CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret));
3591 (*ret)->value.uint32 = 1;
3592 }
3593 (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
3594 } else {
3595 CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret));
3596 }
3597 cleanup:
3598 return (result);
3599 }
3600
3601 static cfg_type_t cfg_type_logseverity = {
3602 "log_severity", parse_logseverity, NULL, cfg_doc_terminal,
3603 NULL, NULL
3604 };
3605
3606 /*%
3607 * The "file" clause of the "channel" statement.
3608 * This is yet another special case.
3609 */
3610
3611 static const char *logversions_enums[] = { "unlimited", NULL };
3612 static isc_result_t
3613 parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type,
3614 cfg_obj_t **ret)
3615 {
3616 return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
3617 }
3618
3619 static void
3620 doc_logversions(cfg_printer_t *pctx, const cfg_type_t *type) {
3621 doc_enum_or_other(pctx, type, &cfg_type_uint32);
3622 }
3623
3624 static cfg_type_t cfg_type_logversions = {
3625 "logversions", parse_logversions, cfg_print_ustring, doc_logversions,
3626 &cfg_rep_string, logversions_enums
3627 };
3628
3629 static const char *logsuffix_enums[] = { "increment", "timestamp", NULL };
3630 static cfg_type_t cfg_type_logsuffix = {
3631 "logsuffix", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
3632 &cfg_rep_string, &logsuffix_enums
3633 };
3634
3635 static cfg_tuplefielddef_t logfile_fields[] = {
3636 { "file", &cfg_type_qstring, 0 },
3637 { "versions", &cfg_type_logversions, 0 },
3638 { "size", &cfg_type_size, 0 },
3639 { "suffix", &cfg_type_logsuffix, 0 },
3640 { NULL, NULL, 0 }
3641 };
3642
3643 static isc_result_t
3644 parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3645 isc_result_t result;
3646 cfg_obj_t *obj = NULL;
3647 const cfg_tuplefielddef_t *fields = type->of;
3648
3649 CHECK(cfg_create_tuple(pctx, type, &obj));
3650
3651 /* Parse the mandatory "file" field */
3652 CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
3653
3654 /* Parse "versions" and "size" fields in any order. */
3655 for (;;) {
3656 CHECK(cfg_peektoken(pctx, 0));
3657 if (pctx->token.type == isc_tokentype_string) {
3658 CHECK(cfg_gettoken(pctx, 0));
3659 if (strcasecmp(TOKEN_STRING(pctx),
3660 "versions") == 0 &&
3661 obj->value.tuple[1] == NULL) {
3662 CHECK(cfg_parse_obj(pctx, fields[1].type,
3663 &obj->value.tuple[1]));
3664 } else if (strcasecmp(TOKEN_STRING(pctx),
3665 "size") == 0 &&
3666 obj->value.tuple[2] == NULL) {
3667 CHECK(cfg_parse_obj(pctx, fields[2].type,
3668 &obj->value.tuple[2]));
3669 } else if (strcasecmp(TOKEN_STRING(pctx),
3670 "suffix") == 0 &&
3671 obj->value.tuple[3] == NULL) {
3672 CHECK(cfg_parse_obj(pctx, fields[3].type,
3673 &obj->value.tuple[3]));
3674 } else {
3675 break;
3676 }
3677 } else {
3678 break;
3679 }
3680 }
3681
3682 /* Create void objects for missing optional values. */
3683 if (obj->value.tuple[1] == NULL)
3684 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
3685 if (obj->value.tuple[2] == NULL)
3686 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
3687 if (obj->value.tuple[3] == NULL)
3688 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[3]));
3689
3690 *ret = obj;
3691 return (ISC_R_SUCCESS);
3692
3693 cleanup:
3694 CLEANUP_OBJ(obj);
3695 return (result);
3696 }
3697
3698 static void
3699 print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) {
3700 cfg_print_obj(pctx, obj->value.tuple[0]); /* file */
3701 if (obj->value.tuple[1]->type->print != cfg_print_void) {
3702 cfg_print_cstr(pctx, " versions ");
3703 cfg_print_obj(pctx, obj->value.tuple[1]);
3704 }
3705 if (obj->value.tuple[2]->type->print != cfg_print_void) {
3706 cfg_print_cstr(pctx, " size ");
3707 cfg_print_obj(pctx, obj->value.tuple[2]);
3708 }
3709 if (obj->value.tuple[3]->type->print != cfg_print_void) {
3710 cfg_print_cstr(pctx, " suffix ");
3711 cfg_print_obj(pctx, obj->value.tuple[3]);
3712 }
3713 }
3714
3715
3716 static void
3717 doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) {
3718 UNUSED(type);
3719 cfg_print_cstr(pctx, "<quoted_string>");
3720 cfg_print_cstr(pctx, " ");
3721 cfg_print_cstr(pctx, "[ versions ( unlimited | <integer> ) ]");
3722 cfg_print_cstr(pctx, " ");
3723 cfg_print_cstr(pctx, "[ size <size> ]");
3724 cfg_print_cstr(pctx, " ");
3725 cfg_print_cstr(pctx, "[ suffix ( increment | timestamp ) ]");
3726 }
3727
3728 static cfg_type_t cfg_type_logfile = {
3729 "log_file", parse_logfile, print_logfile, doc_logfile,
3730 &cfg_rep_tuple, logfile_fields
3731 };
3732
3733 /*% An IPv4 address with optional dscp and port, "*" accepted as wildcard. */
3734 static cfg_type_t cfg_type_sockaddr4wild = {
3735 "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr,
3736 cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags
3737 };
3738
3739 /*% An IPv6 address with optional port, "*" accepted as wildcard. */
3740 static cfg_type_t cfg_type_sockaddr6wild = {
3741 "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr,
3742 cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags
3743 };
3744
3745 /*%
3746 * rndc
3747 */
3748
3749 static cfg_clausedef_t
3750 rndcconf_options_clauses[] = {
3751 { "default-key", &cfg_type_astring, 0 },
3752 { "default-port", &cfg_type_uint32, 0 },
3753 { "default-server", &cfg_type_astring, 0 },
3754 { "default-source-address", &cfg_type_netaddr4wild, 0 },
3755 { "default-source-address-v6", &cfg_type_netaddr6wild, 0 },
3756 { NULL, NULL, 0 }
3757 };
3758
3759 static cfg_clausedef_t *
3760 rndcconf_options_clausesets[] = {
3761 rndcconf_options_clauses,
3762 NULL
3763 };
3764
3765 static cfg_type_t cfg_type_rndcconf_options = {
3766 "rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map,
3767 &cfg_rep_map, rndcconf_options_clausesets
3768 };
3769
3770 static cfg_clausedef_t
3771 rndcconf_server_clauses[] = {
3772 { "key", &cfg_type_astring, 0 },
3773 { "port", &cfg_type_uint32, 0 },
3774 { "source-address", &cfg_type_netaddr4wild, 0 },
3775 { "source-address-v6", &cfg_type_netaddr6wild, 0 },
3776 { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
3777 { NULL, NULL, 0 }
3778 };
3779
3780 static cfg_clausedef_t *
3781 rndcconf_server_clausesets[] = {
3782 rndcconf_server_clauses,
3783 NULL
3784 };
3785
3786 static cfg_type_t cfg_type_rndcconf_server = {
3787 "rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
3788 &cfg_rep_map, rndcconf_server_clausesets
3789 };
3790
3791 static cfg_clausedef_t
3792 rndcconf_clauses[] = {
3793 { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
3794 { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
3795 { "options", &cfg_type_rndcconf_options, 0 },
3796 { NULL, NULL, 0 }
3797 };
3798
3799 static cfg_clausedef_t *
3800 rndcconf_clausesets[] = {
3801 rndcconf_clauses,
3802 NULL
3803 };
3804
3805 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
3806 "rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
3807 &cfg_rep_map, rndcconf_clausesets
3808 };
3809
3810 static cfg_clausedef_t
3811 rndckey_clauses[] = {
3812 { "key", &cfg_type_key, 0 },
3813 { NULL, NULL, 0 }
3814 };
3815
3816 static cfg_clausedef_t *
3817 rndckey_clausesets[] = {
3818 rndckey_clauses,
3819 NULL
3820 };
3821
3822 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
3823 "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
3824 &cfg_rep_map, rndckey_clausesets
3825 };
3826
3827 /*
3828 * session.key has exactly the same syntax as rndc.key, but it's defined
3829 * separately for clarity (and so we can extend it someday, if needed).
3830 */
3831 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_sessionkey = {
3832 "sessionkey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
3833 &cfg_rep_map, rndckey_clausesets
3834 };
3835
3836 static cfg_tuplefielddef_t nameport_fields[] = {
3837 { "name", &cfg_type_astring, 0 },
3838 { "port", &cfg_type_optional_port, 0 },
3839 { "dscp", &cfg_type_optional_dscp, 0 },
3840 { NULL, NULL, 0 }
3841 };
3842
3843 static cfg_type_t cfg_type_nameport = {
3844 "nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3845 &cfg_rep_tuple, nameport_fields
3846 };
3847
3848 static void
3849 doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
3850 UNUSED(type);
3851 cfg_print_cstr(pctx, "( ");
3852 cfg_print_cstr(pctx, "<quoted_string>");
3853 cfg_print_cstr(pctx, " ");
3854 cfg_print_cstr(pctx, "[ port <integer> ]");
3855 cfg_print_cstr(pctx, " ");
3856 cfg_print_cstr(pctx, "[ dscp <integer> ]");
3857 cfg_print_cstr(pctx, " | ");
3858 cfg_print_cstr(pctx, "<ipv4_address>");
3859 cfg_print_cstr(pctx, " ");
3860 cfg_print_cstr(pctx, "[ port <integer> ]");
3861 cfg_print_cstr(pctx, " ");
3862 cfg_print_cstr(pctx, "[ dscp <integer> ]");
3863 cfg_print_cstr(pctx, " | ");
3864 cfg_print_cstr(pctx, "<ipv6_address>");
3865 cfg_print_cstr(pctx, " ");
3866 cfg_print_cstr(pctx, "[ port <integer> ]");
3867 cfg_print_cstr(pctx, " ");
3868 cfg_print_cstr(pctx, "[ dscp <integer> ]");
3869 cfg_print_cstr(pctx, " )");
3870 }
3871
3872 static isc_result_t
3873 parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type,
3874 cfg_obj_t **ret)
3875 {
3876 isc_result_t result;
3877 cfg_obj_t *obj = NULL;
3878 UNUSED(type);
3879
3880 CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3881 if (pctx->token.type == isc_tokentype_string ||
3882 pctx->token.type == isc_tokentype_qstring) {
3883 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
3884 CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr,
3885 ret));
3886 else {
3887 const cfg_tuplefielddef_t *fields =
3888 cfg_type_nameport.of;
3889 CHECK(cfg_create_tuple(pctx, &cfg_type_nameport,
3890 &obj));
3891 CHECK(cfg_parse_obj(pctx, fields[0].type,
3892 &obj->value.tuple[0]));
3893 CHECK(cfg_parse_obj(pctx, fields[1].type,
3894 &obj->value.tuple[1]));
3895 CHECK(cfg_parse_obj(pctx, fields[2].type,
3896 &obj->value.tuple[2]));
3897 *ret = obj;
3898 obj = NULL;
3899 }
3900 } else {
3901 cfg_parser_error(pctx, CFG_LOG_NEAR,
3902 "expected IP address or hostname");
3903 return (ISC_R_UNEXPECTEDTOKEN);
3904 }
3905 cleanup:
3906 CLEANUP_OBJ(obj);
3907 return (result);
3908 }
3909
3910 static cfg_type_t cfg_type_sockaddrnameport = {
3911 "sockaddrnameport_element", parse_sockaddrnameport, NULL,
3912 doc_sockaddrnameport, NULL, NULL
3913 };
3914
3915 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = {
3916 "bracketed_sockaddrnameportlist", cfg_parse_bracketed_list,
3917 cfg_print_bracketed_list, cfg_doc_bracketed_list,
3918 &cfg_rep_list, &cfg_type_sockaddrnameport
3919 };
3920
3921 /*%
3922 * A list of socket addresses or name with an optional default port,
3923 * as used in the dual-stack-servers option. E.g.,
3924 * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
3925 */
3926 static cfg_tuplefielddef_t nameportiplist_fields[] = {
3927 { "port", &cfg_type_optional_port, 0 },
3928 { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
3929 { NULL, NULL, 0 }
3930 };
3931
3932 static cfg_type_t cfg_type_nameportiplist = {
3933 "nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
3934 &cfg_rep_tuple, nameportiplist_fields
3935 };
3936
3937 /*%
3938 * masters element.
3939 */
3940
3941 static void
3942 doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) {
3943 UNUSED(type);
3944 cfg_print_cstr(pctx, "( ");
3945 cfg_print_cstr(pctx, "<masters>");
3946 cfg_print_cstr(pctx, " | ");
3947 cfg_print_cstr(pctx, "<ipv4_address>");
3948 cfg_print_cstr(pctx, " ");
3949 cfg_print_cstr(pctx, "[ port <integer> ]");
3950 cfg_print_cstr(pctx, " | ");
3951 cfg_print_cstr(pctx, "<ipv6_address>");
3952 cfg_print_cstr(pctx, " ");
3953 cfg_print_cstr(pctx, "[ port <integer> ]");
3954 cfg_print_cstr(pctx, " )");
3955 }
3956
3957 static isc_result_t
3958 parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type,
3959 cfg_obj_t **ret)
3960 {
3961 isc_result_t result;
3962 cfg_obj_t *obj = NULL;
3963 UNUSED(type);
3964
3965 CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
3966 if (pctx->token.type == isc_tokentype_string ||
3967 pctx->token.type == isc_tokentype_qstring) {
3968 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
3969 CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr,
3970 ret));
3971 else
3972 CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret));
3973 } else {
3974 cfg_parser_error(pctx, CFG_LOG_NEAR,
3975 "expected IP address or masters name");
3976 return (ISC_R_UNEXPECTEDTOKEN);
3977 }
3978 cleanup:
3979 CLEANUP_OBJ(obj);
3980 return (result);
3981 }
3982
3983 static cfg_type_t cfg_type_masterselement = {
3984 "masters_element", parse_masterselement, NULL,
3985 doc_masterselement, NULL, NULL
3986 };
3987
3988 static isc_result_t
3989 parse_ttlval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
3990 isc_result_t result;
3991 cfg_obj_t *obj = NULL;
3992 isc_uint32_t ttl;
3993
3994 UNUSED(type);
3995
3996 CHECK(cfg_gettoken(pctx, 0));
3997 if (pctx->token.type != isc_tokentype_string) {
3998 result = ISC_R_UNEXPECTEDTOKEN;
3999 goto cleanup;
4000 }
4001
4002 result = dns_ttl_fromtext(&pctx->token.value.as_textregion, &ttl);
4003 if (result == ISC_R_RANGE ) {
4004 cfg_parser_error(pctx, CFG_LOG_NEAR, "TTL out of range ");
4005 return (result);
4006 } else if (result != ISC_R_SUCCESS)
4007 goto cleanup;
4008
4009 CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
4010 obj->value.uint32 = ttl;
4011 *ret = obj;
4012 return (ISC_R_SUCCESS);
4013
4014 cleanup:
4015 cfg_parser_error(pctx, CFG_LOG_NEAR,
4016 "expected integer and optional unit");
4017 return (result);
4018 }
4019
4020 /*%
4021 * A TTL value (number + optional unit).
4022 */
4023 static cfg_type_t cfg_type_ttlval = {
4024 "ttlval", parse_ttlval, cfg_print_uint64, cfg_doc_terminal,
4025 &cfg_rep_uint64, NULL
4026 };
4027
4028 static isc_result_t
4029 parse_maxttl(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
4030 return (parse_enum_or_other(pctx, type, &cfg_type_ttlval, ret));
4031 }
4032
4033 static void
4034 doc_maxttl(cfg_printer_t *pctx, const cfg_type_t *type) {
4035 doc_enum_or_other(pctx, type, &cfg_type_ttlval);
4036 }
4037
4038 /*%
4039 * A size or "unlimited", but not "default".
4040 */
4041 static const char *maxttl_enums[] = { "unlimited", NULL };
4042 static cfg_type_t cfg_type_maxttl = {
4043 "maxttl_no_default", parse_maxttl, cfg_print_ustring, doc_maxttl,
4044 &cfg_rep_string, maxttl_enums
4045 };
4046
4047 static int cmp_clause(const void *ap, const void *bp) {
4048 const cfg_clausedef_t *a = (const cfg_clausedef_t *)ap;
4049 const cfg_clausedef_t *b = (const cfg_clausedef_t *)bp;
4050 return (strcmp(a->name, b->name));
4051 }
4052
4053 isc_boolean_t
4054 cfg_clause_validforzone(const char *name, unsigned int ztype) {
4055 const cfg_clausedef_t *clause;
4056 isc_boolean_t valid = ISC_FALSE;
4057
4058 for (clause = zone_clauses; clause->name != NULL; clause++) {
4059 if ((clause->flags & ztype) == 0 ||
4060 strcmp(clause->name, name) != 0)
4061 {
4062 continue;
4063 }
4064 valid = ISC_TRUE;
4065 }
4066 for (clause = zone_only_clauses; clause->name != NULL; clause++) {
4067 if ((clause->flags & ztype) == 0 ||
4068 strcmp(clause->name, name) != 0)
4069 {
4070 continue;
4071 }
4072 valid = ISC_TRUE;
4073 }
4074
4075 return (valid);
4076 }
4077
4078 void
4079 cfg_print_zonegrammar(const unsigned int zonetype,
4080 void (*f)(void *closure, const char *text, int textlen),
4081 void *closure)
4082 {
4083 #define NCLAUSES \
4084 (((sizeof(zone_clauses) + sizeof(zone_only_clauses)) / \
4085 sizeof(clause[0])) - 1)
4086
4087 cfg_printer_t pctx;
4088 cfg_clausedef_t *clause = NULL;
4089 cfg_clausedef_t clauses[NCLAUSES];
4090
4091 pctx.f = f;
4092 pctx.closure = closure;
4093 pctx.indent = 0;
4094 pctx.flags = 0;
4095
4096 memmove(clauses, zone_clauses, sizeof(zone_clauses));
4097 memmove(clauses + sizeof(zone_clauses)/sizeof(zone_clauses[0]) - 1,
4098 zone_only_clauses, sizeof(zone_only_clauses));
4099 qsort(clauses, NCLAUSES - 1, sizeof(clause[0]), cmp_clause);
4100
4101 cfg_print_cstr(&pctx, "zone <string> [ <class> ] {\n");
4102 pctx.indent++;
4103
4104 switch (zonetype) {
4105 case CFG_ZONE_MASTER:
4106 cfg_print_indent(&pctx);
4107 cfg_print_cstr(&pctx, "type ( master | primary );\n");
4108 break;
4109 case CFG_ZONE_SLAVE:
4110 cfg_print_indent(&pctx);
4111 cfg_print_cstr(&pctx, "type ( slave | secondary );\n");
4112 break;
4113 case CFG_ZONE_STUB:
4114 cfg_print_indent(&pctx);
4115 cfg_print_cstr(&pctx, "type stub;\n");
4116 break;
4117 case CFG_ZONE_HINT:
4118 cfg_print_indent(&pctx);
4119 cfg_print_cstr(&pctx, "type hint;\n");
4120 break;
4121 case CFG_ZONE_FORWARD:
4122 cfg_print_indent(&pctx);
4123 cfg_print_cstr(&pctx, "type forward;\n");
4124 break;
4125 case CFG_ZONE_STATICSTUB:
4126 cfg_print_indent(&pctx);
4127 cfg_print_cstr(&pctx, "type static-stub;\n");
4128 break;
4129 case CFG_ZONE_REDIRECT:
4130 cfg_print_indent(&pctx);
4131 cfg_print_cstr(&pctx, "type redirect;\n");
4132 break;
4133 case CFG_ZONE_DELEGATION:
4134 cfg_print_indent(&pctx);
4135 cfg_print_cstr(&pctx, "type delegation-only;\n");
4136 break;
4137 case CFG_ZONE_INVIEW:
4138 /* no zone type is specified for these */
4139 break;
4140 default:
4141 INSIST(0);
4142 }
4143
4144 for (clause = clauses; clause->name != NULL; clause++) {
4145 if ((clause->flags & zonetype) == 0 ||
4146 strcasecmp(clause->name, "type") == 0) {
4147 continue;
4148 }
4149 cfg_print_indent(&pctx);
4150 cfg_print_cstr(&pctx, clause->name);
4151 cfg_print_cstr(&pctx, " ");
4152 cfg_doc_obj(&pctx, clause->type);
4153 cfg_print_cstr(&pctx, ";");
4154 cfg_print_clauseflags(&pctx, clause->flags);
4155 cfg_print_cstr(&pctx, "\n");
4156 }
4157
4158 pctx.indent--;
4159 cfg_print_cstr(&pctx, "};\n");
4160 }
4161