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