]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/bind9/lib/isccfg/namedconf.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / bind9 / lib / isccfg / namedconf.c
1 /*
2  * Copyright (C) 2004-2006, 2008  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2002, 2003  Internet Software Consortium.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15  * PERFORMANCE OF THIS SOFTWARE.
16  */
17
18 /* $Id: namedconf.c,v 1.30.18.43 2008/09/04 08:03:08 marka Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <isc/lex.h>
27 #include <isc/result.h>
28 #include <isc/string.h>
29 #include <isc/util.h>
30
31 #include <isccfg/cfg.h>
32 #include <isccfg/grammar.h>
33 #include <isccfg/log.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 isc_result_t
57 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
58
59 static isc_result_t
60 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
61
62 static void
63 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj);
64
65 static void
66 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
67
68 static void
69 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type);
70
71 static cfg_type_t cfg_type_acl;
72 static cfg_type_t cfg_type_addrmatchelt;
73 static cfg_type_t cfg_type_bracketed_aml;
74 static cfg_type_t cfg_type_bracketed_namesockaddrkeylist;
75 static cfg_type_t cfg_type_bracketed_sockaddrlist;
76 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist;
77 static cfg_type_t cfg_type_controls;
78 static cfg_type_t cfg_type_controls_sockaddr;
79 static cfg_type_t cfg_type_destinationlist;
80 static cfg_type_t cfg_type_dialuptype;
81 static cfg_type_t cfg_type_ixfrdifftype;
82 static cfg_type_t cfg_type_key;
83 static cfg_type_t cfg_type_logfile;
84 static cfg_type_t cfg_type_logging;
85 static cfg_type_t cfg_type_logseverity;
86 static cfg_type_t cfg_type_lwres;
87 static cfg_type_t cfg_type_masterselement;
88 static cfg_type_t cfg_type_nameportiplist;
89 static cfg_type_t cfg_type_negated;
90 static cfg_type_t cfg_type_notifytype;
91 static cfg_type_t cfg_type_optional_class;
92 static cfg_type_t cfg_type_optional_facility;
93 static cfg_type_t cfg_type_optional_facility;
94 static cfg_type_t cfg_type_optional_keyref;
95 static cfg_type_t cfg_type_optional_port;
96 static cfg_type_t cfg_type_options;
97 static cfg_type_t cfg_type_portiplist;
98 static cfg_type_t cfg_type_querysource4;
99 static cfg_type_t cfg_type_querysource6;
100 static cfg_type_t cfg_type_querysource;
101 static cfg_type_t cfg_type_server;
102 static cfg_type_t cfg_type_server_key_kludge;
103 static cfg_type_t cfg_type_size;
104 static cfg_type_t cfg_type_sizenodefault;
105 static cfg_type_t cfg_type_sockaddr4wild;
106 static cfg_type_t cfg_type_sockaddr6wild;
107 static cfg_type_t cfg_type_view;
108 static cfg_type_t cfg_type_viewopts;
109 static cfg_type_t cfg_type_zone;
110 static cfg_type_t cfg_type_zoneopts;
111 static cfg_type_t cfg_type_dynamically_loadable_zones;
112 static cfg_type_t cfg_type_dynamically_loadable_zones_opts;
113
114 /*
115  * Clauses that can be found in a 'dynamically loadable zones' statement
116  */
117 static cfg_clausedef_t
118 dynamically_loadable_zones_clauses[] = {
119         { "database", &cfg_type_astring, 0 },
120         { NULL, NULL, 0 }
121 };
122
123 /*
124  * A dynamically loadable zones statement.
125  */
126 static cfg_tuplefielddef_t dynamically_loadable_zones_fields[] = {
127         { "name", &cfg_type_astring, 0 },
128         { "options", &cfg_type_dynamically_loadable_zones_opts, 0 },
129         { NULL, NULL, 0 }
130 };
131
132 static cfg_type_t cfg_type_dynamically_loadable_zones = {
133         "dlz", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
134         &cfg_rep_tuple,
135         dynamically_loadable_zones_fields
136         };
137
138
139 /*% tkey-dhkey */
140
141 static cfg_tuplefielddef_t tkey_dhkey_fields[] = {
142         { "name", &cfg_type_qstring, 0 },
143         { "keyid", &cfg_type_uint32, 0 },
144         { NULL, NULL, 0 }
145 };
146
147 static cfg_type_t cfg_type_tkey_dhkey = {
148         "tkey-dhkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
149         tkey_dhkey_fields
150 };
151
152 /*% listen-on */
153
154 static cfg_tuplefielddef_t listenon_fields[] = {
155         { "port", &cfg_type_optional_port, 0 },
156         { "acl", &cfg_type_bracketed_aml, 0 },
157         { NULL, NULL, 0 }
158 };
159 static cfg_type_t cfg_type_listenon = {
160         "listenon", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, listenon_fields };
161
162 /*% acl */
163
164 static cfg_tuplefielddef_t acl_fields[] = {
165         { "name", &cfg_type_astring, 0 },
166         { "value", &cfg_type_bracketed_aml, 0 },
167         { NULL, NULL, 0 }
168 };
169
170 static cfg_type_t cfg_type_acl = {
171         "acl", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, acl_fields };
172
173 /*% masters */
174 static cfg_tuplefielddef_t masters_fields[] = {
175         { "name", &cfg_type_astring, 0 },
176         { "port", &cfg_type_optional_port, 0 },
177         { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
178         { NULL, NULL, 0 }
179 };
180
181 static cfg_type_t cfg_type_masters = {
182         "masters", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, masters_fields };
183
184 /*%
185  * "sockaddrkeylist", a list of socket addresses with optional keys
186  * and an optional default port, as used in the masters option.
187  * E.g.,
188  *   "port 1234 { mymasters; 10.0.0.1 key foo; 1::2 port 69; }"
189  */
190
191 static cfg_tuplefielddef_t namesockaddrkey_fields[] = {
192         { "masterselement", &cfg_type_masterselement, 0 },
193         { "key", &cfg_type_optional_keyref, 0 },
194         { NULL, NULL, 0 },
195 };
196
197 static cfg_type_t cfg_type_namesockaddrkey = {
198         "namesockaddrkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
199         namesockaddrkey_fields
200 };
201
202 static cfg_type_t cfg_type_bracketed_namesockaddrkeylist = {
203         "bracketed_namesockaddrkeylist", cfg_parse_bracketed_list,
204         cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_namesockaddrkey
205 };
206
207 static cfg_tuplefielddef_t namesockaddrkeylist_fields[] = {
208         { "port", &cfg_type_optional_port, 0 },
209         { "addresses", &cfg_type_bracketed_namesockaddrkeylist, 0 },
210         { NULL, NULL, 0 }
211 };
212 static cfg_type_t cfg_type_namesockaddrkeylist = {
213         "sockaddrkeylist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
214         namesockaddrkeylist_fields
215 };
216
217 /*%
218  * A list of socket addresses with an optional default port,
219  * as used in the also-notify option.  E.g.,
220  * "port 1234 { 10.0.0.1; 1::2 port 69; }"
221  */
222 static cfg_tuplefielddef_t portiplist_fields[] = {
223         { "port", &cfg_type_optional_port, 0 },
224         { "addresses", &cfg_type_bracketed_sockaddrlist, 0 },
225         { NULL, NULL, 0 }
226 };
227 static cfg_type_t cfg_type_portiplist = {
228         "portiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
229         portiplist_fields
230 };
231
232 /*%
233  * A public key, as in the "pubkey" statement.
234  */
235 static cfg_tuplefielddef_t pubkey_fields[] = {
236         { "flags", &cfg_type_uint32, 0 },
237         { "protocol", &cfg_type_uint32, 0 },
238         { "algorithm", &cfg_type_uint32, 0 },
239         { "key", &cfg_type_qstring, 0 },
240         { NULL, NULL, 0 }
241 };
242 static cfg_type_t cfg_type_pubkey = {
243         "pubkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, pubkey_fields };
244
245 /*%
246  * A list of RR types, used in grant statements.
247  * Note that the old parser allows quotes around the RR type names.
248  */
249 static cfg_type_t cfg_type_rrtypelist = {
250         "rrtypelist", cfg_parse_spacelist, cfg_print_spacelist, cfg_doc_terminal,
251         &cfg_rep_list, &cfg_type_astring
252 };
253
254 static const char *mode_enums[] = { "grant", "deny", NULL };
255 static cfg_type_t cfg_type_mode = {
256         "mode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
257         &mode_enums
258 };
259
260 static const char *matchtype_enums[] = {
261         "name", "subdomain", "wildcard", "self", "selfsub", "selfwild", NULL };
262 static cfg_type_t cfg_type_matchtype = {
263         "matchtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
264         &matchtype_enums
265 };
266
267 /*%
268  * A grant statement, used in the update policy.
269  */
270 static cfg_tuplefielddef_t grant_fields[] = {
271         { "mode", &cfg_type_mode, 0 },
272         { "identity", &cfg_type_astring, 0 }, /* domain name */
273         { "matchtype", &cfg_type_matchtype, 0 },
274         { "name", &cfg_type_astring, 0 }, /* domain name */
275         { "types", &cfg_type_rrtypelist, 0 },
276         { NULL, NULL, 0 }
277 };
278 static cfg_type_t cfg_type_grant = {
279         "grant", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, grant_fields };
280
281 static cfg_type_t cfg_type_updatepolicy = {
282         "update_policy", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
283         &cfg_rep_list, &cfg_type_grant
284 };
285
286 /*%
287  * A view statement.
288  */
289 static cfg_tuplefielddef_t view_fields[] = {
290         { "name", &cfg_type_astring, 0 },
291         { "class", &cfg_type_optional_class, 0 },
292         { "options", &cfg_type_viewopts, 0 },
293         { NULL, NULL, 0 }
294 };
295 static cfg_type_t cfg_type_view = {
296         "view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, view_fields };
297
298 /*%
299  * A zone statement.
300  */
301 static cfg_tuplefielddef_t zone_fields[] = {
302         { "name", &cfg_type_astring, 0 },
303         { "class", &cfg_type_optional_class, 0 },
304         { "options", &cfg_type_zoneopts, 0 },
305         { NULL, NULL, 0 }
306 };
307 static cfg_type_t cfg_type_zone = {
308         "zone", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, zone_fields };
309
310 /*%
311  * A "category" clause in the "logging" statement.
312  */
313 static cfg_tuplefielddef_t category_fields[] = {
314         { "name", &cfg_type_astring, 0 },
315         { "destinations", &cfg_type_destinationlist,0 },
316         { NULL, NULL, 0 }
317 };
318 static cfg_type_t cfg_type_category = {
319         "category", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple, category_fields };
320
321
322 /*%
323  * A trusted key, as used in the "trusted-keys" statement.
324  */
325 static cfg_tuplefielddef_t trustedkey_fields[] = {
326         { "name", &cfg_type_astring, 0 },
327         { "flags", &cfg_type_uint32, 0 },
328         { "protocol", &cfg_type_uint32, 0 },
329         { "algorithm", &cfg_type_uint32, 0 },
330         { "key", &cfg_type_qstring, 0 },
331         { NULL, NULL, 0 }
332 };
333 static cfg_type_t cfg_type_trustedkey = {
334         "trustedkey", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
335         trustedkey_fields
336 };
337
338 static keyword_type_t wild_class_kw = { "class", &cfg_type_ustring };
339
340 static cfg_type_t cfg_type_optional_wild_class = {
341         "optional_wild_class", parse_optional_keyvalue, print_keyvalue,
342         doc_optional_keyvalue, &cfg_rep_string, &wild_class_kw
343 };
344
345 static keyword_type_t wild_type_kw = { "type", &cfg_type_ustring };
346
347 static cfg_type_t cfg_type_optional_wild_type = {
348         "optional_wild_type", parse_optional_keyvalue,
349         print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_type_kw
350 };
351
352 static keyword_type_t wild_name_kw = { "name", &cfg_type_qstring };
353
354 static cfg_type_t cfg_type_optional_wild_name = {
355         "optional_wild_name", parse_optional_keyvalue,
356         print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &wild_name_kw
357 };
358
359 /*%
360  * An rrset ordering element.
361  */
362 static cfg_tuplefielddef_t rrsetorderingelement_fields[] = {
363         { "class", &cfg_type_optional_wild_class, 0 },
364         { "type", &cfg_type_optional_wild_type, 0 },
365         { "name", &cfg_type_optional_wild_name, 0 },
366         { "order", &cfg_type_ustring, 0 }, /* must be literal "order" */
367         { "ordering", &cfg_type_ustring, 0 },
368         { NULL, NULL, 0 }
369 };
370 static cfg_type_t cfg_type_rrsetorderingelement = {
371         "rrsetorderingelement", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
372         rrsetorderingelement_fields
373 };
374
375 /*%
376  * A global or view "check-names" option.  Note that the zone
377  * "check-names" option has a different syntax.
378  */
379
380 static const char *checktype_enums[] = { "master", "slave", "response", NULL };
381 static cfg_type_t cfg_type_checktype = {
382         "checktype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
383         &cfg_rep_string, &checktype_enums
384 };
385
386 static const char *checkmode_enums[] = { "fail", "warn", "ignore", NULL };
387 static cfg_type_t cfg_type_checkmode = {
388         "checkmode", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
389         &cfg_rep_string, &checkmode_enums
390 };
391
392 static cfg_tuplefielddef_t checknames_fields[] = {
393         { "type", &cfg_type_checktype, 0 },
394         { "mode", &cfg_type_checkmode, 0 },
395         { NULL, NULL, 0 }
396 };
397 static cfg_type_t cfg_type_checknames = {
398         "checknames", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
399         checknames_fields
400 };
401
402 static cfg_type_t cfg_type_bracketed_sockaddrlist = {
403         "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
404         &cfg_rep_list, &cfg_type_sockaddr
405 };
406
407 static cfg_type_t cfg_type_rrsetorder = {
408         "rrsetorder", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
409         &cfg_rep_list, &cfg_type_rrsetorderingelement
410 };
411
412 static keyword_type_t port_kw = { "port", &cfg_type_uint32 };
413
414 static cfg_type_t cfg_type_optional_port = {
415         "optional_port", parse_optional_keyvalue, print_keyvalue,
416         doc_optional_keyvalue, &cfg_rep_uint32, &port_kw
417 };
418
419 /*% A list of keys, as in the "key" clause of the controls statement. */
420 static cfg_type_t cfg_type_keylist = {
421         "keylist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list,
422         &cfg_type_astring
423 };
424
425 static cfg_type_t cfg_type_trustedkeys = {
426         "trusted-keys", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list, &cfg_rep_list,
427         &cfg_type_trustedkey
428 };
429
430 static const char *forwardtype_enums[] = { "first", "only", NULL };
431 static cfg_type_t cfg_type_forwardtype = {
432         "forwardtype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
433         &forwardtype_enums
434 };
435
436 static const char *zonetype_enums[] = {
437         "master", "slave", "stub", "hint", "forward", "delegation-only", NULL };
438 static cfg_type_t cfg_type_zonetype = {
439         "zonetype", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
440         &cfg_rep_string, &zonetype_enums
441 };
442
443 static const char *loglevel_enums[] = {
444         "critical", "error", "warning", "notice", "info", "dynamic", NULL };
445 static cfg_type_t cfg_type_loglevel = {
446         "loglevel", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
447         &loglevel_enums
448 };
449
450 static const char *transferformat_enums[] = {
451         "many-answers", "one-answer", NULL };
452 static cfg_type_t cfg_type_transferformat = {
453         "transferformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum, &cfg_rep_string,
454         &transferformat_enums
455 };
456
457 /*%
458  * The special keyword "none", as used in the pid-file option.
459  */
460
461 static void
462 print_none(cfg_printer_t *pctx, const cfg_obj_t *obj) {
463         UNUSED(obj);
464         cfg_print_chars(pctx, "none", 4);
465 }
466
467 static cfg_type_t cfg_type_none = {
468         "none", NULL, print_none, NULL, &cfg_rep_void, NULL
469 };
470
471 /*%
472  * A quoted string or the special keyword "none".  Used in the pid-file option.
473  */
474 static isc_result_t
475 parse_qstringornone(cfg_parser_t *pctx, const cfg_type_t *type,
476                     cfg_obj_t **ret)
477 {
478         isc_result_t result;
479         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
480         if (pctx->token.type == isc_tokentype_string &&
481             strcasecmp(TOKEN_STRING(pctx), "none") == 0)
482                 return (cfg_create_obj(pctx, &cfg_type_none, ret));
483         cfg_ungettoken(pctx);
484         return (cfg_parse_qstring(pctx, type, ret));
485  cleanup:
486         return (result);
487 }
488
489 static void
490 doc_qstringornone(cfg_printer_t *pctx, const cfg_type_t *type) {
491         UNUSED(type);
492         cfg_print_chars(pctx, "( <quoted_string> | none )", 26);
493 }
494
495 static cfg_type_t cfg_type_qstringornone = {
496         "qstringornone", parse_qstringornone, NULL, doc_qstringornone, NULL, NULL };
497
498 /*%
499  * keyword hostname
500  */
501
502 static void
503 print_hostname(cfg_printer_t *pctx, const cfg_obj_t *obj) {
504         UNUSED(obj);
505         cfg_print_chars(pctx, "hostname", 4);
506 }
507
508 static cfg_type_t cfg_type_hostname = {
509         "hostname", NULL, print_hostname, NULL, &cfg_rep_boolean, NULL
510 };
511
512 /*%
513  * "server-id" argument.
514  */
515
516 static isc_result_t
517 parse_serverid(cfg_parser_t *pctx, const cfg_type_t *type,
518                     cfg_obj_t **ret)
519 {
520         isc_result_t result;
521         CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
522         if (pctx->token.type == isc_tokentype_string &&
523             strcasecmp(TOKEN_STRING(pctx), "none") == 0)
524                 return (cfg_create_obj(pctx, &cfg_type_none, ret));
525         if (pctx->token.type == isc_tokentype_string &&
526             strcasecmp(TOKEN_STRING(pctx), "hostname") == 0) {
527                 return (cfg_create_obj(pctx, &cfg_type_hostname, ret));
528         }
529         cfg_ungettoken(pctx);
530         return (cfg_parse_qstring(pctx, type, ret));
531  cleanup:
532         return (result);
533 }
534
535 static void
536 doc_serverid(cfg_printer_t *pctx, const cfg_type_t *type) {
537         UNUSED(type);
538         cfg_print_chars(pctx, "( <quoted_string> | none | hostname )", 26);
539 }
540
541 static cfg_type_t cfg_type_serverid = {
542         "serverid", parse_serverid, NULL, doc_serverid, NULL, NULL };
543
544 /*%
545  * Port list.
546  */
547 static cfg_tuplefielddef_t porttuple_fields[] = {
548         { "loport", &cfg_type_uint32, 0 },
549         { "hiport", &cfg_type_uint32, 0 },
550         { NULL, NULL, 0 }
551 };
552 static cfg_type_t cfg_type_porttuple = {
553         "porttuple", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
554         &cfg_rep_tuple, porttuple_fields
555 };
556
557 static isc_result_t
558 parse_port(cfg_parser_t *pctx, cfg_obj_t **ret) {
559         isc_result_t result;
560
561         CHECK(cfg_parse_uint32(pctx, NULL, ret));
562         if ((*ret)->value.uint32 > 0xffff) {
563                 cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port");
564                 cfg_obj_destroy(pctx, ret);
565                 result = ISC_R_RANGE;
566         }
567
568  cleanup:
569         return (result);
570 }
571
572 static isc_result_t
573 parse_portrange(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
574         isc_result_t result;
575         cfg_obj_t *obj = NULL;
576
577         UNUSED(type);
578
579         CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
580         if (pctx->token.type == isc_tokentype_number)
581                 CHECK(parse_port(pctx, ret));
582         else {
583                 CHECK(cfg_gettoken(pctx, 0));
584                 if (pctx->token.type != isc_tokentype_string ||
585                     strcasecmp(TOKEN_STRING(pctx), "range") != 0) {
586                         cfg_parser_error(pctx, CFG_LOG_NEAR,
587                                          "expected integer or 'range'");
588                         return (ISC_R_UNEXPECTEDTOKEN);
589                 }
590                 CHECK(cfg_create_tuple(pctx, &cfg_type_porttuple, &obj));
591                 CHECK(parse_port(pctx, &obj->value.tuple[0]));
592                 CHECK(parse_port(pctx, &obj->value.tuple[1]));
593                 if (obj->value.tuple[0]->value.uint32 >
594                     obj->value.tuple[1]->value.uint32) {
595                         cfg_parser_error(pctx, CFG_LOG_NOPREP,
596                                          "low port '%u' must not be larger "
597                                          "than high port",
598                                          obj->value.tuple[0]->value.uint32);
599                         result = ISC_R_RANGE;
600                         goto cleanup;
601                 }
602                 *ret = obj;
603                 obj = NULL;
604         }
605
606  cleanup:
607         if (obj != NULL)
608                 cfg_obj_destroy(pctx, &obj);
609         return (result);
610 }
611
612 static cfg_type_t cfg_type_portrange = {
613         "portrange", parse_portrange, NULL, cfg_doc_terminal,
614         NULL, NULL
615 };
616
617 static cfg_type_t cfg_type_bracketed_portlist = {
618         "bracketed_sockaddrlist", cfg_parse_bracketed_list,
619         cfg_print_bracketed_list, cfg_doc_bracketed_list,
620         &cfg_rep_list, &cfg_type_portrange
621 };
622
623 /*%
624  * Clauses that can be found within the top level of the named.conf
625  * file only.
626  */
627 static cfg_clausedef_t
628 namedconf_clauses[] = {
629         { "options", &cfg_type_options, 0 },
630         { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
631         { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
632         { "masters", &cfg_type_masters, CFG_CLAUSEFLAG_MULTI },
633         { "logging", &cfg_type_logging, 0 },
634         { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
635         { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI },
636         { NULL, NULL, 0 }
637 };
638
639 /*%
640  * Clauses that can occur at the top level or in the view
641  * statement, but not in the options block.
642  */
643 static cfg_clausedef_t
644 namedconf_or_view_clauses[] = {
645         { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
646         { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
647         /* only 1 DLZ per view allowed */
648         { "dlz", &cfg_type_dynamically_loadable_zones, 0 },
649         { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
650         { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI },
651         { NULL, NULL, 0 }
652 };
653
654 /*%
655  * Clauses that can be found within the 'options' statement.
656  */
657 static cfg_clausedef_t
658 options_clauses[] = {
659         { "use-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
660         { "use-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
661         { "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
662         { "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
663         { "blackhole", &cfg_type_bracketed_aml, 0 },
664         { "coresize", &cfg_type_size, 0 },
665         { "datasize", &cfg_type_size, 0 },
666         { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
667         { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
668         { "dump-file", &cfg_type_qstring, 0 },
669         { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
670         { "files", &cfg_type_size, 0 },
671         { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
672         { "heartbeat-interval", &cfg_type_uint32, 0 },
673         { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP },
674         { "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
675         { "hostname", &cfg_type_qstringornone, 0 },
676         { "interface-interval", &cfg_type_uint32, 0 },
677         { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
678         { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
679         { "match-mapped-addresses", &cfg_type_boolean, 0 },
680         { "memstatistics-file", &cfg_type_qstring, 0 },
681         { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
682         { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
683         { "pid-file", &cfg_type_qstringornone, 0 },
684         { "port", &cfg_type_uint32, 0 },
685         { "querylog", &cfg_type_boolean, 0 },
686         { "recursing-file", &cfg_type_qstring, 0 },
687         { "random-device", &cfg_type_qstring, 0 },
688         { "recursive-clients", &cfg_type_uint32, 0 },
689         { "reserved-sockets", &cfg_type_uint32, 0 },
690         { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
691         { "serial-query-rate", &cfg_type_uint32, 0 },
692         { "server-id", &cfg_type_serverid, 0 },
693         { "stacksize", &cfg_type_size, 0 },
694         { "statistics-file", &cfg_type_qstring, 0 },
695         { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI },
696         { "tcp-clients", &cfg_type_uint32, 0 },
697         { "tcp-listen-queue", &cfg_type_uint32, 0 },
698         { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 },
699         { "tkey-gssapi-credential", &cfg_type_qstring, 0 },
700         { "tkey-domain", &cfg_type_qstring, 0 },
701         { "transfers-per-ns", &cfg_type_uint32, 0 },
702         { "transfers-in", &cfg_type_uint32, 0 },
703         { "transfers-out", &cfg_type_uint32, 0 },
704         { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
705         { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
706         { "use-ixfr", &cfg_type_boolean, 0 },
707         { "version", &cfg_type_qstringornone, 0 },
708         { "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
709         { NULL, NULL, 0 }
710 };
711
712
713 static cfg_type_t cfg_type_namelist = {
714         "namelist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
715         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_qstring };
716
717 static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
718
719 static cfg_type_t cfg_type_optional_exclude = {
720         "optional_exclude", parse_optional_keyvalue, print_keyvalue,
721         doc_optional_keyvalue, &cfg_rep_list, &exclude_kw };
722
723 static cfg_type_t cfg_type_algorithmlist = {
724         "algorithmlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
725         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring };
726
727 static cfg_tuplefielddef_t disablealgorithm_fields[] = {
728         { "name", &cfg_type_astring, 0 },
729         { "algorithms", &cfg_type_algorithmlist, 0 },
730         { NULL, NULL, 0 }
731 };
732
733 static cfg_type_t cfg_type_disablealgorithm = {
734         "disablealgorithm", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
735         &cfg_rep_tuple, disablealgorithm_fields
736 };
737
738 static cfg_tuplefielddef_t mustbesecure_fields[] = {
739         { "name", &cfg_type_astring, 0 },
740         { "value", &cfg_type_boolean, 0 },
741         { NULL, NULL, 0 }
742 };
743
744 static cfg_type_t cfg_type_mustbesecure = {
745         "mustbesecure", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
746         &cfg_rep_tuple, mustbesecure_fields
747 };
748
749 static const char *masterformat_enums[] = { "text", "raw", NULL };
750 static cfg_type_t cfg_type_masterformat = {
751         "masterformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
752         &cfg_rep_string, &masterformat_enums
753 };
754
755 /*%
756  * dnssec-lookaside
757  */
758
759 static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring };
760
761 static cfg_type_t cfg_type_trustanchor = {
762         "trust-anchor", parse_keyvalue, print_keyvalue, doc_keyvalue,
763         &cfg_rep_string, &trustanchor_kw
764 };
765
766 static cfg_tuplefielddef_t lookaside_fields[] = {
767         { "domain", &cfg_type_astring, 0 },
768         { "trust-anchor", &cfg_type_trustanchor, 0 },
769         { NULL, NULL, 0 }
770 };
771
772 static cfg_type_t cfg_type_lookaside = {
773         "lookaside", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
774         &cfg_rep_tuple, lookaside_fields
775 };
776
777 /*%
778  * Clauses that can be found within the 'view' statement,
779  * with defaults in the 'options' statement.
780  */
781
782 static cfg_clausedef_t
783 view_clauses[] = {
784         { "allow-query-cache", &cfg_type_bracketed_aml, 0 },
785         { "allow-recursion", &cfg_type_bracketed_aml, 0 },
786         { "allow-v6-synthesis", &cfg_type_bracketed_aml,
787           CFG_CLAUSEFLAG_OBSOLETE },
788         { "sortlist", &cfg_type_bracketed_aml, 0 },
789         { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
790         { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
791         { "minimal-responses", &cfg_type_boolean, 0 },
792         { "recursion", &cfg_type_boolean, 0 },
793         { "rrset-order", &cfg_type_rrsetorder, 0 },
794         { "provide-ixfr", &cfg_type_boolean, 0 },
795         { "request-ixfr", &cfg_type_boolean, 0 },
796         { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
797         { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
798         { "additional-from-auth", &cfg_type_boolean, 0 },
799         { "additional-from-cache", &cfg_type_boolean, 0 },
800         /*
801          * Note that the query-source option syntax is different
802          * from the other -source options.
803          */
804         { "query-source", &cfg_type_querysource4, 0 },
805         { "query-source-v6", &cfg_type_querysource6, 0 },
806         { "cleaning-interval", &cfg_type_uint32, 0 },
807         { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
808         { "lame-ttl", &cfg_type_uint32, 0 },
809         { "max-ncache-ttl", &cfg_type_uint32, 0 },
810         { "max-cache-ttl", &cfg_type_uint32, 0 },
811         { "transfer-format", &cfg_type_transferformat, 0 },
812         { "max-cache-size", &cfg_type_sizenodefault, 0 },
813         { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
814         { "cache-file", &cfg_type_qstring, 0 },
815         { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
816         { "preferred-glue", &cfg_type_astring, 0 },
817         { "dual-stack-servers", &cfg_type_nameportiplist, 0 },
818         { "edns-udp-size", &cfg_type_uint32, 0 },
819         { "max-udp-size", &cfg_type_uint32, 0 },
820         { "root-delegation-only",  &cfg_type_optional_exclude, 0 },
821         { "disable-algorithms", &cfg_type_disablealgorithm,
822           CFG_CLAUSEFLAG_MULTI },
823         { "dnssec-enable", &cfg_type_boolean, 0 },
824         { "dnssec-validation", &cfg_type_boolean, 0 },
825         { "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI },
826         { "dnssec-must-be-secure",  &cfg_type_mustbesecure,
827            CFG_CLAUSEFLAG_MULTI },
828         { "dnssec-accept-expired", &cfg_type_boolean, 0 },
829         { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
830         { "acache-enable", &cfg_type_boolean, 0 },
831         { "acache-cleaning-interval", &cfg_type_uint32, 0 },
832         { "max-acache-size", &cfg_type_sizenodefault, 0 },
833         { "clients-per-query", &cfg_type_uint32, 0 },
834         { "max-clients-per-query", &cfg_type_uint32, 0 },
835         { "empty-server", &cfg_type_astring, 0 },
836         { "empty-contact", &cfg_type_astring, 0 },
837         { "empty-zones-enable", &cfg_type_boolean, 0 },
838         { "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
839         { "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
840         { NULL, NULL, 0 }
841 };
842
843 /*%
844  * Clauses that can be found within the 'view' statement only.
845  */
846 static cfg_clausedef_t
847 view_only_clauses[] = {
848         { "match-clients", &cfg_type_bracketed_aml, 0 },
849         { "match-destinations", &cfg_type_bracketed_aml, 0 },
850         { "match-recursive-only", &cfg_type_boolean, 0 },
851         { NULL, NULL, 0 }
852 };
853
854 /*%
855  * Clauses that can be found in a 'zone' statement,
856  * with defaults in the 'view' or 'options' statement.
857  */
858 static cfg_clausedef_t
859 zone_clauses[] = {
860         { "allow-query", &cfg_type_bracketed_aml, 0 },
861         { "allow-transfer", &cfg_type_bracketed_aml, 0 },
862         { "allow-update", &cfg_type_bracketed_aml, 0 },
863         { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 },
864         { "allow-notify", &cfg_type_bracketed_aml, 0 },
865         { "masterfile-format", &cfg_type_masterformat, 0 },
866         { "notify", &cfg_type_notifytype, 0 },
867         { "notify-source", &cfg_type_sockaddr4wild, 0 },
868         { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
869         { "also-notify", &cfg_type_portiplist, 0 },
870         { "notify-delay", &cfg_type_uint32, 0 },
871         { "dialup", &cfg_type_dialuptype, 0 },
872         { "forward", &cfg_type_forwardtype, 0 },
873         { "forwarders", &cfg_type_portiplist, 0 },
874         { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
875         { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE },
876         { "max-journal-size", &cfg_type_sizenodefault, 0 },
877         { "max-transfer-time-in", &cfg_type_uint32, 0 },
878         { "max-transfer-time-out", &cfg_type_uint32, 0 },
879         { "max-transfer-idle-in", &cfg_type_uint32, 0 },
880         { "max-transfer-idle-out", &cfg_type_uint32, 0 },
881         { "max-retry-time", &cfg_type_uint32, 0 },
882         { "min-retry-time", &cfg_type_uint32, 0 },
883         { "max-refresh-time", &cfg_type_uint32, 0 },
884         { "min-refresh-time", &cfg_type_uint32, 0 },
885         { "multi-master", &cfg_type_boolean, 0 },
886         { "sig-validity-interval", &cfg_type_uint32, 0 },
887         { "transfer-source", &cfg_type_sockaddr4wild, 0 },
888         { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
889         { "alt-transfer-source", &cfg_type_sockaddr4wild, 0 },
890         { "alt-transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
891         { "use-alt-transfer-source", &cfg_type_boolean, 0 },
892         { "zone-statistics", &cfg_type_boolean, 0 },
893         { "key-directory", &cfg_type_qstring, 0 },
894         { "check-wildcard", &cfg_type_boolean, 0 },
895         { "check-integrity", &cfg_type_boolean, 0 },
896         { "check-mx", &cfg_type_checkmode, 0 },
897         { "check-mx-cname", &cfg_type_checkmode, 0 },
898         { "check-srv-cname", &cfg_type_checkmode, 0 },
899         { "check-sibling", &cfg_type_boolean, 0 },
900         { "zero-no-soa-ttl", &cfg_type_boolean, 0 },
901         { "update-check-ksk", &cfg_type_boolean, 0 },
902         { NULL, NULL, 0 }
903 };
904
905 /*%
906  * Clauses that can be found in a 'zone' statement
907  * only.
908  */
909 static cfg_clausedef_t
910 zone_only_clauses[] = {
911         { "type", &cfg_type_zonetype, 0 },
912         { "file", &cfg_type_qstring, 0 },
913         { "journal", &cfg_type_qstring, 0 },
914         { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
915         { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
916         { "masters", &cfg_type_namesockaddrkeylist, 0 },
917         { "pubkey", &cfg_type_pubkey,
918           CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
919         { "update-policy", &cfg_type_updatepolicy, 0 },
920         { "database", &cfg_type_astring, 0 },
921         { "delegation-only", &cfg_type_boolean, 0 },
922         /*
923          * Note that the format of the check-names option is different between
924          * the zone options and the global/view options.  Ugh.
925          */
926         { "check-names", &cfg_type_checkmode, 0 },
927         { "ixfr-from-differences", &cfg_type_boolean, 0 },
928         { NULL, NULL, 0 }
929 };
930
931
932 /*% The top-level named.conf syntax. */
933
934 static cfg_clausedef_t *
935 namedconf_clausesets[] = {
936         namedconf_clauses,
937         namedconf_or_view_clauses,
938         NULL
939 };
940
941 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
942         "namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
943         &cfg_rep_map, namedconf_clausesets
944 };
945
946 /*% The "options" statement syntax. */
947
948 static cfg_clausedef_t *
949 options_clausesets[] = {
950         options_clauses,
951         view_clauses,
952         zone_clauses,
953         NULL
954 };
955 static cfg_type_t cfg_type_options = {
956         "options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, options_clausesets };
957
958 /*% The "view" statement syntax. */
959
960 static cfg_clausedef_t *
961 view_clausesets[] = {
962         view_only_clauses,
963         namedconf_or_view_clauses,
964         view_clauses,
965         zone_clauses,
966         dynamically_loadable_zones_clauses,
967         NULL
968 };
969 static cfg_type_t cfg_type_viewopts = {
970         "view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, view_clausesets };
971
972 /*% The "zone" statement syntax. */
973
974 static cfg_clausedef_t *
975 zone_clausesets[] = {
976         zone_only_clauses,
977         zone_clauses,
978         NULL
979 };
980 static cfg_type_t cfg_type_zoneopts = {
981         "zoneopts", cfg_parse_map, cfg_print_map,
982         cfg_doc_map, &cfg_rep_map, zone_clausesets };
983
984 /*% The "dynamically loadable zones" statement syntax. */
985
986 static cfg_clausedef_t *
987 dynamically_loadable_zones_clausesets[] = {
988         dynamically_loadable_zones_clauses,
989         NULL
990 };
991 static cfg_type_t cfg_type_dynamically_loadable_zones_opts = {
992         "dynamically_loadable_zones_opts", cfg_parse_map,
993         cfg_print_map, cfg_doc_map, &cfg_rep_map,
994         dynamically_loadable_zones_clausesets
995 };
996
997 /*%
998  * Clauses that can be found within the 'key' statement.
999  */
1000 static cfg_clausedef_t
1001 key_clauses[] = {
1002         { "algorithm", &cfg_type_astring, 0 },
1003         { "secret", &cfg_type_astring, 0 },
1004         { NULL, NULL, 0 }
1005 };
1006
1007 static cfg_clausedef_t *
1008 key_clausesets[] = {
1009         key_clauses,
1010         NULL
1011 };
1012 static cfg_type_t cfg_type_key = {
1013         "key", cfg_parse_named_map, cfg_print_map,
1014         cfg_doc_map, &cfg_rep_map, key_clausesets
1015 };
1016
1017
1018 /*%
1019  * Clauses that can be found in a 'server' statement.
1020  */
1021 static cfg_clausedef_t
1022 server_clauses[] = {
1023         { "bogus", &cfg_type_boolean, 0 },
1024         { "provide-ixfr", &cfg_type_boolean, 0 },
1025         { "request-ixfr", &cfg_type_boolean, 0 },
1026         { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
1027         { "transfers", &cfg_type_uint32, 0 },
1028         { "transfer-format", &cfg_type_transferformat, 0 },
1029         { "keys", &cfg_type_server_key_kludge, 0 },
1030         { "edns", &cfg_type_boolean, 0 },
1031         { "edns-udp-size", &cfg_type_uint32, 0 },
1032         { "max-udp-size", &cfg_type_uint32, 0 },
1033         { "notify-source", &cfg_type_sockaddr4wild, 0 },
1034         { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
1035         { "query-source", &cfg_type_querysource4, 0 },
1036         { "query-source-v6", &cfg_type_querysource6, 0 },
1037         { "transfer-source", &cfg_type_sockaddr4wild, 0 },
1038         { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
1039         { NULL, NULL, 0 }
1040 };
1041 static cfg_clausedef_t *
1042 server_clausesets[] = {
1043         server_clauses,
1044         NULL
1045 };
1046 static cfg_type_t cfg_type_server = {
1047         "server", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
1048         server_clausesets
1049 };
1050
1051
1052 /*%
1053  * Clauses that can be found in a 'channel' clause in the
1054  * 'logging' statement.
1055  *
1056  * These have some additional constraints that need to be
1057  * checked after parsing:
1058  *  - There must exactly one of file/syslog/null/stderr
1059  *
1060  */
1061 static cfg_clausedef_t
1062 channel_clauses[] = {
1063         /* Destinations.  We no longer require these to be first. */
1064         { "file", &cfg_type_logfile, 0 },
1065         { "syslog", &cfg_type_optional_facility, 0 },
1066         { "null", &cfg_type_void, 0 },
1067         { "stderr", &cfg_type_void, 0 },
1068         /* Options.  We now accept these for the null channel, too. */
1069         { "severity", &cfg_type_logseverity, 0 },
1070         { "print-time", &cfg_type_boolean, 0 },
1071         { "print-severity", &cfg_type_boolean, 0 },
1072         { "print-category", &cfg_type_boolean, 0 },
1073         { NULL, NULL, 0 }
1074 };
1075 static cfg_clausedef_t *
1076 channel_clausesets[] = {
1077         channel_clauses,
1078         NULL
1079 };
1080 static cfg_type_t cfg_type_channel = {
1081         "channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
1082         &cfg_rep_map, channel_clausesets
1083 };
1084
1085 /*% A list of log destination, used in the "category" clause. */
1086 static cfg_type_t cfg_type_destinationlist = {
1087         "destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
1088         &cfg_rep_list, &cfg_type_astring };
1089
1090 /*%
1091  * Clauses that can be found in a 'logging' statement.
1092  */
1093 static cfg_clausedef_t
1094 logging_clauses[] = {
1095         { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
1096         { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
1097         { NULL, NULL, 0 }
1098 };
1099 static cfg_clausedef_t *
1100 logging_clausesets[] = {
1101         logging_clauses,
1102         NULL
1103 };
1104 static cfg_type_t cfg_type_logging = {
1105         "logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets };
1106
1107
1108 static isc_result_t
1109 parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
1110         char *endp;
1111         unsigned int len;
1112         isc_uint64_t value;
1113         isc_uint64_t unit;
1114
1115         value = isc_string_touint64(str, &endp, 10);
1116         if (*endp == 0) {
1117                 *valuep = value;
1118                 return (ISC_R_SUCCESS);
1119         }
1120
1121         len = strlen(str);
1122         if (len < 2 || endp[1] != '\0')
1123                 return (ISC_R_FAILURE);
1124
1125         switch (str[len - 1]) {
1126         case 'k':
1127         case 'K':
1128                 unit = 1024;
1129                 break;
1130         case 'm':
1131         case 'M':
1132                 unit = 1024 * 1024;
1133                 break;
1134         case 'g':
1135         case 'G':
1136                 unit = 1024 * 1024 * 1024;
1137                 break;
1138         default:
1139                 return (ISC_R_FAILURE);
1140         }
1141         if (value > ISC_UINT64_MAX / unit)
1142                 return (ISC_R_FAILURE);
1143         *valuep = value * unit;
1144         return (ISC_R_SUCCESS);
1145 }
1146
1147 static isc_result_t
1148 parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1149         isc_result_t result;
1150         cfg_obj_t *obj = NULL;
1151         isc_uint64_t val;
1152
1153         UNUSED(type);
1154
1155         CHECK(cfg_gettoken(pctx, 0));
1156         if (pctx->token.type != isc_tokentype_string) {
1157                 result = ISC_R_UNEXPECTEDTOKEN;
1158                 goto cleanup;
1159         }
1160         CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
1161
1162         CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
1163         obj->value.uint64 = val;
1164         *ret = obj;
1165         return (ISC_R_SUCCESS);
1166
1167  cleanup:
1168         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer and optional unit");
1169         return (result);
1170 }
1171
1172 /*%
1173  * A size value (number + optional unit).
1174  */
1175 static cfg_type_t cfg_type_sizeval = {
1176         "sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal,
1177         &cfg_rep_uint64, NULL };
1178
1179 /*%
1180  * A size, "unlimited", or "default".
1181  */
1182
1183 static isc_result_t
1184 parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1185         return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
1186 }
1187
1188 static const char *size_enums[] = { "unlimited", "default", NULL };
1189 static cfg_type_t cfg_type_size = {
1190         "size", parse_size, cfg_print_ustring, cfg_doc_terminal,
1191         &cfg_rep_string, size_enums
1192 };
1193
1194 /*%
1195  * A size or "unlimited", but not "default".
1196  */
1197 static const char *sizenodefault_enums[] = { "unlimited", NULL };
1198 static cfg_type_t cfg_type_sizenodefault = {
1199         "size_no_default", parse_size, cfg_print_ustring, cfg_doc_terminal,
1200         &cfg_rep_string, sizenodefault_enums
1201 };
1202
1203 /*%
1204  * optional_keyvalue
1205  */
1206 static isc_result_t
1207 parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
1208                               isc_boolean_t optional, cfg_obj_t **ret)
1209 {
1210         isc_result_t result;
1211         cfg_obj_t *obj = NULL;
1212         const keyword_type_t *kw = type->of;
1213
1214         CHECK(cfg_peektoken(pctx, 0));
1215         if (pctx->token.type == isc_tokentype_string &&
1216             strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) {
1217                 CHECK(cfg_gettoken(pctx, 0));
1218                 CHECK(kw->type->parse(pctx, kw->type, &obj));
1219                 obj->type = type; /* XXX kludge */
1220         } else {
1221                 if (optional) {
1222                         CHECK(cfg_parse_void(pctx, NULL, &obj));
1223                 } else {
1224                         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'",
1225                                      kw->name);
1226                         result = ISC_R_UNEXPECTEDTOKEN;
1227                         goto cleanup;
1228                 }
1229         }
1230         *ret = obj;
1231  cleanup:
1232         return (result);
1233 }
1234
1235 static isc_result_t
1236 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
1237                     const cfg_type_t *othertype, cfg_obj_t **ret)
1238 {
1239         isc_result_t result;
1240         CHECK(cfg_peektoken(pctx, 0));
1241         if (pctx->token.type == isc_tokentype_string &&
1242             cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) {
1243                 CHECK(cfg_parse_enum(pctx, enumtype, ret));
1244         } else {
1245                 CHECK(cfg_parse_obj(pctx, othertype, ret));
1246         }
1247  cleanup:
1248         return (result);
1249 }
1250
1251 static void
1252 doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type) {
1253         cfg_doc_terminal(pctx, type);
1254 #if 0 /* XXX */
1255         cfg_print_chars(pctx, "( ", 2);...
1256 #endif
1257
1258 }
1259
1260 static isc_result_t
1261 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1262         return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret));
1263 }
1264
1265 static isc_result_t
1266 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1267         return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret));
1268 }
1269
1270 static void
1271 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1272         const keyword_type_t *kw = obj->type->of;
1273         cfg_print_cstr(pctx, kw->name);
1274         cfg_print_chars(pctx, " ", 1);
1275         kw->type->print(pctx, obj);
1276 }
1277
1278 static void
1279 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
1280         const keyword_type_t *kw = type->of;
1281         cfg_print_cstr(pctx, kw->name);
1282         cfg_print_chars(pctx, " ", 1);
1283         cfg_doc_obj(pctx, kw->type);
1284 }
1285
1286 static void
1287 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
1288         const keyword_type_t *kw = type->of;
1289         cfg_print_chars(pctx, "[ ", 2);
1290         cfg_print_cstr(pctx, kw->name);
1291         cfg_print_chars(pctx, " ", 1);
1292         cfg_doc_obj(pctx, kw->type);
1293         cfg_print_chars(pctx, " ]", 2);
1294 }
1295
1296 static const char *dialup_enums[] = {
1297         "notify", "notify-passive", "refresh", "passive", NULL };
1298 static isc_result_t
1299 parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1300         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1301 }
1302 static cfg_type_t cfg_type_dialuptype = {
1303         "dialuptype", parse_dialup_type, cfg_print_ustring, doc_enum_or_other,
1304         &cfg_rep_string, dialup_enums
1305 };
1306
1307 static const char *notify_enums[] = { "explicit", "master-only", NULL };
1308 static isc_result_t
1309 parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1310         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1311 }
1312 static cfg_type_t cfg_type_notifytype = {
1313         "notifytype", parse_notify_type, cfg_print_ustring, doc_enum_or_other,
1314         &cfg_rep_string, notify_enums,
1315 };
1316
1317 static const char *ixfrdiff_enums[] = { "master", "slave", NULL };
1318 static isc_result_t
1319 parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1320         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1321 }
1322 static cfg_type_t cfg_type_ixfrdifftype = {
1323         "ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring, doc_enum_or_other,
1324         &cfg_rep_string, ixfrdiff_enums,
1325 };
1326
1327 static keyword_type_t key_kw = { "key", &cfg_type_astring };
1328
1329 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
1330         "keyref", parse_keyvalue, print_keyvalue, doc_keyvalue,
1331         &cfg_rep_string, &key_kw
1332 };
1333
1334 static cfg_type_t cfg_type_optional_keyref = {
1335         "optional_keyref", parse_optional_keyvalue, print_keyvalue,
1336         doc_optional_keyvalue, &cfg_rep_string, &key_kw
1337 };
1338
1339 /*%
1340  * A "controls" statement is represented as a map with the multivalued
1341  * "inet" and "unix" clauses.
1342  */
1343
1344 static keyword_type_t controls_allow_kw = {
1345         "allow", &cfg_type_bracketed_aml };
1346
1347 static cfg_type_t cfg_type_controls_allow = {
1348         "controls_allow", parse_keyvalue,
1349         print_keyvalue, doc_keyvalue,
1350         &cfg_rep_list, &controls_allow_kw
1351 };
1352
1353 static keyword_type_t controls_keys_kw = {
1354         "keys", &cfg_type_keylist };
1355
1356 static cfg_type_t cfg_type_controls_keys = {
1357         "controls_keys", parse_optional_keyvalue,
1358         print_keyvalue, doc_optional_keyvalue,
1359         &cfg_rep_list, &controls_keys_kw
1360 };
1361
1362 static cfg_tuplefielddef_t inetcontrol_fields[] = {
1363         { "address", &cfg_type_controls_sockaddr, 0 },
1364         { "allow", &cfg_type_controls_allow, 0 },
1365         { "keys", &cfg_type_controls_keys, 0 },
1366         { NULL, NULL, 0 }
1367 };
1368
1369 static cfg_type_t cfg_type_inetcontrol = {
1370         "inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1371         inetcontrol_fields
1372 };
1373
1374 static keyword_type_t controls_perm_kw = {
1375         "perm", &cfg_type_uint32 };
1376
1377 static cfg_type_t cfg_type_controls_perm = {
1378         "controls_perm", parse_keyvalue,
1379         print_keyvalue, doc_keyvalue,
1380         &cfg_rep_uint32, &controls_perm_kw
1381 };
1382
1383 static keyword_type_t controls_owner_kw = {
1384         "owner", &cfg_type_uint32 };
1385
1386 static cfg_type_t cfg_type_controls_owner = {
1387         "controls_owner", parse_keyvalue,
1388         print_keyvalue, doc_keyvalue,
1389         &cfg_rep_uint32, &controls_owner_kw
1390 };
1391
1392 static keyword_type_t controls_group_kw = {
1393         "group", &cfg_type_uint32 };
1394
1395 static cfg_type_t cfg_type_controls_group = {
1396         "controls_allow", parse_keyvalue,
1397         print_keyvalue, doc_keyvalue,
1398         &cfg_rep_uint32, &controls_group_kw
1399 };
1400
1401 static cfg_tuplefielddef_t unixcontrol_fields[] = {
1402         { "path", &cfg_type_qstring, 0 },
1403         { "perm", &cfg_type_controls_perm, 0 },
1404         { "owner", &cfg_type_controls_owner, 0 },
1405         { "group", &cfg_type_controls_group, 0 },
1406         { "keys", &cfg_type_controls_keys, 0 },
1407         { NULL, NULL, 0 }
1408 };
1409
1410 static cfg_type_t cfg_type_unixcontrol = {
1411         "unixcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1412         unixcontrol_fields
1413 };
1414
1415 static cfg_clausedef_t
1416 controls_clauses[] = {
1417         { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
1418         { "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI },
1419         { NULL, NULL, 0 }
1420 };
1421
1422 static cfg_clausedef_t *
1423 controls_clausesets[] = {
1424         controls_clauses,
1425         NULL
1426 };
1427 static cfg_type_t cfg_type_controls = {
1428         "controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,    &controls_clausesets
1429 };
1430
1431 /*%
1432  * An optional class, as used in view and zone statements.
1433  */
1434 static isc_result_t
1435 parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1436         isc_result_t result;
1437         UNUSED(type);
1438         CHECK(cfg_peektoken(pctx, 0));
1439         if (pctx->token.type == isc_tokentype_string)
1440                 CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret));
1441         else
1442                 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1443  cleanup:
1444         return (result);
1445 }
1446
1447 static cfg_type_t cfg_type_optional_class = {
1448         "optional_class", parse_optional_class, NULL, cfg_doc_terminal,
1449         NULL, NULL
1450 };
1451
1452 static isc_result_t
1453 parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1454         isc_result_t result;
1455         cfg_obj_t *obj = NULL;
1456         isc_netaddr_t netaddr;
1457         in_port_t port;
1458         unsigned int have_address = 0;
1459         unsigned int have_port = 0;
1460         const unsigned int *flagp = type->of;
1461
1462         if ((*flagp & CFG_ADDR_V4OK) != 0)
1463                 isc_netaddr_any(&netaddr);
1464         else if ((*flagp & CFG_ADDR_V6OK) != 0)
1465                 isc_netaddr_any6(&netaddr);
1466         else
1467                 INSIST(0);
1468
1469         port = 0;
1470
1471         for (;;) {
1472                 CHECK(cfg_peektoken(pctx, 0));
1473                 if (pctx->token.type == isc_tokentype_string) {
1474                         if (strcasecmp(TOKEN_STRING(pctx),
1475                                        "address") == 0)
1476                         {
1477                                 /* read "address" */
1478                                 CHECK(cfg_gettoken(pctx, 0));
1479                                 CHECK(cfg_parse_rawaddr(pctx, *flagp,
1480                                                         &netaddr));
1481                                 have_address++;
1482                         } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
1483                         {
1484                                 /* read "port" */
1485                                 CHECK(cfg_gettoken(pctx, 0));
1486                                 CHECK(cfg_parse_rawport(pctx,
1487                                                         CFG_ADDR_WILDOK,
1488                                                         &port));
1489                                 have_port++;
1490                         } else if (have_port == 0 && have_address == 0) {
1491                                 return (cfg_parse_sockaddr(pctx, type, ret));
1492                         } else {
1493                                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1494                                              "expected 'address' or 'port'");
1495                                 return (ISC_R_UNEXPECTEDTOKEN);
1496                         }
1497                 } else
1498                         break;
1499         }
1500         if (have_address > 1 || have_port > 1 ||
1501             have_address + have_port == 0) {
1502                 cfg_parser_error(pctx, 0, "expected one address and/or port");
1503                 return (ISC_R_UNEXPECTEDTOKEN);
1504         }
1505
1506         CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj));
1507         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
1508         *ret = obj;
1509         return (ISC_R_SUCCESS);
1510
1511  cleanup:
1512         cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
1513         CLEANUP_OBJ(obj);
1514         return (result);
1515 }
1516
1517 static void
1518 print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1519         isc_netaddr_t na;
1520         isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
1521         cfg_print_chars(pctx, "address ", 8);
1522         cfg_print_rawaddr(pctx, &na);
1523         cfg_print_chars(pctx, " port ", 6);
1524         cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
1525 }
1526
1527 static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK;
1528 static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK;
1529 static cfg_type_t cfg_type_querysource4 = {
1530         "querysource4", parse_querysource, NULL, cfg_doc_terminal,
1531         NULL, &sockaddr4wild_flags
1532 };
1533
1534 static cfg_type_t cfg_type_querysource6 = {
1535         "querysource6", parse_querysource, NULL, cfg_doc_terminal,
1536         NULL, &sockaddr6wild_flags
1537 };
1538
1539 static cfg_type_t cfg_type_querysource = {
1540         "querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL
1541 };
1542
1543 /*% addrmatchelt */
1544
1545 static isc_result_t
1546 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1547         isc_result_t result;
1548         UNUSED(type);
1549
1550         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1551
1552         if (pctx->token.type == isc_tokentype_string ||
1553             pctx->token.type == isc_tokentype_qstring) {
1554                 if (pctx->token.type == isc_tokentype_string &&
1555                     (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) {
1556                         CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
1557                 } else {
1558                         if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK |
1559                                                   CFG_ADDR_V4PREFIXOK |
1560                                                   CFG_ADDR_V6OK))
1561                         {
1562                                 CHECK(cfg_parse_netprefix(pctx, NULL, ret));
1563                         } else {
1564                                 CHECK(cfg_parse_astring(pctx, NULL, ret));
1565                         }
1566                 }
1567         } else if (pctx->token.type == isc_tokentype_special) {
1568                 if (pctx->token.value.as_char == '{') {
1569                         /* Nested match list. */
1570                         CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml, ret));
1571                 } else if (pctx->token.value.as_char == '!') {
1572                         CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
1573                         CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
1574                 } else {
1575                         goto bad;
1576                 }
1577         } else {
1578         bad:
1579                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1580                              "expected IP match list element");
1581                 return (ISC_R_UNEXPECTEDTOKEN);
1582         }
1583  cleanup:
1584         return (result);
1585 }
1586
1587 /*%
1588  * A negated address match list element (like "! 10.0.0.1").
1589  * Somewhat sneakily, the caller is expected to parse the
1590  * "!", but not to print it.
1591  */
1592
1593 static cfg_tuplefielddef_t negated_fields[] = {
1594         { "value", &cfg_type_addrmatchelt, 0 },
1595         { NULL, NULL, 0 }
1596 };
1597
1598 static void
1599 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1600         cfg_print_chars(pctx, "!", 1);
1601         cfg_print_tuple(pctx, obj);
1602 }
1603
1604 static cfg_type_t cfg_type_negated = {
1605         "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple,
1606         &negated_fields
1607 };
1608
1609 /*% An address match list element */
1610
1611 static cfg_type_t cfg_type_addrmatchelt = {
1612         "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal,
1613         NULL, NULL
1614 };
1615
1616 /*% A bracketed address match list */
1617
1618 static cfg_type_t cfg_type_bracketed_aml = {
1619         "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1620         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt
1621 };
1622
1623 /*%
1624  * The socket address syntax in the "controls" statement is silly.
1625  * It allows both socket address families, but also allows "*",
1626  * whis is gratuitously interpreted as the IPv4 wildcard address.
1627  */
1628 static unsigned int controls_sockaddr_flags =
1629         CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1630 static cfg_type_t cfg_type_controls_sockaddr = {
1631         "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr,
1632         cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags
1633 };
1634
1635 /*%
1636  * Handle the special kludge syntax of the "keys" clause in the "server"
1637  * statement, which takes a single key with or without braces and semicolon.
1638  */
1639 static isc_result_t
1640 parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1641 {
1642         isc_result_t result;
1643         isc_boolean_t braces = ISC_FALSE;
1644         UNUSED(type);
1645
1646         /* Allow opening brace. */
1647         CHECK(cfg_peektoken(pctx, 0));
1648         if (pctx->token.type == isc_tokentype_special &&
1649             pctx->token.value.as_char == '{') {
1650                 result = cfg_gettoken(pctx, 0);
1651                 braces = ISC_TRUE;
1652         }
1653
1654         CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
1655
1656         if (braces) {
1657                 /* Skip semicolon if present. */
1658                 CHECK(cfg_peektoken(pctx, 0));
1659                 if (pctx->token.type == isc_tokentype_special &&
1660                     pctx->token.value.as_char == ';')
1661                         CHECK(cfg_gettoken(pctx, 0));
1662
1663                 CHECK(cfg_parse_special(pctx, '}'));
1664         }
1665  cleanup:
1666         return (result);
1667 }
1668 static cfg_type_t cfg_type_server_key_kludge = {
1669         "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal,
1670         NULL, NULL
1671 };
1672
1673
1674 /*%
1675  * An optional logging facility.
1676  */
1677
1678 static isc_result_t
1679 parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1680 {
1681         isc_result_t result;
1682         UNUSED(type);
1683
1684         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1685         if (pctx->token.type == isc_tokentype_string ||
1686             pctx->token.type == isc_tokentype_qstring) {
1687                 CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
1688         } else {
1689                 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1690         }
1691  cleanup:
1692         return (result);
1693 }
1694
1695 static cfg_type_t cfg_type_optional_facility = {
1696         "optional_facility", parse_optional_facility, NULL, cfg_doc_terminal,
1697         NULL, NULL };
1698
1699
1700 /*%
1701  * A log severity.  Return as a string, except "debug N",
1702  * which is returned as a keyword object.
1703  */
1704
1705 static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
1706 static cfg_type_t cfg_type_debuglevel = {
1707         "debuglevel", parse_keyvalue,
1708         print_keyvalue, doc_keyvalue,
1709         &cfg_rep_uint32, &debug_kw
1710 };
1711
1712 static isc_result_t
1713 parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1714         isc_result_t result;
1715         UNUSED(type);
1716
1717         CHECK(cfg_peektoken(pctx, 0));
1718         if (pctx->token.type == isc_tokentype_string &&
1719             strcasecmp(TOKEN_STRING(pctx), "debug") == 0) {
1720                 CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
1721                 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
1722                 if (pctx->token.type == isc_tokentype_number) {
1723                         CHECK(cfg_parse_uint32(pctx, NULL, ret));
1724                 } else {
1725                         /*
1726                          * The debug level is optional and defaults to 1.
1727                          * This makes little sense, but we support it for
1728                          * compatibility with BIND 8.
1729                          */
1730                         CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret));
1731                         (*ret)->value.uint32 = 1;
1732                 }
1733                 (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
1734         } else {
1735                 CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret));
1736         }
1737  cleanup:
1738         return (result);
1739 }
1740
1741 static cfg_type_t cfg_type_logseverity = {
1742         "log_severity", parse_logseverity, NULL, cfg_doc_terminal,
1743         NULL, NULL };
1744
1745 /*%
1746  * The "file" clause of the "channel" statement.
1747  * This is yet another special case.
1748  */
1749
1750 static const char *logversions_enums[] = { "unlimited", NULL };
1751 static isc_result_t
1752 parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1753         return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
1754 }
1755
1756 static cfg_type_t cfg_type_logversions = {
1757         "logversions", parse_logversions, cfg_print_ustring, cfg_doc_terminal,
1758         &cfg_rep_string, logversions_enums
1759 };
1760
1761 static cfg_tuplefielddef_t logfile_fields[] = {
1762         { "file", &cfg_type_qstring, 0 },
1763         { "versions", &cfg_type_logversions, 0 },
1764         { "size", &cfg_type_size, 0 },
1765         { NULL, NULL, 0 }
1766 };
1767
1768 static isc_result_t
1769 parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1770         isc_result_t result;
1771         cfg_obj_t *obj = NULL;
1772         const cfg_tuplefielddef_t *fields = type->of;
1773
1774         CHECK(cfg_create_tuple(pctx, type, &obj));
1775
1776         /* Parse the mandatory "file" field */
1777         CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1778
1779         /* Parse "versions" and "size" fields in any order. */
1780         for (;;) {
1781                 CHECK(cfg_peektoken(pctx, 0));
1782                 if (pctx->token.type == isc_tokentype_string) {
1783                         CHECK(cfg_gettoken(pctx, 0));
1784                         if (strcasecmp(TOKEN_STRING(pctx),
1785                                        "versions") == 0 &&
1786                             obj->value.tuple[1] == NULL) {
1787                                 CHECK(cfg_parse_obj(pctx, fields[1].type,
1788                                             &obj->value.tuple[1]));
1789                         } else if (strcasecmp(TOKEN_STRING(pctx),
1790                                               "size") == 0 &&
1791                                    obj->value.tuple[2] == NULL) {
1792                                 CHECK(cfg_parse_obj(pctx, fields[2].type,
1793                                             &obj->value.tuple[2]));
1794                         } else {
1795                                 break;
1796                         }
1797                 } else {
1798                         break;
1799                 }
1800         }
1801
1802         /* Create void objects for missing optional values. */
1803         if (obj->value.tuple[1] == NULL)
1804                 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
1805         if (obj->value.tuple[2] == NULL)
1806                 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
1807
1808         *ret = obj;
1809         return (ISC_R_SUCCESS);
1810
1811  cleanup:
1812         CLEANUP_OBJ(obj);
1813         return (result);
1814 }
1815
1816 static void
1817 print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1818         cfg_print_obj(pctx, obj->value.tuple[0]); /* file */
1819         if (obj->value.tuple[1]->type->print != cfg_print_void) {
1820                 cfg_print_chars(pctx, " versions ", 10);
1821                 cfg_print_obj(pctx, obj->value.tuple[1]);
1822         }
1823         if (obj->value.tuple[2]->type->print != cfg_print_void) {
1824                 cfg_print_chars(pctx, " size ", 6);
1825                 cfg_print_obj(pctx, obj->value.tuple[2]);
1826         }
1827 }
1828
1829
1830 static void
1831 doc_logfile(cfg_printer_t *pctx, const cfg_type_t *type) {
1832         UNUSED(type);
1833         cfg_print_cstr(pctx, "<quoted_string>");
1834         cfg_print_chars(pctx, " ", 1);
1835         cfg_print_cstr(pctx, "[ versions ( \"unlimited\" | <integer> ) ]");
1836         cfg_print_chars(pctx, " ", 1);
1837         cfg_print_cstr(pctx, "[ size <size> ]");
1838 }
1839
1840 static cfg_type_t cfg_type_logfile = {
1841         "log_file", parse_logfile, print_logfile, doc_logfile,
1842         &cfg_rep_tuple, logfile_fields
1843 };
1844
1845 /*% An IPv4 address with optional port, "*" accepted as wildcard. */
1846 static cfg_type_t cfg_type_sockaddr4wild = {
1847         "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr,
1848         cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags
1849 };
1850
1851 /*% An IPv6 address with optional port, "*" accepted as wildcard. */
1852 static cfg_type_t cfg_type_sockaddr6wild = {
1853         "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr,
1854         cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags
1855 };
1856
1857 /*%
1858  * lwres
1859  */
1860
1861 static cfg_tuplefielddef_t lwres_view_fields[] = {
1862         { "name", &cfg_type_astring, 0 },
1863         { "class", &cfg_type_optional_class, 0 },
1864         { NULL, NULL, 0 }
1865 };
1866 static cfg_type_t cfg_type_lwres_view = {
1867         "lwres_view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1868         lwres_view_fields
1869 };
1870
1871 static cfg_type_t cfg_type_lwres_searchlist = {
1872         "lwres_searchlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1873         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring };
1874
1875 static cfg_clausedef_t
1876 lwres_clauses[] = {
1877         { "listen-on", &cfg_type_portiplist, 0 },
1878         { "view", &cfg_type_lwres_view, 0 },
1879         { "search", &cfg_type_lwres_searchlist, 0 },
1880         { "ndots", &cfg_type_uint32, 0 },
1881         { NULL, NULL, 0 }
1882 };
1883
1884 static cfg_clausedef_t *
1885 lwres_clausesets[] = {
1886         lwres_clauses,
1887         NULL
1888 };
1889 static cfg_type_t cfg_type_lwres = {
1890         "lwres", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
1891         lwres_clausesets
1892 };
1893
1894 /*%
1895  * rndc
1896  */
1897
1898 static cfg_clausedef_t
1899 rndcconf_options_clauses[] = {
1900         { "default-key", &cfg_type_astring, 0 },
1901         { "default-port", &cfg_type_uint32, 0 },
1902         { "default-server", &cfg_type_astring, 0 },
1903         { "default-source-address", &cfg_type_netaddr4wild, 0 },
1904         { "default-source-address-v6", &cfg_type_netaddr6wild, 0 },
1905         { NULL, NULL, 0 }
1906 };
1907
1908 static cfg_clausedef_t *
1909 rndcconf_options_clausesets[] = {
1910         rndcconf_options_clauses,
1911         NULL
1912 };
1913
1914 static cfg_type_t cfg_type_rndcconf_options = {
1915         "rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map,
1916         &cfg_rep_map, rndcconf_options_clausesets
1917 };
1918
1919 static cfg_clausedef_t
1920 rndcconf_server_clauses[] = {
1921         { "key", &cfg_type_astring, 0 },
1922         { "port", &cfg_type_uint32, 0 },
1923         { "source-address", &cfg_type_netaddr4wild, 0 },
1924         { "source-address-v6", &cfg_type_netaddr6wild, 0 },
1925         { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
1926         { NULL, NULL, 0 }
1927 };
1928
1929 static cfg_clausedef_t *
1930 rndcconf_server_clausesets[] = {
1931         rndcconf_server_clauses,
1932         NULL
1933 };
1934
1935 static cfg_type_t cfg_type_rndcconf_server = {
1936         "rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
1937         &cfg_rep_map, rndcconf_server_clausesets
1938 };
1939
1940 static cfg_clausedef_t
1941 rndcconf_clauses[] = {
1942         { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
1943         { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
1944         { "options", &cfg_type_rndcconf_options, 0 },
1945         { NULL, NULL, 0 }
1946 };
1947
1948 static cfg_clausedef_t *
1949 rndcconf_clausesets[] = {
1950         rndcconf_clauses,
1951         NULL
1952 };
1953
1954 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
1955         "rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1956         &cfg_rep_map, rndcconf_clausesets
1957 };
1958
1959 static cfg_clausedef_t
1960 rndckey_clauses[] = {
1961         { "key", &cfg_type_key, 0 },
1962         { NULL, NULL, 0 }
1963 };
1964
1965 static cfg_clausedef_t *
1966 rndckey_clausesets[] = {
1967         rndckey_clauses,
1968         NULL
1969 };
1970
1971 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
1972         "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1973         &cfg_rep_map, rndckey_clausesets
1974 };
1975
1976 static cfg_tuplefielddef_t nameport_fields[] = {
1977         { "name", &cfg_type_astring, 0 },
1978         { "port", &cfg_type_optional_port, 0 },
1979         { NULL, NULL, 0 }
1980 };
1981 static cfg_type_t cfg_type_nameport = {
1982         "nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1983         &cfg_rep_tuple, nameport_fields
1984 };
1985
1986 static void
1987 doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
1988         UNUSED(type);
1989         cfg_print_chars(pctx, "( ", 2);
1990         cfg_print_cstr(pctx, "<quoted_string>");
1991         cfg_print_chars(pctx, " ", 1);
1992         cfg_print_cstr(pctx, "[ port <integer> ]");
1993         cfg_print_chars(pctx, " | ", 3);
1994         cfg_print_cstr(pctx, "<ipv4_address>");
1995         cfg_print_chars(pctx, " ", 1);
1996         cfg_print_cstr(pctx, "[ port <integer> ]");
1997         cfg_print_chars(pctx, " | ", 3);
1998         cfg_print_cstr(pctx, "<ipv6_address>");
1999         cfg_print_chars(pctx, " ", 1);
2000         cfg_print_cstr(pctx, "[ port <integer> ]");
2001         cfg_print_chars(pctx, " )", 2);
2002 }
2003
2004 static isc_result_t
2005 parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type,
2006                        cfg_obj_t **ret)
2007 {
2008         isc_result_t result;
2009         cfg_obj_t *obj = NULL;
2010         UNUSED(type);
2011
2012         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2013         if (pctx->token.type == isc_tokentype_string ||
2014             pctx->token.type == isc_tokentype_qstring) {
2015                 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
2016                         CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
2017                 else {
2018                         const cfg_tuplefielddef_t *fields =
2019                                                    cfg_type_nameport.of;
2020                         CHECK(cfg_create_tuple(pctx, &cfg_type_nameport,
2021                                                &obj));
2022                         CHECK(cfg_parse_obj(pctx, fields[0].type,
2023                                             &obj->value.tuple[0]));
2024                         CHECK(cfg_parse_obj(pctx, fields[1].type,
2025                                             &obj->value.tuple[1]));
2026                         *ret = obj;
2027                         obj = NULL;
2028                 }
2029         } else {
2030                 cfg_parser_error(pctx, CFG_LOG_NEAR,
2031                              "expected IP address or hostname");
2032                 return (ISC_R_UNEXPECTEDTOKEN);
2033         }
2034  cleanup:
2035         CLEANUP_OBJ(obj);
2036         return (result);
2037 }
2038
2039 static cfg_type_t cfg_type_sockaddrnameport = {
2040         "sockaddrnameport_element", parse_sockaddrnameport, NULL,
2041          doc_sockaddrnameport, NULL, NULL
2042 };
2043
2044 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = {
2045         "bracketed_sockaddrnameportlist", cfg_parse_bracketed_list,
2046         cfg_print_bracketed_list, cfg_doc_bracketed_list,
2047         &cfg_rep_list, &cfg_type_sockaddrnameport
2048 };
2049
2050 /*%
2051  * A list of socket addresses or name with an optional default port,
2052  * as used in the dual-stack-servers option.  E.g.,
2053  * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
2054  */
2055 static cfg_tuplefielddef_t nameportiplist_fields[] = {
2056         { "port", &cfg_type_optional_port, 0 },
2057         { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
2058         { NULL, NULL, 0 }
2059 };
2060
2061 static cfg_type_t cfg_type_nameportiplist = {
2062         "nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
2063         &cfg_rep_tuple, nameportiplist_fields
2064 };
2065
2066 /*%
2067  * masters element.
2068  */
2069
2070 static void
2071 doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) {
2072         UNUSED(type);
2073         cfg_print_chars(pctx, "( ", 2);
2074         cfg_print_cstr(pctx, "<masters>");
2075         cfg_print_chars(pctx, " | ", 3);
2076         cfg_print_cstr(pctx, "<ipv4_address>");
2077         cfg_print_chars(pctx, " ", 1);
2078         cfg_print_cstr(pctx, "[ port <integer> ]");
2079         cfg_print_chars(pctx, " | ", 3);
2080         cfg_print_cstr(pctx, "<ipv6_address>");
2081         cfg_print_chars(pctx, " ", 1);
2082         cfg_print_cstr(pctx, "[ port <integer> ]");
2083         cfg_print_chars(pctx, " )", 2);
2084 }
2085
2086 static isc_result_t
2087 parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type,
2088                      cfg_obj_t **ret)
2089 {
2090         isc_result_t result;
2091         cfg_obj_t *obj = NULL;
2092         UNUSED(type);
2093
2094         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2095         if (pctx->token.type == isc_tokentype_string ||
2096             pctx->token.type == isc_tokentype_qstring) {
2097                 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
2098                         CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
2099                 else
2100                         CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret));
2101         } else {
2102                 cfg_parser_error(pctx, CFG_LOG_NEAR,
2103                              "expected IP address or masters name");
2104                 return (ISC_R_UNEXPECTEDTOKEN);
2105         }
2106  cleanup:
2107         CLEANUP_OBJ(obj);
2108         return (result);
2109 }
2110
2111 static cfg_type_t cfg_type_masterselement = {
2112         "masters_element", parse_masterselement, NULL,
2113          doc_masterselement, NULL, NULL
2114 };