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