]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind9/lib/isccfg/namedconf.c
add -n option to suppress clearing the build tree and add -DNO_CLEAN
[FreeBSD/FreeBSD.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.38.50.2 2008/07/23 23:48:17 tbox 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 isc_result_t
548 parse_port(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
549         isc_result_t result;
550         
551         UNUSED(type);
552
553         CHECK(cfg_parse_uint32(pctx, NULL, ret));
554         if ((*ret)->value.uint32 > 0xffff) {
555                 cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid port");
556                 cfg_obj_destroy(pctx, ret);
557                 result = ISC_R_RANGE;
558         }
559  cleanup:
560         return (result);
561 }
562
563 static cfg_type_t cfg_type_port = {
564         "port", parse_port, NULL, cfg_doc_terminal,
565         NULL, NULL
566 };
567
568 static cfg_type_t cfg_type_bracketed_portlist = {
569         "bracketed_sockaddrlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
570         &cfg_rep_list, &cfg_type_port
571 };
572
573 /*%
574  * Clauses that can be found within the top level of the named.conf
575  * file only.
576  */
577 static cfg_clausedef_t
578 namedconf_clauses[] = {
579         { "options", &cfg_type_options, 0 },
580         { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI },
581         { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI },
582         { "masters", &cfg_type_masters, CFG_CLAUSEFLAG_MULTI },
583         { "logging", &cfg_type_logging, 0 },
584         { "view", &cfg_type_view, CFG_CLAUSEFLAG_MULTI },
585         { "lwres", &cfg_type_lwres, CFG_CLAUSEFLAG_MULTI },
586         { NULL, NULL, 0 }
587 };
588
589 /*%
590  * Clauses that can occur at the top level or in the view
591  * statement, but not in the options block.
592  */
593 static cfg_clausedef_t
594 namedconf_or_view_clauses[] = {
595         { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
596         { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
597         /* only 1 DLZ per view allowed */
598         { "dlz", &cfg_type_dynamically_loadable_zones, 0 },
599         { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
600         { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI },
601         { NULL, NULL, 0 }
602 };
603
604 /*%
605  * Clauses that can be found within the 'options' statement.
606  */
607 static cfg_clausedef_t
608 options_clauses[] = {
609         { "avoid-v4-udp-ports", &cfg_type_bracketed_portlist, 0 },
610         { "avoid-v6-udp-ports", &cfg_type_bracketed_portlist, 0 },
611         { "blackhole", &cfg_type_bracketed_aml, 0 },
612         { "coresize", &cfg_type_size, 0 },
613         { "datasize", &cfg_type_size, 0 },
614         { "deallocate-on-exit", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
615         { "directory", &cfg_type_qstring, CFG_CLAUSEFLAG_CALLBACK },
616         { "dump-file", &cfg_type_qstring, 0 },
617         { "fake-iquery", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
618         { "files", &cfg_type_size, 0 },
619         { "has-old-clients", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
620         { "heartbeat-interval", &cfg_type_uint32, 0 },
621         { "host-statistics", &cfg_type_boolean, CFG_CLAUSEFLAG_NOTIMP },
622         { "host-statistics-max", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
623         { "hostname", &cfg_type_qstringornone, 0 },
624         { "interface-interval", &cfg_type_uint32, 0 },
625         { "listen-on", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
626         { "listen-on-v6", &cfg_type_listenon, CFG_CLAUSEFLAG_MULTI },
627         { "match-mapped-addresses", &cfg_type_boolean, 0 },
628         { "memstatistics-file", &cfg_type_qstring, 0 },
629         { "multiple-cnames", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
630         { "named-xfer", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
631         { "pid-file", &cfg_type_qstringornone, 0 },
632         { "port", &cfg_type_uint32, 0 },
633         { "querylog", &cfg_type_boolean, 0 },
634         { "recursing-file", &cfg_type_qstring, 0 },
635         { "random-device", &cfg_type_qstring, 0 },
636         { "recursive-clients", &cfg_type_uint32, 0 },
637         { "reserved-sockets", &cfg_type_uint32, 0 },
638         { "serial-queries", &cfg_type_uint32, CFG_CLAUSEFLAG_OBSOLETE },
639         { "serial-query-rate", &cfg_type_uint32, 0 },
640         { "server-id", &cfg_type_serverid, 0 },
641         { "stacksize", &cfg_type_size, 0 },
642         { "statistics-file", &cfg_type_qstring, 0 },
643         { "statistics-interval", &cfg_type_uint32, CFG_CLAUSEFLAG_NYI },
644         { "tcp-clients", &cfg_type_uint32, 0 },
645         { "tcp-listen-queue", &cfg_type_uint32, 0 },
646         { "tkey-dhkey", &cfg_type_tkey_dhkey, 0 },
647         { "tkey-gssapi-credential", &cfg_type_qstring, 0 },
648         { "tkey-domain", &cfg_type_qstring, 0 },
649         { "transfers-per-ns", &cfg_type_uint32, 0 },
650         { "transfers-in", &cfg_type_uint32, 0 },
651         { "transfers-out", &cfg_type_uint32, 0 },
652         { "treat-cr-as-space", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
653         { "use-id-pool", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
654         { "use-ixfr", &cfg_type_boolean, 0 },
655         { "version", &cfg_type_qstringornone, 0 },
656         { "flush-zones-on-shutdown", &cfg_type_boolean, 0 },
657         { NULL, NULL, 0 }
658 };
659
660
661 static cfg_type_t cfg_type_namelist = {
662         "namelist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
663         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_qstring };
664
665 static keyword_type_t exclude_kw = { "exclude", &cfg_type_namelist };
666
667 static cfg_type_t cfg_type_optional_exclude = {
668         "optional_exclude", parse_optional_keyvalue, print_keyvalue,
669         doc_optional_keyvalue, &cfg_rep_list, &exclude_kw };
670
671 static cfg_type_t cfg_type_algorithmlist = {
672         "algorithmlist", cfg_parse_bracketed_list, cfg_print_bracketed_list,
673         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_astring };
674
675 static cfg_tuplefielddef_t disablealgorithm_fields[] = {
676         { "name", &cfg_type_astring, 0 },
677         { "algorithms", &cfg_type_algorithmlist, 0 },
678         { NULL, NULL, 0 }
679 };
680
681 static cfg_type_t cfg_type_disablealgorithm = {
682         "disablealgorithm", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
683         &cfg_rep_tuple, disablealgorithm_fields
684 };
685
686 static cfg_tuplefielddef_t mustbesecure_fields[] = {
687         { "name", &cfg_type_astring, 0 },
688         { "value", &cfg_type_boolean, 0 },
689         { NULL, NULL, 0 }
690 };
691
692 static cfg_type_t cfg_type_mustbesecure = {
693         "mustbesecure", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
694         &cfg_rep_tuple, mustbesecure_fields
695 };
696
697 static const char *masterformat_enums[] = { "text", "raw", NULL };
698 static cfg_type_t cfg_type_masterformat = {
699         "masterformat", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
700         &cfg_rep_string, &masterformat_enums
701 };
702
703 /*%
704  * dnssec-lookaside
705  */
706
707 static keyword_type_t trustanchor_kw = { "trust-anchor", &cfg_type_astring };
708
709 static cfg_type_t cfg_type_trustanchor = {
710         "trust-anchor", parse_keyvalue, print_keyvalue, doc_keyvalue,
711         &cfg_rep_string, &trustanchor_kw
712 };
713
714 static cfg_tuplefielddef_t lookaside_fields[] = {
715         { "domain", &cfg_type_astring, 0 },
716         { "trust-anchor", &cfg_type_trustanchor, 0 },
717         { NULL, NULL, 0 }
718 };
719
720 static cfg_type_t cfg_type_lookaside = {
721         "lookaside", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
722         &cfg_rep_tuple, lookaside_fields
723 };
724
725 /*%
726  * Clauses that can be found within the 'view' statement,
727  * with defaults in the 'options' statement.
728  */
729
730 static cfg_clausedef_t
731 view_clauses[] = {
732         { "allow-query-cache", &cfg_type_bracketed_aml, 0 },
733         { "allow-recursion", &cfg_type_bracketed_aml, 0 },
734         { "allow-v6-synthesis", &cfg_type_bracketed_aml,
735           CFG_CLAUSEFLAG_OBSOLETE },
736         { "sortlist", &cfg_type_bracketed_aml, 0 },
737         { "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_NOTIMP },
738         { "auth-nxdomain", &cfg_type_boolean, CFG_CLAUSEFLAG_NEWDEFAULT },
739         { "minimal-responses", &cfg_type_boolean, 0 },
740         { "recursion", &cfg_type_boolean, 0 },
741         { "rrset-order", &cfg_type_rrsetorder, 0 },
742         { "provide-ixfr", &cfg_type_boolean, 0 },
743         { "request-ixfr", &cfg_type_boolean, 0 },
744         { "fetch-glue", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
745         { "rfc2308-type1", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
746         { "additional-from-auth", &cfg_type_boolean, 0 },
747         { "additional-from-cache", &cfg_type_boolean, 0 },
748         /*
749          * Note that the query-source option syntax is different
750          * from the other -source options.
751          */
752         { "query-source", &cfg_type_querysource4, 0 },
753         { "query-source-v6", &cfg_type_querysource6, 0 },
754         { "cleaning-interval", &cfg_type_uint32, 0 },
755         { "min-roots", &cfg_type_uint32, CFG_CLAUSEFLAG_NOTIMP },
756         { "lame-ttl", &cfg_type_uint32, 0 },
757         { "max-ncache-ttl", &cfg_type_uint32, 0 },
758         { "max-cache-ttl", &cfg_type_uint32, 0 },
759         { "transfer-format", &cfg_type_transferformat, 0 },
760         { "max-cache-size", &cfg_type_sizenodefault, 0 },
761         { "check-names", &cfg_type_checknames, CFG_CLAUSEFLAG_MULTI },
762         { "cache-file", &cfg_type_qstring, 0 },
763         { "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
764         { "preferred-glue", &cfg_type_astring, 0 },
765         { "dual-stack-servers", &cfg_type_nameportiplist, 0 },
766         { "edns-udp-size", &cfg_type_uint32, 0 },
767         { "max-udp-size", &cfg_type_uint32, 0 },
768         { "root-delegation-only",  &cfg_type_optional_exclude, 0 },
769         { "disable-algorithms", &cfg_type_disablealgorithm,
770           CFG_CLAUSEFLAG_MULTI },
771         { "dnssec-enable", &cfg_type_boolean, 0 },
772         { "dnssec-validation", &cfg_type_boolean, 0 },
773         { "dnssec-lookaside", &cfg_type_lookaside, CFG_CLAUSEFLAG_MULTI },
774         { "dnssec-must-be-secure",  &cfg_type_mustbesecure,
775            CFG_CLAUSEFLAG_MULTI },
776         { "dnssec-accept-expired", &cfg_type_boolean, 0 },
777         { "ixfr-from-differences", &cfg_type_ixfrdifftype, 0 },
778         { "acache-enable", &cfg_type_boolean, 0 },
779         { "acache-cleaning-interval", &cfg_type_uint32, 0 },
780         { "max-acache-size", &cfg_type_sizenodefault, 0 },
781         { "clients-per-query", &cfg_type_uint32, 0 },
782         { "max-clients-per-query", &cfg_type_uint32, 0 },
783         { "empty-server", &cfg_type_astring, 0 },
784         { "empty-contact", &cfg_type_astring, 0 },
785         { "empty-zones-enable", &cfg_type_boolean, 0 },
786         { "disable-empty-zone", &cfg_type_astring, CFG_CLAUSEFLAG_MULTI },
787         { "zero-no-soa-ttl-cache", &cfg_type_boolean, 0 },
788         { NULL, NULL, 0 }
789 };
790
791 /*%
792  * Clauses that can be found within the 'view' statement only.
793  */
794 static cfg_clausedef_t
795 view_only_clauses[] = {
796         { "match-clients", &cfg_type_bracketed_aml, 0 },
797         { "match-destinations", &cfg_type_bracketed_aml, 0 },
798         { "match-recursive-only", &cfg_type_boolean, 0 },
799         { NULL, NULL, 0 }
800 };
801
802 /*%
803  * Clauses that can be found in a 'zone' statement,
804  * with defaults in the 'view' or 'options' statement.
805  */
806 static cfg_clausedef_t
807 zone_clauses[] = {
808         { "allow-query", &cfg_type_bracketed_aml, 0 },
809         { "allow-transfer", &cfg_type_bracketed_aml, 0 },
810         { "allow-update", &cfg_type_bracketed_aml, 0 },
811         { "allow-update-forwarding", &cfg_type_bracketed_aml, 0 },
812         { "allow-notify", &cfg_type_bracketed_aml, 0 },
813         { "masterfile-format", &cfg_type_masterformat, 0 },
814         { "notify", &cfg_type_notifytype, 0 },
815         { "notify-source", &cfg_type_sockaddr4wild, 0 },
816         { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
817         { "also-notify", &cfg_type_portiplist, 0 },
818         { "notify-delay", &cfg_type_uint32, 0 },
819         { "dialup", &cfg_type_dialuptype, 0 },
820         { "forward", &cfg_type_forwardtype, 0 },
821         { "forwarders", &cfg_type_portiplist, 0 },
822         { "maintain-ixfr-base", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
823         { "max-ixfr-log-size", &cfg_type_size, CFG_CLAUSEFLAG_OBSOLETE },
824         { "max-journal-size", &cfg_type_sizenodefault, 0 },
825         { "max-transfer-time-in", &cfg_type_uint32, 0 },
826         { "max-transfer-time-out", &cfg_type_uint32, 0 },
827         { "max-transfer-idle-in", &cfg_type_uint32, 0 },
828         { "max-transfer-idle-out", &cfg_type_uint32, 0 },
829         { "max-retry-time", &cfg_type_uint32, 0 },
830         { "min-retry-time", &cfg_type_uint32, 0 },
831         { "max-refresh-time", &cfg_type_uint32, 0 },
832         { "min-refresh-time", &cfg_type_uint32, 0 },
833         { "multi-master", &cfg_type_boolean, 0 },
834         { "sig-validity-interval", &cfg_type_uint32, 0 },
835         { "transfer-source", &cfg_type_sockaddr4wild, 0 },
836         { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
837         { "alt-transfer-source", &cfg_type_sockaddr4wild, 0 },
838         { "alt-transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
839         { "use-alt-transfer-source", &cfg_type_boolean, 0 },
840         { "zone-statistics", &cfg_type_boolean, 0 },
841         { "key-directory", &cfg_type_qstring, 0 },
842         { "check-wildcard", &cfg_type_boolean, 0 },
843         { "check-integrity", &cfg_type_boolean, 0 },
844         { "check-mx", &cfg_type_checkmode, 0 },
845         { "check-mx-cname", &cfg_type_checkmode, 0 },
846         { "check-srv-cname", &cfg_type_checkmode, 0 },
847         { "check-sibling", &cfg_type_boolean, 0 },
848         { "zero-no-soa-ttl", &cfg_type_boolean, 0 },
849         { "update-check-ksk", &cfg_type_boolean, 0 },
850         { NULL, NULL, 0 }
851 };
852
853 /*%
854  * Clauses that can be found in a 'zone' statement
855  * only.
856  */
857 static cfg_clausedef_t
858 zone_only_clauses[] = {
859         { "type", &cfg_type_zonetype, 0 },
860         { "file", &cfg_type_qstring, 0 },
861         { "journal", &cfg_type_qstring, 0 },
862         { "ixfr-base", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
863         { "ixfr-tmp-file", &cfg_type_qstring, CFG_CLAUSEFLAG_OBSOLETE },
864         { "masters", &cfg_type_namesockaddrkeylist, 0 },
865         { "pubkey", &cfg_type_pubkey,
866           CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_OBSOLETE },
867         { "update-policy", &cfg_type_updatepolicy, 0 },
868         { "database", &cfg_type_astring, 0 },
869         { "delegation-only", &cfg_type_boolean, 0 },
870         /*
871          * Note that the format of the check-names option is different between
872          * the zone options and the global/view options.  Ugh.
873          */
874         { "check-names", &cfg_type_checkmode, 0 },
875         { "ixfr-from-differences", &cfg_type_boolean, 0 },
876         { NULL, NULL, 0 }
877 };
878
879
880 /*% The top-level named.conf syntax. */
881
882 static cfg_clausedef_t *
883 namedconf_clausesets[] = {
884         namedconf_clauses,
885         namedconf_or_view_clauses,
886         NULL
887 };
888
889 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_namedconf = {
890         "namedconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
891         &cfg_rep_map, namedconf_clausesets
892 };
893
894 /*% The "options" statement syntax. */
895
896 static cfg_clausedef_t *
897 options_clausesets[] = {
898         options_clauses,
899         view_clauses,
900         zone_clauses,
901         NULL
902 };
903 static cfg_type_t cfg_type_options = {
904         "options", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, options_clausesets };
905
906 /*% The "view" statement syntax. */
907
908 static cfg_clausedef_t *
909 view_clausesets[] = {
910         view_only_clauses,
911         namedconf_or_view_clauses,
912         view_clauses,
913         zone_clauses,
914         dynamically_loadable_zones_clauses,
915         NULL
916 };
917 static cfg_type_t cfg_type_viewopts = {
918         "view", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, view_clausesets };
919
920 /*% The "zone" statement syntax. */
921
922 static cfg_clausedef_t *
923 zone_clausesets[] = {
924         zone_only_clauses,
925         zone_clauses,
926         NULL
927 };
928 static cfg_type_t cfg_type_zoneopts = {
929         "zoneopts", cfg_parse_map, cfg_print_map, 
930         cfg_doc_map, &cfg_rep_map, zone_clausesets };
931  
932 /*% The "dynamically loadable zones" statement syntax. */
933  
934 static cfg_clausedef_t *
935 dynamically_loadable_zones_clausesets[] = {
936         dynamically_loadable_zones_clauses,
937         NULL
938 };
939 static cfg_type_t cfg_type_dynamically_loadable_zones_opts = {
940         "dynamically_loadable_zones_opts", cfg_parse_map, 
941         cfg_print_map, cfg_doc_map, &cfg_rep_map,
942         dynamically_loadable_zones_clausesets 
943 };
944  
945 /*%
946  * Clauses that can be found within the 'key' statement.
947  */
948 static cfg_clausedef_t
949 key_clauses[] = {
950         { "algorithm", &cfg_type_astring, 0 },
951         { "secret", &cfg_type_astring, 0 },
952         { NULL, NULL, 0 }
953 };
954
955 static cfg_clausedef_t *
956 key_clausesets[] = {
957         key_clauses,
958         NULL
959 };
960 static cfg_type_t cfg_type_key = {
961         "key", cfg_parse_named_map, cfg_print_map,
962         cfg_doc_map, &cfg_rep_map, key_clausesets 
963 };
964
965
966 /*%
967  * Clauses that can be found in a 'server' statement.
968  */
969 static cfg_clausedef_t
970 server_clauses[] = {
971         { "bogus", &cfg_type_boolean, 0 },
972         { "provide-ixfr", &cfg_type_boolean, 0 },
973         { "request-ixfr", &cfg_type_boolean, 0 },
974         { "support-ixfr", &cfg_type_boolean, CFG_CLAUSEFLAG_OBSOLETE },
975         { "transfers", &cfg_type_uint32, 0 },
976         { "transfer-format", &cfg_type_transferformat, 0 },
977         { "keys", &cfg_type_server_key_kludge, 0 },
978         { "edns", &cfg_type_boolean, 0 },
979         { "edns-udp-size", &cfg_type_uint32, 0 },
980         { "max-udp-size", &cfg_type_uint32, 0 },
981         { "notify-source", &cfg_type_sockaddr4wild, 0 },
982         { "notify-source-v6", &cfg_type_sockaddr6wild, 0 },
983         { "query-source", &cfg_type_querysource4, 0 },
984         { "query-source-v6", &cfg_type_querysource6, 0 },
985         { "transfer-source", &cfg_type_sockaddr4wild, 0 },
986         { "transfer-source-v6", &cfg_type_sockaddr6wild, 0 },
987         { NULL, NULL, 0 }
988 };
989 static cfg_clausedef_t *
990 server_clausesets[] = {
991         server_clauses,
992         NULL
993 };
994 static cfg_type_t cfg_type_server = {
995         "server", cfg_parse_netprefix_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
996         server_clausesets
997 };
998
999
1000 /*%
1001  * Clauses that can be found in a 'channel' clause in the
1002  * 'logging' statement.
1003  *
1004  * These have some additional constraints that need to be
1005  * checked after parsing:
1006  *  - There must exactly one of file/syslog/null/stderr
1007  *
1008  */
1009 static cfg_clausedef_t
1010 channel_clauses[] = {
1011         /* Destinations.  We no longer require these to be first. */
1012         { "file", &cfg_type_logfile, 0 },
1013         { "syslog", &cfg_type_optional_facility, 0 },
1014         { "null", &cfg_type_void, 0 },
1015         { "stderr", &cfg_type_void, 0 },
1016         /* Options.  We now accept these for the null channel, too. */
1017         { "severity", &cfg_type_logseverity, 0 },
1018         { "print-time", &cfg_type_boolean, 0 },
1019         { "print-severity", &cfg_type_boolean, 0 },
1020         { "print-category", &cfg_type_boolean, 0 },
1021         { NULL, NULL, 0 }
1022 };
1023 static cfg_clausedef_t *
1024 channel_clausesets[] = {
1025         channel_clauses,
1026         NULL
1027 };
1028 static cfg_type_t cfg_type_channel = {
1029         "channel", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
1030         &cfg_rep_map, channel_clausesets
1031 };
1032
1033 /*% A list of log destination, used in the "category" clause. */
1034 static cfg_type_t cfg_type_destinationlist = {
1035         "destinationlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
1036         &cfg_rep_list, &cfg_type_astring };
1037
1038 /*%
1039  * Clauses that can be found in a 'logging' statement.
1040  */
1041 static cfg_clausedef_t
1042 logging_clauses[] = {
1043         { "channel", &cfg_type_channel, CFG_CLAUSEFLAG_MULTI },
1044         { "category", &cfg_type_category, CFG_CLAUSEFLAG_MULTI },
1045         { NULL, NULL, 0 }
1046 };
1047 static cfg_clausedef_t *
1048 logging_clausesets[] = {
1049         logging_clauses,
1050         NULL
1051 };
1052 static cfg_type_t cfg_type_logging = {
1053         "logging", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, logging_clausesets };
1054
1055
1056 static isc_result_t
1057 parse_unitstring(char *str, isc_resourcevalue_t *valuep) {
1058         char *endp;
1059         unsigned int len;
1060         isc_uint64_t value;
1061         isc_uint64_t unit;
1062
1063         value = isc_string_touint64(str, &endp, 10);
1064         if (*endp == 0) {
1065                 *valuep = value;
1066                 return (ISC_R_SUCCESS);
1067         }
1068
1069         len = strlen(str);
1070         if (len < 2 || endp[1] != '\0')
1071                 return (ISC_R_FAILURE);
1072
1073         switch (str[len - 1]) {
1074         case 'k':
1075         case 'K':
1076                 unit = 1024;
1077                 break;
1078         case 'm':
1079         case 'M':
1080                 unit = 1024 * 1024;
1081                 break;
1082         case 'g':
1083         case 'G':
1084                 unit = 1024 * 1024 * 1024;
1085                 break;
1086         default:
1087                 return (ISC_R_FAILURE);
1088         }
1089         if (value > ISC_UINT64_MAX / unit)
1090                 return (ISC_R_FAILURE);
1091         *valuep = value * unit;
1092         return (ISC_R_SUCCESS);
1093 }
1094
1095 static isc_result_t
1096 parse_sizeval(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1097         isc_result_t result;
1098         cfg_obj_t *obj = NULL;
1099         isc_uint64_t val;
1100
1101         UNUSED(type);
1102
1103         CHECK(cfg_gettoken(pctx, 0));
1104         if (pctx->token.type != isc_tokentype_string) {
1105                 result = ISC_R_UNEXPECTEDTOKEN;
1106                 goto cleanup;
1107         }
1108         CHECK(parse_unitstring(TOKEN_STRING(pctx), &val));
1109
1110         CHECK(cfg_create_obj(pctx, &cfg_type_uint64, &obj));
1111         obj->value.uint64 = val;
1112         *ret = obj;
1113         return (ISC_R_SUCCESS);
1114
1115  cleanup:
1116         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected integer and optional unit");
1117         return (result);
1118 }
1119
1120 /*%
1121  * A size value (number + optional unit).
1122  */
1123 static cfg_type_t cfg_type_sizeval = {
1124         "sizeval", parse_sizeval, cfg_print_uint64, cfg_doc_terminal,
1125         &cfg_rep_uint64, NULL };
1126
1127 /*%
1128  * A size, "unlimited", or "default".
1129  */
1130
1131 static isc_result_t
1132 parse_size(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1133         return (parse_enum_or_other(pctx, type, &cfg_type_sizeval, ret));
1134 }
1135
1136 static const char *size_enums[] = { "unlimited", "default", NULL };
1137 static cfg_type_t cfg_type_size = {
1138         "size", parse_size, cfg_print_ustring, cfg_doc_terminal,
1139         &cfg_rep_string, size_enums
1140 };
1141
1142 /*%
1143  * A size or "unlimited", but not "default".
1144  */
1145 static const char *sizenodefault_enums[] = { "unlimited", NULL };
1146 static cfg_type_t cfg_type_sizenodefault = {
1147         "size_no_default", parse_size, cfg_print_ustring, cfg_doc_terminal,
1148         &cfg_rep_string, sizenodefault_enums
1149 };
1150
1151 /*%
1152  * optional_keyvalue
1153  */
1154 static isc_result_t
1155 parse_maybe_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type,
1156                               isc_boolean_t optional, cfg_obj_t **ret)
1157 {
1158         isc_result_t result;
1159         cfg_obj_t *obj = NULL;
1160         const keyword_type_t *kw = type->of;
1161
1162         CHECK(cfg_peektoken(pctx, 0));
1163         if (pctx->token.type == isc_tokentype_string &&
1164             strcasecmp(TOKEN_STRING(pctx), kw->name) == 0) {
1165                 CHECK(cfg_gettoken(pctx, 0));
1166                 CHECK(kw->type->parse(pctx, kw->type, &obj));
1167                 obj->type = type; /* XXX kludge */
1168         } else {
1169                 if (optional) {
1170                         CHECK(cfg_parse_void(pctx, NULL, &obj));
1171                 } else {
1172                         cfg_parser_error(pctx, CFG_LOG_NEAR, "expected '%s'",
1173                                      kw->name);
1174                         result = ISC_R_UNEXPECTEDTOKEN;
1175                         goto cleanup;
1176                 }
1177         }
1178         *ret = obj;
1179  cleanup:
1180         return (result);
1181 }
1182
1183 static isc_result_t
1184 parse_enum_or_other(cfg_parser_t *pctx, const cfg_type_t *enumtype,
1185                     const cfg_type_t *othertype, cfg_obj_t **ret)
1186 {
1187         isc_result_t result;
1188         CHECK(cfg_peektoken(pctx, 0));
1189         if (pctx->token.type == isc_tokentype_string &&
1190             cfg_is_enum(TOKEN_STRING(pctx), enumtype->of)) {
1191                 CHECK(cfg_parse_enum(pctx, enumtype, ret));
1192         } else {
1193                 CHECK(cfg_parse_obj(pctx, othertype, ret));
1194         }
1195  cleanup:
1196         return (result);
1197 }
1198
1199 static void
1200 doc_enum_or_other(cfg_printer_t *pctx, const cfg_type_t *type) {
1201         cfg_doc_terminal(pctx, type);
1202 #if 0 /* XXX */
1203         cfg_print_chars(pctx, "( ", 2);...
1204 #endif
1205
1206 }
1207
1208 static isc_result_t
1209 parse_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1210         return (parse_maybe_optional_keyvalue(pctx, type, ISC_FALSE, ret));
1211 }
1212
1213 static isc_result_t
1214 parse_optional_keyvalue(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1215         return (parse_maybe_optional_keyvalue(pctx, type, ISC_TRUE, ret));
1216 }
1217
1218 static void
1219 print_keyvalue(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1220         const keyword_type_t *kw = obj->type->of;
1221         cfg_print_cstr(pctx, kw->name);
1222         cfg_print_chars(pctx, " ", 1);
1223         kw->type->print(pctx, obj);
1224 }
1225
1226 static void
1227 doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
1228         const keyword_type_t *kw = type->of;
1229         cfg_print_cstr(pctx, kw->name);
1230         cfg_print_chars(pctx, " ", 1);
1231         cfg_doc_obj(pctx, kw->type);
1232 }
1233
1234 static void
1235 doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type) {
1236         const keyword_type_t *kw = type->of;
1237         cfg_print_chars(pctx, "[ ", 2);
1238         cfg_print_cstr(pctx, kw->name);
1239         cfg_print_chars(pctx, " ", 1);
1240         cfg_doc_obj(pctx, kw->type);
1241         cfg_print_chars(pctx, " ]", 2);
1242 }
1243
1244 static const char *dialup_enums[] = {
1245         "notify", "notify-passive", "refresh", "passive", NULL };
1246 static isc_result_t
1247 parse_dialup_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1248         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1249 }
1250 static cfg_type_t cfg_type_dialuptype = {
1251         "dialuptype", parse_dialup_type, cfg_print_ustring, doc_enum_or_other,
1252         &cfg_rep_string, dialup_enums
1253 };
1254
1255 static const char *notify_enums[] = { "explicit", "master-only", NULL };
1256 static isc_result_t
1257 parse_notify_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1258         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1259 }
1260 static cfg_type_t cfg_type_notifytype = {
1261         "notifytype", parse_notify_type, cfg_print_ustring, doc_enum_or_other,
1262         &cfg_rep_string, notify_enums,
1263 };
1264
1265 static const char *ixfrdiff_enums[] = { "master", "slave", NULL };
1266 static isc_result_t
1267 parse_ixfrdiff_type(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1268         return (parse_enum_or_other(pctx, type, &cfg_type_boolean, ret));
1269 }
1270 static cfg_type_t cfg_type_ixfrdifftype = {
1271         "ixfrdiff", parse_ixfrdiff_type, cfg_print_ustring, doc_enum_or_other,
1272         &cfg_rep_string, ixfrdiff_enums,
1273 };
1274
1275 static keyword_type_t key_kw = { "key", &cfg_type_astring };
1276
1277 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_keyref = {
1278         "keyref", parse_keyvalue, print_keyvalue, doc_keyvalue,
1279         &cfg_rep_string, &key_kw
1280 };
1281
1282 static cfg_type_t cfg_type_optional_keyref = {
1283         "optional_keyref", parse_optional_keyvalue, print_keyvalue,
1284         doc_optional_keyvalue, &cfg_rep_string, &key_kw
1285 };
1286
1287 /*%
1288  * A "controls" statement is represented as a map with the multivalued
1289  * "inet" and "unix" clauses. 
1290  */
1291
1292 static keyword_type_t controls_allow_kw = {
1293         "allow", &cfg_type_bracketed_aml };
1294
1295 static cfg_type_t cfg_type_controls_allow = {
1296         "controls_allow", parse_keyvalue,
1297         print_keyvalue, doc_keyvalue,
1298         &cfg_rep_list, &controls_allow_kw
1299 };
1300
1301 static keyword_type_t controls_keys_kw = {
1302         "keys", &cfg_type_keylist };
1303
1304 static cfg_type_t cfg_type_controls_keys = {
1305         "controls_keys", parse_optional_keyvalue,
1306         print_keyvalue, doc_optional_keyvalue,
1307         &cfg_rep_list, &controls_keys_kw
1308 };
1309
1310 static cfg_tuplefielddef_t inetcontrol_fields[] = {
1311         { "address", &cfg_type_controls_sockaddr, 0 },
1312         { "allow", &cfg_type_controls_allow, 0 },
1313         { "keys", &cfg_type_controls_keys, 0 },
1314         { NULL, NULL, 0 }
1315 };
1316
1317 static cfg_type_t cfg_type_inetcontrol = {
1318         "inetcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1319         inetcontrol_fields
1320 };
1321
1322 static keyword_type_t controls_perm_kw = {
1323         "perm", &cfg_type_uint32 };
1324
1325 static cfg_type_t cfg_type_controls_perm = {
1326         "controls_perm", parse_keyvalue,
1327         print_keyvalue, doc_keyvalue,
1328         &cfg_rep_uint32, &controls_perm_kw
1329 };
1330
1331 static keyword_type_t controls_owner_kw = {
1332         "owner", &cfg_type_uint32 };
1333
1334 static cfg_type_t cfg_type_controls_owner = {
1335         "controls_owner", parse_keyvalue,
1336         print_keyvalue, doc_keyvalue,
1337         &cfg_rep_uint32, &controls_owner_kw
1338 };
1339
1340 static keyword_type_t controls_group_kw = {
1341         "group", &cfg_type_uint32 };
1342
1343 static cfg_type_t cfg_type_controls_group = {
1344         "controls_allow", parse_keyvalue,
1345         print_keyvalue, doc_keyvalue,
1346         &cfg_rep_uint32, &controls_group_kw
1347 };
1348
1349 static cfg_tuplefielddef_t unixcontrol_fields[] = {
1350         { "path", &cfg_type_qstring, 0 },
1351         { "perm", &cfg_type_controls_perm, 0 },
1352         { "owner", &cfg_type_controls_owner, 0 },
1353         { "group", &cfg_type_controls_group, 0 },
1354         { "keys", &cfg_type_controls_keys, 0 },
1355         { NULL, NULL, 0 }
1356 };
1357
1358 static cfg_type_t cfg_type_unixcontrol = {
1359         "unixcontrol", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1360         unixcontrol_fields
1361 };
1362
1363 static cfg_clausedef_t
1364 controls_clauses[] = {
1365         { "inet", &cfg_type_inetcontrol, CFG_CLAUSEFLAG_MULTI },
1366         { "unix", &cfg_type_unixcontrol, CFG_CLAUSEFLAG_MULTI },
1367         { NULL, NULL, 0 }
1368 };
1369
1370 static cfg_clausedef_t *
1371 controls_clausesets[] = {
1372         controls_clauses,
1373         NULL
1374 };
1375 static cfg_type_t cfg_type_controls = {
1376         "controls", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,    &controls_clausesets
1377 };
1378
1379 /*%
1380  * An optional class, as used in view and zone statements.
1381  */
1382 static isc_result_t
1383 parse_optional_class(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1384         isc_result_t result;
1385         UNUSED(type);
1386         CHECK(cfg_peektoken(pctx, 0));
1387         if (pctx->token.type == isc_tokentype_string)
1388                 CHECK(cfg_parse_obj(pctx, &cfg_type_ustring, ret));
1389         else
1390                 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1391  cleanup:
1392         return (result);
1393 }
1394
1395 static cfg_type_t cfg_type_optional_class = {
1396         "optional_class", parse_optional_class, NULL, cfg_doc_terminal,
1397         NULL, NULL
1398 };
1399
1400 static isc_result_t
1401 parse_querysource(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1402         isc_result_t result;
1403         cfg_obj_t *obj = NULL;
1404         isc_netaddr_t netaddr;
1405         in_port_t port;
1406         unsigned int have_address = 0;
1407         unsigned int have_port = 0;
1408         const unsigned int *flagp = type->of;
1409
1410         if ((*flagp & CFG_ADDR_V4OK) != 0)
1411                 isc_netaddr_any(&netaddr);
1412         else if ((*flagp & CFG_ADDR_V6OK) != 0)
1413                 isc_netaddr_any6(&netaddr);
1414         else
1415                 INSIST(0);
1416
1417         port = 0;
1418
1419         for (;;) {
1420                 CHECK(cfg_peektoken(pctx, 0));
1421                 if (pctx->token.type == isc_tokentype_string) {
1422                         if (strcasecmp(TOKEN_STRING(pctx),
1423                                        "address") == 0)
1424                         {
1425                                 /* read "address" */
1426                                 CHECK(cfg_gettoken(pctx, 0)); 
1427                                 CHECK(cfg_parse_rawaddr(pctx, *flagp,
1428                                                         &netaddr));
1429                                 have_address++;
1430                         } else if (strcasecmp(TOKEN_STRING(pctx), "port") == 0)
1431                         {
1432                                 /* read "port" */
1433                                 CHECK(cfg_gettoken(pctx, 0)); 
1434                                 CHECK(cfg_parse_rawport(pctx,
1435                                                         CFG_ADDR_WILDOK,
1436                                                         &port));
1437                                 have_port++;
1438                         } else if (have_port == 0 && have_address == 0) {
1439                                 return (cfg_parse_sockaddr(pctx, type, ret));
1440                         } else {
1441                                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1442                                              "expected 'address' or 'port'");
1443                                 return (ISC_R_UNEXPECTEDTOKEN);
1444                         }
1445                 } else
1446                         break;
1447         }
1448         if (have_address > 1 || have_port > 1 ||
1449             have_address + have_port == 0) {
1450                 cfg_parser_error(pctx, 0, "expected one address and/or port");
1451                 return (ISC_R_UNEXPECTEDTOKEN);
1452         }
1453
1454         CHECK(cfg_create_obj(pctx, &cfg_type_querysource, &obj));
1455         isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, port);
1456         *ret = obj;
1457         return (ISC_R_SUCCESS);
1458
1459  cleanup:
1460         cfg_parser_error(pctx, CFG_LOG_NEAR, "invalid query source");
1461         CLEANUP_OBJ(obj);
1462         return (result);
1463 }
1464
1465 static void
1466 print_querysource(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1467         isc_netaddr_t na;
1468         isc_netaddr_fromsockaddr(&na, &obj->value.sockaddr);
1469         cfg_print_chars(pctx, "address ", 8);
1470         cfg_print_rawaddr(pctx, &na);
1471         cfg_print_chars(pctx, " port ", 6);
1472         cfg_print_rawuint(pctx, isc_sockaddr_getport(&obj->value.sockaddr));
1473 }
1474
1475 static unsigned int sockaddr4wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V4OK;
1476 static unsigned int sockaddr6wild_flags = CFG_ADDR_WILDOK | CFG_ADDR_V6OK;
1477 static cfg_type_t cfg_type_querysource4 = {
1478         "querysource4", parse_querysource, NULL, cfg_doc_terminal,
1479         NULL, &sockaddr4wild_flags
1480 };
1481
1482 static cfg_type_t cfg_type_querysource6 = {
1483         "querysource6", parse_querysource, NULL, cfg_doc_terminal,
1484         NULL, &sockaddr6wild_flags
1485 };
1486
1487 static cfg_type_t cfg_type_querysource = {
1488         "querysource", NULL, print_querysource, NULL, &cfg_rep_sockaddr, NULL
1489 };
1490
1491 /*% addrmatchelt */
1492
1493 static isc_result_t
1494 parse_addrmatchelt(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1495         isc_result_t result;
1496         UNUSED(type);
1497
1498         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1499
1500         if (pctx->token.type == isc_tokentype_string ||
1501             pctx->token.type == isc_tokentype_qstring) {
1502                 if (pctx->token.type == isc_tokentype_string &&
1503                     (strcasecmp(TOKEN_STRING(pctx), "key") == 0)) {
1504                         CHECK(cfg_parse_obj(pctx, &cfg_type_keyref, ret));
1505                 } else {
1506                         if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK |
1507                                                   CFG_ADDR_V4PREFIXOK |
1508                                                   CFG_ADDR_V6OK))
1509                         {
1510                                 CHECK(cfg_parse_netprefix(pctx, NULL, ret));
1511                         } else {
1512                                 CHECK(cfg_parse_astring(pctx, NULL, ret));
1513                         }
1514                 }
1515         } else if (pctx->token.type == isc_tokentype_special) {
1516                 if (pctx->token.value.as_char == '{') {
1517                         /* Nested match list. */
1518                         CHECK(cfg_parse_obj(pctx, &cfg_type_bracketed_aml, ret));
1519                 } else if (pctx->token.value.as_char == '!') {
1520                         CHECK(cfg_gettoken(pctx, 0)); /* read "!" */
1521                         CHECK(cfg_parse_obj(pctx, &cfg_type_negated, ret));
1522                 } else {
1523                         goto bad;
1524                 }
1525         } else {
1526         bad:
1527                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1528                              "expected IP match list element");
1529                 return (ISC_R_UNEXPECTEDTOKEN);
1530         }
1531  cleanup:
1532         return (result);
1533 }
1534
1535 /*%
1536  * A negated address match list element (like "! 10.0.0.1").
1537  * Somewhat sneakily, the caller is expected to parse the
1538  * "!", but not to print it.
1539  */
1540
1541 static cfg_tuplefielddef_t negated_fields[] = {
1542         { "value", &cfg_type_addrmatchelt, 0 },
1543         { NULL, NULL, 0 }
1544 };
1545
1546 static void
1547 print_negated(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1548         cfg_print_chars(pctx, "!", 1);
1549         cfg_print_tuple(pctx, obj);
1550 }
1551
1552 static cfg_type_t cfg_type_negated = {
1553         "negated", cfg_parse_tuple, print_negated, NULL, &cfg_rep_tuple,
1554         &negated_fields
1555 };
1556
1557 /*% An address match list element */
1558
1559 static cfg_type_t cfg_type_addrmatchelt = {
1560         "address_match_element", parse_addrmatchelt, NULL, cfg_doc_terminal,
1561         NULL, NULL
1562 };
1563
1564 /*% A bracketed address match list */
1565
1566 static cfg_type_t cfg_type_bracketed_aml = {
1567         "bracketed_aml", cfg_parse_bracketed_list, cfg_print_bracketed_list,
1568         cfg_doc_bracketed_list, &cfg_rep_list, &cfg_type_addrmatchelt
1569 };
1570
1571 /*%
1572  * The socket address syntax in the "controls" statement is silly.
1573  * It allows both socket address families, but also allows "*",
1574  * whis is gratuitously interpreted as the IPv4 wildcard address.
1575  */
1576 static unsigned int controls_sockaddr_flags =
1577         CFG_ADDR_V4OK | CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
1578 static cfg_type_t cfg_type_controls_sockaddr = {
1579         "controls_sockaddr", cfg_parse_sockaddr, cfg_print_sockaddr,
1580         cfg_doc_sockaddr, &cfg_rep_sockaddr, &controls_sockaddr_flags
1581 };
1582
1583 /*%
1584  * Handle the special kludge syntax of the "keys" clause in the "server"
1585  * statement, which takes a single key with or without braces and semicolon.
1586  */
1587 static isc_result_t
1588 parse_server_key_kludge(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1589 {
1590         isc_result_t result;
1591         isc_boolean_t braces = ISC_FALSE;
1592         UNUSED(type);
1593
1594         /* Allow opening brace. */
1595         CHECK(cfg_peektoken(pctx, 0));
1596         if (pctx->token.type == isc_tokentype_special &&
1597             pctx->token.value.as_char == '{') {
1598                 result = cfg_gettoken(pctx, 0);
1599                 braces = ISC_TRUE;
1600         }
1601
1602         CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
1603
1604         if (braces) {
1605                 /* Skip semicolon if present. */
1606                 CHECK(cfg_peektoken(pctx, 0));
1607                 if (pctx->token.type == isc_tokentype_special &&
1608                     pctx->token.value.as_char == ';')
1609                         CHECK(cfg_gettoken(pctx, 0));
1610
1611                 CHECK(cfg_parse_special(pctx, '}'));
1612         }
1613  cleanup:
1614         return (result);
1615 }
1616 static cfg_type_t cfg_type_server_key_kludge = {
1617         "server_key", parse_server_key_kludge, NULL, cfg_doc_terminal,
1618         NULL, NULL
1619 };
1620
1621
1622 /*%
1623  * An optional logging facility.
1624  */
1625
1626 static isc_result_t
1627 parse_optional_facility(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
1628 {
1629         isc_result_t result;
1630         UNUSED(type);
1631
1632         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1633         if (pctx->token.type == isc_tokentype_string ||
1634             pctx->token.type == isc_tokentype_qstring) {
1635                 CHECK(cfg_parse_obj(pctx, &cfg_type_astring, ret));
1636         } else {
1637                 CHECK(cfg_parse_obj(pctx, &cfg_type_void, ret));
1638         }
1639  cleanup:
1640         return (result);
1641 }
1642
1643 static cfg_type_t cfg_type_optional_facility = {
1644         "optional_facility", parse_optional_facility, NULL, cfg_doc_terminal,
1645         NULL, NULL };
1646
1647
1648 /*%
1649  * A log severity.  Return as a string, except "debug N",
1650  * which is returned as a keyword object.
1651  */
1652
1653 static keyword_type_t debug_kw = { "debug", &cfg_type_uint32 };
1654 static cfg_type_t cfg_type_debuglevel = {
1655         "debuglevel", parse_keyvalue,
1656         print_keyvalue, doc_keyvalue,
1657         &cfg_rep_uint32, &debug_kw
1658 };
1659
1660 static isc_result_t
1661 parse_logseverity(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1662         isc_result_t result;
1663         UNUSED(type);
1664
1665         CHECK(cfg_peektoken(pctx, 0));
1666         if (pctx->token.type == isc_tokentype_string &&
1667             strcasecmp(TOKEN_STRING(pctx), "debug") == 0) {
1668                 CHECK(cfg_gettoken(pctx, 0)); /* read "debug" */
1669                 CHECK(cfg_peektoken(pctx, ISC_LEXOPT_NUMBER));
1670                 if (pctx->token.type == isc_tokentype_number) {
1671                         CHECK(cfg_parse_uint32(pctx, NULL, ret));
1672                 } else {
1673                         /*
1674                          * The debug level is optional and defaults to 1.
1675                          * This makes little sense, but we support it for
1676                          * compatibility with BIND 8.
1677                          */
1678                         CHECK(cfg_create_obj(pctx, &cfg_type_uint32, ret));
1679                         (*ret)->value.uint32 = 1;
1680                 }
1681                 (*ret)->type = &cfg_type_debuglevel; /* XXX kludge */
1682         } else {
1683                 CHECK(cfg_parse_obj(pctx, &cfg_type_loglevel, ret));
1684         }
1685  cleanup:
1686         return (result);
1687 }
1688
1689 static cfg_type_t cfg_type_logseverity = {
1690         "log_severity", parse_logseverity, NULL, cfg_doc_terminal,
1691         NULL, NULL };
1692
1693 /*%
1694  * The "file" clause of the "channel" statement.
1695  * This is yet another special case.
1696  */
1697
1698 static const char *logversions_enums[] = { "unlimited", NULL };
1699 static isc_result_t
1700 parse_logversions(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1701         return (parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
1702 }
1703 static cfg_type_t cfg_type_logversions = {
1704         "logversions", parse_logversions, cfg_print_ustring, cfg_doc_terminal,
1705         &cfg_rep_string, logversions_enums
1706 };
1707
1708 static cfg_tuplefielddef_t logfile_fields[] = {
1709         { "file", &cfg_type_qstring, 0 },
1710         { "versions", &cfg_type_logversions, 0 },
1711         { "size", &cfg_type_size, 0 },
1712         { NULL, NULL, 0 }
1713 };
1714
1715 static isc_result_t
1716 parse_logfile(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
1717         isc_result_t result;
1718         cfg_obj_t *obj = NULL;
1719         const cfg_tuplefielddef_t *fields = type->of;   
1720
1721         CHECK(cfg_create_tuple(pctx, type, &obj));      
1722
1723         /* Parse the mandatory "file" field */
1724         CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
1725
1726         /* Parse "versions" and "size" fields in any order. */
1727         for (;;) {
1728                 CHECK(cfg_peektoken(pctx, 0));
1729                 if (pctx->token.type == isc_tokentype_string) {
1730                         CHECK(cfg_gettoken(pctx, 0));           
1731                         if (strcasecmp(TOKEN_STRING(pctx),
1732                                        "versions") == 0 &&
1733                             obj->value.tuple[1] == NULL) {
1734                                 CHECK(cfg_parse_obj(pctx, fields[1].type,
1735                                             &obj->value.tuple[1]));
1736                         } else if (strcasecmp(TOKEN_STRING(pctx),
1737                                               "size") == 0 &&
1738                                    obj->value.tuple[2] == NULL) {
1739                                 CHECK(cfg_parse_obj(pctx, fields[2].type,
1740                                             &obj->value.tuple[2]));
1741                         } else {
1742                                 break;
1743                         }
1744                 } else {
1745                         break;
1746                 }
1747         }
1748
1749         /* Create void objects for missing optional values. */
1750         if (obj->value.tuple[1] == NULL)
1751                 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[1]));
1752         if (obj->value.tuple[2] == NULL)
1753                 CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
1754
1755         *ret = obj;
1756         return (ISC_R_SUCCESS);
1757
1758  cleanup:
1759         CLEANUP_OBJ(obj);       
1760         return (result);
1761 }
1762
1763 static void
1764 print_logfile(cfg_printer_t *pctx, const cfg_obj_t *obj) {
1765         cfg_print_obj(pctx, obj->value.tuple[0]); /* file */
1766         if (obj->value.tuple[1]->type->print != cfg_print_void) {
1767                 cfg_print_chars(pctx, " versions ", 10);
1768                 cfg_print_obj(pctx, obj->value.tuple[1]);
1769         }
1770         if (obj->value.tuple[2]->type->print != cfg_print_void) {
1771                 cfg_print_chars(pctx, " size ", 6);
1772                 cfg_print_obj(pctx, obj->value.tuple[2]);
1773         }
1774 }
1775
1776 static cfg_type_t cfg_type_logfile = {
1777         "log_file", parse_logfile, print_logfile, cfg_doc_terminal,
1778         &cfg_rep_tuple, logfile_fields
1779 };
1780
1781 /*% An IPv4 address with optional port, "*" accepted as wildcard. */
1782 static cfg_type_t cfg_type_sockaddr4wild = {
1783         "sockaddr4wild", cfg_parse_sockaddr, cfg_print_sockaddr,
1784         cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr4wild_flags
1785 };
1786
1787 /*% An IPv6 address with optional port, "*" accepted as wildcard. */
1788 static cfg_type_t cfg_type_sockaddr6wild = {
1789         "v6addrportwild", cfg_parse_sockaddr, cfg_print_sockaddr,
1790         cfg_doc_sockaddr, &cfg_rep_sockaddr, &sockaddr6wild_flags
1791 };
1792
1793 /*%
1794  * lwres
1795  */
1796
1797 static cfg_tuplefielddef_t lwres_view_fields[] = {
1798         { "name", &cfg_type_astring, 0 },
1799         { "class", &cfg_type_optional_class, 0 },
1800         { NULL, NULL, 0 }
1801 };
1802 static cfg_type_t cfg_type_lwres_view = {
1803         "lwres_view", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple, &cfg_rep_tuple,
1804         lwres_view_fields
1805 };
1806
1807 static cfg_type_t cfg_type_lwres_searchlist = {
1808         "lwres_searchlist", cfg_parse_bracketed_list, cfg_print_bracketed_list, cfg_doc_bracketed_list,
1809         &cfg_rep_list, &cfg_type_astring };
1810
1811 static cfg_clausedef_t
1812 lwres_clauses[] = {
1813         { "listen-on", &cfg_type_portiplist, 0 },
1814         { "view", &cfg_type_lwres_view, 0 },
1815         { "search", &cfg_type_lwres_searchlist, 0 },
1816         { "ndots", &cfg_type_uint32, 0 },
1817         { NULL, NULL, 0 }
1818 };
1819
1820 static cfg_clausedef_t *
1821 lwres_clausesets[] = {
1822         lwres_clauses,
1823         NULL
1824 };
1825 static cfg_type_t cfg_type_lwres = {
1826         "lwres", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map,
1827         lwres_clausesets
1828 };
1829
1830 /*%
1831  * rndc
1832  */
1833
1834 static cfg_clausedef_t
1835 rndcconf_options_clauses[] = {
1836         { "default-key", &cfg_type_astring, 0 },
1837         { "default-port", &cfg_type_uint32, 0 },
1838         { "default-server", &cfg_type_astring, 0 },
1839         { "default-source-address", &cfg_type_netaddr4wild, 0 },
1840         { "default-source-address-v6", &cfg_type_netaddr6wild, 0 },
1841         { NULL, NULL, 0 }
1842 };
1843
1844 static cfg_clausedef_t *
1845 rndcconf_options_clausesets[] = {
1846         rndcconf_options_clauses,
1847         NULL
1848 };
1849
1850 static cfg_type_t cfg_type_rndcconf_options = {
1851         "rndcconf_options", cfg_parse_map, cfg_print_map, cfg_doc_map,
1852         &cfg_rep_map, rndcconf_options_clausesets
1853 };
1854
1855 static cfg_clausedef_t
1856 rndcconf_server_clauses[] = {
1857         { "key", &cfg_type_astring, 0 },
1858         { "port", &cfg_type_uint32, 0 },
1859         { "source-address", &cfg_type_netaddr4wild, 0 },
1860         { "source-address-v6", &cfg_type_netaddr6wild, 0 },
1861         { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
1862         { NULL, NULL, 0 }
1863 };
1864
1865 static cfg_clausedef_t *
1866 rndcconf_server_clausesets[] = {
1867         rndcconf_server_clauses,
1868         NULL
1869 };
1870
1871 static cfg_type_t cfg_type_rndcconf_server = {
1872         "rndcconf_server", cfg_parse_named_map, cfg_print_map, cfg_doc_map,
1873         &cfg_rep_map, rndcconf_server_clausesets
1874 };
1875
1876 static cfg_clausedef_t
1877 rndcconf_clauses[] = {
1878         { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
1879         { "server", &cfg_type_rndcconf_server, CFG_CLAUSEFLAG_MULTI },
1880         { "options", &cfg_type_rndcconf_options, 0 },
1881         { NULL, NULL, 0 }
1882 };
1883
1884 static cfg_clausedef_t *
1885 rndcconf_clausesets[] = {
1886         rndcconf_clauses,
1887         NULL
1888 };
1889
1890 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndcconf = {
1891         "rndcconf", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1892         &cfg_rep_map, rndcconf_clausesets
1893 };
1894
1895 static cfg_clausedef_t
1896 rndckey_clauses[] = {
1897         { "key", &cfg_type_key, 0 },
1898         { NULL, NULL, 0 }
1899 };
1900
1901 static cfg_clausedef_t *
1902 rndckey_clausesets[] = {
1903         rndckey_clauses,
1904         NULL
1905 };
1906
1907 LIBISCCFG_EXTERNAL_DATA cfg_type_t cfg_type_rndckey = {
1908         "rndckey", cfg_parse_mapbody, cfg_print_mapbody, cfg_doc_mapbody,
1909         &cfg_rep_map, rndckey_clausesets
1910 };
1911
1912 static cfg_tuplefielddef_t nameport_fields[] = {
1913         { "name", &cfg_type_astring, 0 },
1914         { "port", &cfg_type_optional_port, 0 },
1915         { NULL, NULL, 0 }
1916 };
1917 static cfg_type_t cfg_type_nameport = {
1918         "nameport", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1919         &cfg_rep_tuple, nameport_fields
1920 };
1921
1922 static void
1923 doc_sockaddrnameport(cfg_printer_t *pctx, const cfg_type_t *type) {
1924         UNUSED(type);
1925         cfg_print_chars(pctx, "( ", 2);
1926         cfg_print_cstr(pctx, "<quoted_string>");
1927         cfg_print_chars(pctx, " ", 1);
1928         cfg_print_cstr(pctx, "[port <integer>]");
1929         cfg_print_chars(pctx, " | ", 3);
1930         cfg_print_cstr(pctx, "<ipv4_address>");
1931         cfg_print_chars(pctx, " ", 1);
1932         cfg_print_cstr(pctx, "[port <integer>]");
1933         cfg_print_chars(pctx, " | ", 3);
1934         cfg_print_cstr(pctx, "<ipv6_address>");
1935         cfg_print_chars(pctx, " ", 1);
1936         cfg_print_cstr(pctx, "[port <integer>]");
1937         cfg_print_chars(pctx, " )", 2);
1938 }
1939
1940 static isc_result_t
1941 parse_sockaddrnameport(cfg_parser_t *pctx, const cfg_type_t *type,
1942                        cfg_obj_t **ret)
1943 {
1944         isc_result_t result;
1945         cfg_obj_t *obj = NULL;
1946         UNUSED(type);
1947
1948         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
1949         if (pctx->token.type == isc_tokentype_string ||
1950             pctx->token.type == isc_tokentype_qstring) {
1951                 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
1952                         CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
1953                 else {
1954                         const cfg_tuplefielddef_t *fields =
1955                                                    cfg_type_nameport.of;        
1956                         CHECK(cfg_create_tuple(pctx, &cfg_type_nameport,
1957                                                &obj));  
1958                         CHECK(cfg_parse_obj(pctx, fields[0].type,
1959                                             &obj->value.tuple[0]));
1960                         CHECK(cfg_parse_obj(pctx, fields[1].type,
1961                                             &obj->value.tuple[1]));
1962                         *ret = obj;
1963                         obj = NULL;
1964                 }
1965         } else {
1966                 cfg_parser_error(pctx, CFG_LOG_NEAR,
1967                              "expected IP address or hostname");
1968                 return (ISC_R_UNEXPECTEDTOKEN);
1969         }
1970  cleanup:
1971         CLEANUP_OBJ(obj);       
1972         return (result);
1973 }
1974
1975 static cfg_type_t cfg_type_sockaddrnameport = {
1976         "sockaddrnameport_element", parse_sockaddrnameport, NULL,
1977          doc_sockaddrnameport, NULL, NULL
1978 };
1979
1980 static cfg_type_t cfg_type_bracketed_sockaddrnameportlist = {
1981         "bracketed_sockaddrnameportlist", cfg_parse_bracketed_list,
1982         cfg_print_bracketed_list, cfg_doc_bracketed_list,
1983         &cfg_rep_list, &cfg_type_sockaddrnameport
1984 };
1985
1986 /*%
1987  * A list of socket addresses or name with an optional default port,
1988  * as used in the dual-stack-servers option.  E.g.,
1989  * "port 1234 { dual-stack-servers.net; 10.0.0.1; 1::2 port 69; }"
1990  */
1991 static cfg_tuplefielddef_t nameportiplist_fields[] = {
1992         { "port", &cfg_type_optional_port, 0 },
1993         { "addresses", &cfg_type_bracketed_sockaddrnameportlist, 0 },
1994         { NULL, NULL, 0 }
1995 };
1996
1997 static cfg_type_t cfg_type_nameportiplist = {
1998         "nameportiplist", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
1999         &cfg_rep_tuple, nameportiplist_fields
2000 };
2001
2002 /*%
2003  * masters element.
2004  */
2005
2006 static void
2007 doc_masterselement(cfg_printer_t *pctx, const cfg_type_t *type) {
2008         UNUSED(type);
2009         cfg_print_chars(pctx, "( ", 2);
2010         cfg_print_cstr(pctx, "<masters>");
2011         cfg_print_chars(pctx, " | ", 3);
2012         cfg_print_cstr(pctx, "<ipv4_address>");
2013         cfg_print_chars(pctx, " ", 1);
2014         cfg_print_cstr(pctx, "[port <integer>]");
2015         cfg_print_chars(pctx, " | ", 3);
2016         cfg_print_cstr(pctx, "<ipv6_address>");
2017         cfg_print_chars(pctx, " ", 1);
2018         cfg_print_cstr(pctx, "[port <integer>]");
2019         cfg_print_chars(pctx, " )", 2);
2020 }
2021
2022 static isc_result_t
2023 parse_masterselement(cfg_parser_t *pctx, const cfg_type_t *type,
2024                      cfg_obj_t **ret)
2025 {
2026         isc_result_t result;
2027         cfg_obj_t *obj = NULL;
2028         UNUSED(type);
2029
2030         CHECK(cfg_peektoken(pctx, CFG_LEXOPT_QSTRING));
2031         if (pctx->token.type == isc_tokentype_string ||
2032             pctx->token.type == isc_tokentype_qstring) {
2033                 if (cfg_lookingat_netaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V6OK))
2034                         CHECK(cfg_parse_sockaddr(pctx, &cfg_type_sockaddr, ret));
2035                 else
2036                         CHECK(cfg_parse_astring(pctx, &cfg_type_astring, ret));
2037         } else {
2038                 cfg_parser_error(pctx, CFG_LOG_NEAR,
2039                              "expected IP address or masters name");
2040                 return (ISC_R_UNEXPECTEDTOKEN);
2041         }
2042  cleanup:
2043         CLEANUP_OBJ(obj);       
2044         return (result);
2045 }
2046
2047 static cfg_type_t cfg_type_masterselement = {
2048         "masters_element", parse_masterselement, NULL,
2049          doc_masterselement, NULL, NULL
2050 };