2 * Copyright (C) 2004-2006 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2003 Internet Software Consortium.
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.
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.
18 /* $Id: zoneconf.c,v 1.87.2.4.10.19 2006/02/28 06:32:53 marka Exp $ */
22 #include <isc/buffer.h>
25 #include <isc/print.h>
26 #include <isc/string.h> /* Required for HP/UX (and others?) */
30 #include <dns/fixedname.h>
33 #include <dns/rdatatype.h>
38 #include <named/config.h>
39 #include <named/globals.h>
40 #include <named/log.h>
41 #include <named/server.h>
42 #include <named/zoneconf.h>
45 * These are BIND9 server defaults, not necessarily identical to the
46 * library defaults defined in zone.c.
48 #define RETERR(x) do { \
49 isc_result_t _r = (x); \
50 if (_r != ISC_R_SUCCESS) \
55 * Convenience function for configuring a single zone ACL.
58 configure_zone_acl(const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
59 const cfg_obj_t *config, const char *aclname,
60 ns_aclconfctx_t *actx, dns_zone_t *zone,
61 void (*setzacl)(dns_zone_t *, dns_acl_t *),
62 void (*clearzacl)(dns_zone_t *))
65 const cfg_obj_t *maps[4];
66 const cfg_obj_t *aclobj = NULL;
68 dns_acl_t *dacl = NULL;
71 maps[i++] = cfg_tuple_get(zconfig, "options");
73 maps[i++] = cfg_tuple_get(vconfig, "options");
75 const cfg_obj_t *options = NULL;
76 (void)cfg_map_get(config, "options", &options);
82 result = ns_config_get(maps, aclname, &aclobj);
85 return (ISC_R_SUCCESS);
88 result = ns_acl_fromconfig(aclobj, config, actx,
89 dns_zone_getmctx(zone), &dacl);
90 if (result != ISC_R_SUCCESS)
92 (*setzacl)(zone, dacl);
93 dns_acl_detach(&dacl);
94 return (ISC_R_SUCCESS);
98 * Parse the zone update-policy statement.
101 configure_zone_ssutable(const cfg_obj_t *zconfig, dns_zone_t *zone) {
102 const cfg_obj_t *updatepolicy = NULL;
103 const cfg_listelt_t *element, *element2;
104 dns_ssutable_t *table = NULL;
105 isc_mem_t *mctx = dns_zone_getmctx(zone);
108 (void)cfg_map_get(zconfig, "update-policy", &updatepolicy);
109 if (updatepolicy == NULL) {
110 dns_zone_setssutable(zone, NULL);
111 return (ISC_R_SUCCESS);
114 result = dns_ssutable_create(mctx, &table);
115 if (result != ISC_R_SUCCESS)
118 for (element = cfg_list_first(updatepolicy);
120 element = cfg_list_next(element))
122 const cfg_obj_t *stmt = cfg_listelt_value(element);
123 const cfg_obj_t *mode = cfg_tuple_get(stmt, "mode");
124 const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
125 const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
126 const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
127 const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
129 isc_boolean_t grant = ISC_FALSE;
130 unsigned int mtype = DNS_SSUMATCHTYPE_NAME;
131 dns_fixedname_t fname, fident;
133 dns_rdatatype_t *types;
136 str = cfg_obj_asstring(mode);
137 if (strcasecmp(str, "grant") == 0)
139 else if (strcasecmp(str, "deny") == 0)
144 str = cfg_obj_asstring(matchtype);
145 if (strcasecmp(str, "name") == 0)
146 mtype = DNS_SSUMATCHTYPE_NAME;
147 else if (strcasecmp(str, "subdomain") == 0)
148 mtype = DNS_SSUMATCHTYPE_SUBDOMAIN;
149 else if (strcasecmp(str, "wildcard") == 0)
150 mtype = DNS_SSUMATCHTYPE_WILDCARD;
151 else if (strcasecmp(str, "self") == 0)
152 mtype = DNS_SSUMATCHTYPE_SELF;
156 dns_fixedname_init(&fident);
157 str = cfg_obj_asstring(identity);
158 isc_buffer_init(&b, str, strlen(str));
159 isc_buffer_add(&b, strlen(str));
160 result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
161 dns_rootname, ISC_FALSE, NULL);
162 if (result != ISC_R_SUCCESS) {
163 cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
164 "'%s' is not a valid name", str);
168 dns_fixedname_init(&fname);
169 str = cfg_obj_asstring(dname);
170 isc_buffer_init(&b, str, strlen(str));
171 isc_buffer_add(&b, strlen(str));
172 result = dns_name_fromtext(dns_fixedname_name(&fname), &b,
173 dns_rootname, ISC_FALSE, NULL);
174 if (result != ISC_R_SUCCESS) {
175 cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
176 "'%s' is not a valid name", str);
180 n = ns_config_listcount(typelist);
184 types = isc_mem_get(mctx, n * sizeof(dns_rdatatype_t));
186 result = ISC_R_NOMEMORY;
192 for (element2 = cfg_list_first(typelist);
194 element2 = cfg_list_next(element2))
196 const cfg_obj_t *typeobj;
201 typeobj = cfg_listelt_value(element2);
202 str = cfg_obj_asstring(typeobj);
203 DE_CONST(str, r.base);
204 r.length = strlen(str);
206 result = dns_rdatatype_fromtext(&types[i++], &r);
207 if (result != ISC_R_SUCCESS) {
208 cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
209 "'%s' is not a valid type", str);
210 isc_mem_put(mctx, types,
211 n * sizeof(dns_rdatatype_t));
217 result = dns_ssutable_addrule(table, grant,
218 dns_fixedname_name(&fident),
220 dns_fixedname_name(&fname),
223 isc_mem_put(mctx, types, n * sizeof(dns_rdatatype_t));
224 if (result != ISC_R_SUCCESS) {
230 result = ISC_R_SUCCESS;
231 dns_zone_setssutable(zone, table);
234 dns_ssutable_detach(&table);
239 * Convert a config file zone type into a server zone type.
241 static inline dns_zonetype_t
242 zonetype_fromconfig(const cfg_obj_t *map) {
243 const cfg_obj_t *obj = NULL;
246 result = cfg_map_get(map, "type", &obj);
247 INSIST(result == ISC_R_SUCCESS);
248 return (ns_config_getzonetype(obj));
252 * Helper function for strtoargv(). Pardon the gratuitous recursion.
255 strtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
256 char ***argvp, unsigned int n)
260 /* Discard leading whitespace. */
261 while (*s == ' ' || *s == '\t')
265 /* We have reached the end of the string. */
267 *argvp = isc_mem_get(mctx, n * sizeof(char *));
269 return (ISC_R_NOMEMORY);
272 while (*p != ' ' && *p != '\t' && *p != '\0')
277 result = strtoargvsub(mctx, p, argcp, argvp, n + 1);
278 if (result != ISC_R_SUCCESS)
282 return (ISC_R_SUCCESS);
286 * Tokenize the string "s" into whitespace-separated words,
287 * return the number of words in '*argcp' and an array
288 * of pointers to the words in '*argvp'. The caller
289 * must free the array using isc_mem_put(). The string
290 * is modified in-place.
293 strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) {
294 return (strtoargvsub(mctx, s, argcp, argvp, 0));
298 checknames(dns_zonetype_t ztype, const cfg_obj_t **maps,
299 const cfg_obj_t **objp)
301 const char *zone = NULL;
305 case dns_zone_slave: zone = "slave"; break;
306 case dns_zone_master: zone = "master"; break;
310 result = ns_checknames_get(maps, zone, objp);
311 INSIST(result == ISC_R_SUCCESS);
315 ns_zone_configure(const cfg_obj_t *config, const cfg_obj_t *vconfig,
316 const cfg_obj_t *zconfig, ns_aclconfctx_t *ac,
321 dns_rdataclass_t zclass;
322 dns_rdataclass_t vclass;
323 const cfg_obj_t *maps[5];
324 const cfg_obj_t *zoptions = NULL;
325 const cfg_obj_t *options = NULL;
326 const cfg_obj_t *obj;
327 const char *filename = NULL;
328 dns_notifytype_t notifytype = dns_notifytype_yes;
329 isc_sockaddr_t *addrs;
330 dns_name_t **keynames;
335 static char default_dbtype[] = "rbt";
336 isc_mem_t *mctx = dns_zone_getmctx(zone);
337 dns_dialuptype_t dialup = dns_dialuptype_no;
338 dns_zonetype_t ztype;
340 isc_int32_t journal_size;
344 isc_boolean_t check = ISC_FALSE, fail = ISC_FALSE;
347 if (zconfig != NULL) {
348 zoptions = cfg_tuple_get(zconfig, "options");
349 maps[i++] = zoptions;
352 maps[i++] = cfg_tuple_get(vconfig, "options");
353 if (config != NULL) {
354 (void)cfg_map_get(config, "options", &options);
358 maps[i++] = ns_g_defaults;
362 RETERR(ns_config_getclass(cfg_tuple_get(vconfig, "class"),
363 dns_rdataclass_in, &vclass));
365 vclass = dns_rdataclass_in;
368 * Configure values common to all zone types.
371 zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
373 RETERR(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
375 dns_zone_setclass(zone, zclass);
377 ztype = zonetype_fromconfig(zoptions);
378 dns_zone_settype(zone, ztype);
381 result = cfg_map_get(zoptions, "database", &obj);
382 if (result == ISC_R_SUCCESS)
383 cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
385 cpval = default_dbtype;
388 return(ISC_R_NOMEMORY);
390 result = strtoargv(mctx, cpval, &dbargc, &dbargv);
391 if (result != ISC_R_SUCCESS && cpval != default_dbtype) {
392 isc_mem_free(mctx, cpval);
397 * ANSI C is strange here. There is no logical reason why (char **)
398 * cannot be promoted automatically to (const char * const *) by the
399 * compiler w/o generating a warning.
401 result = dns_zone_setdbtype(zone, dbargc, (const char * const *)dbargv);
402 isc_mem_put(mctx, dbargv, dbargc * sizeof(*dbargv));
403 if (cpval != default_dbtype)
404 isc_mem_free(mctx, cpval);
405 if (result != ISC_R_SUCCESS)
409 result = cfg_map_get(zoptions, "file", &obj);
410 if (result == ISC_R_SUCCESS)
411 filename = cfg_obj_asstring(obj);
412 RETERR(dns_zone_setfile(zone, filename));
414 if (ztype == dns_zone_slave)
415 RETERR(configure_zone_acl(zconfig, vconfig, config,
416 "allow-notify", ac, zone,
417 dns_zone_setnotifyacl,
418 dns_zone_clearnotifyacl));
420 * XXXAG This probably does not make sense for stubs.
422 RETERR(configure_zone_acl(zconfig, vconfig, config,
423 "allow-query", ac, zone,
424 dns_zone_setqueryacl,
425 dns_zone_clearqueryacl));
428 result = ns_config_get(maps, "dialup", &obj);
429 INSIST(result == ISC_R_SUCCESS);
430 if (cfg_obj_isboolean(obj)) {
431 if (cfg_obj_asboolean(obj))
432 dialup = dns_dialuptype_yes;
434 dialup = dns_dialuptype_no;
436 const char *dialupstr = cfg_obj_asstring(obj);
437 if (strcasecmp(dialupstr, "notify") == 0)
438 dialup = dns_dialuptype_notify;
439 else if (strcasecmp(dialupstr, "notify-passive") == 0)
440 dialup = dns_dialuptype_notifypassive;
441 else if (strcasecmp(dialupstr, "refresh") == 0)
442 dialup = dns_dialuptype_refresh;
443 else if (strcasecmp(dialupstr, "passive") == 0)
444 dialup = dns_dialuptype_passive;
448 dns_zone_setdialup(zone, dialup);
451 result = ns_config_get(maps, "zone-statistics", &obj);
452 INSIST(result == ISC_R_SUCCESS);
453 RETERR(dns_zone_setstatistics(zone, cfg_obj_asboolean(obj)));
456 * Configure master functionality. This applies
457 * to primary masters (type "master") and slaves
458 * acting as masters (type "slave"), but not to stubs.
460 if (ztype != dns_zone_stub) {
462 result = ns_config_get(maps, "notify", &obj);
463 INSIST(result == ISC_R_SUCCESS);
464 if (cfg_obj_isboolean(obj)) {
465 if (cfg_obj_asboolean(obj))
466 notifytype = dns_notifytype_yes;
468 notifytype = dns_notifytype_no;
470 const char *notifystr = cfg_obj_asstring(obj);
471 if (strcasecmp(notifystr, "explicit") == 0)
472 notifytype = dns_notifytype_explicit;
476 dns_zone_setnotifytype(zone, notifytype);
479 result = ns_config_get(maps, "also-notify", &obj);
480 if (result == ISC_R_SUCCESS) {
481 isc_sockaddr_t *addrs = NULL;
482 isc_uint32_t addrcount;
483 result = ns_config_getiplist(config, obj, 0, mctx,
485 if (result != ISC_R_SUCCESS)
487 result = dns_zone_setalsonotify(zone, addrs,
489 ns_config_putiplist(mctx, &addrs, addrcount);
490 if (result != ISC_R_SUCCESS)
493 RETERR(dns_zone_setalsonotify(zone, NULL, 0));
496 result = ns_config_get(maps, "notify-source", &obj);
497 INSIST(result == ISC_R_SUCCESS);
498 RETERR(dns_zone_setnotifysrc4(zone, cfg_obj_assockaddr(obj)));
499 ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
502 result = ns_config_get(maps, "notify-source-v6", &obj);
503 INSIST(result == ISC_R_SUCCESS);
504 RETERR(dns_zone_setnotifysrc6(zone, cfg_obj_assockaddr(obj)));
505 ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
507 RETERR(configure_zone_acl(zconfig, vconfig, config,
508 "allow-transfer", ac, zone,
510 dns_zone_clearxfracl));
513 result = ns_config_get(maps, "max-transfer-time-out", &obj);
514 INSIST(result == ISC_R_SUCCESS);
515 dns_zone_setmaxxfrout(zone, cfg_obj_asuint32(obj) * 60);
518 result = ns_config_get(maps, "max-transfer-idle-out", &obj);
519 INSIST(result == ISC_R_SUCCESS);
520 dns_zone_setidleout(zone, cfg_obj_asuint32(obj) * 60);
523 result = ns_config_get(maps, "max-journal-size", &obj);
524 INSIST(result == ISC_R_SUCCESS);
525 dns_zone_setjournalsize(zone, -1);
526 if (cfg_obj_isstring(obj)) {
527 const char *str = cfg_obj_asstring(obj);
528 INSIST(strcasecmp(str, "unlimited") == 0);
529 journal_size = ISC_UINT32_MAX / 2;
531 isc_resourcevalue_t value;
532 value = cfg_obj_asuint64(obj);
533 if (value > ISC_UINT32_MAX / 2) {
534 cfg_obj_log(obj, ns_g_lctx,
537 "%" ISC_PRINT_QUADFORMAT "d' "
542 journal_size = (isc_uint32_t)value;
544 dns_zone_setjournalsize(zone, journal_size);
547 result = ns_config_get(maps, "ixfr-from-differences", &obj);
548 INSIST(result == ISC_R_SUCCESS);
549 dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS,
550 cfg_obj_asboolean(obj));
552 checknames(ztype, maps, &obj);
554 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
557 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
558 fail = check = ISC_TRUE;
559 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
560 fail = check = ISC_FALSE;
563 dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, check);
564 dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, fail);
568 * Configure update-related options. These apply to
569 * primary masters only.
571 if (ztype == dns_zone_master) {
572 dns_acl_t *updateacl;
573 RETERR(configure_zone_acl(zconfig, vconfig, config,
574 "allow-update", ac, zone,
575 dns_zone_setupdateacl,
576 dns_zone_clearupdateacl));
578 updateacl = dns_zone_getupdateacl(zone);
579 if (updateacl != NULL && dns_acl_isinsecure(updateacl))
580 isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
581 NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
582 "zone '%s' allows updates by IP "
583 "address, which is insecure",
586 RETERR(configure_zone_ssutable(zoptions, zone));
589 result = ns_config_get(maps, "sig-validity-interval", &obj);
590 INSIST(result == ISC_R_SUCCESS);
591 dns_zone_setsigvalidityinterval(zone,
592 cfg_obj_asuint32(obj) * 86400);
595 result = ns_config_get(maps, "key-directory", &obj);
596 if (result == ISC_R_SUCCESS) {
597 filename = cfg_obj_asstring(obj);
598 if (!isc_file_isabsolute(filename)) {
599 cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
600 "key-directory '%s' "
601 "is not absolute", filename);
602 return (ISC_R_FAILURE);
604 RETERR(dns_zone_setkeydirectory(zone, filename));
607 } else if (ztype == dns_zone_slave) {
608 RETERR(configure_zone_acl(zconfig, vconfig, config,
609 "allow-update-forwarding", ac, zone,
610 dns_zone_setforwardacl,
611 dns_zone_clearforwardacl));
615 * Configure slave functionality.
622 result = cfg_map_get(zoptions, "masters", &obj);
626 RETERR(ns_config_getipandkeylist(config, obj, mctx,
629 result = dns_zone_setmasterswithkeys(zone, addrs,
631 ns_config_putipandkeylist(mctx, &addrs, &keynames,
634 result = dns_zone_setmasters(zone, NULL, 0);
640 result = ns_config_get(maps, "multi-master", &obj);
641 INSIST(result == ISC_R_SUCCESS);
642 multi = cfg_obj_asboolean(obj);
644 dns_zone_setoption(zone, DNS_ZONEOPT_MULTIMASTER, multi);
647 result = ns_config_get(maps, "max-transfer-time-in", &obj);
648 INSIST(result == ISC_R_SUCCESS);
649 dns_zone_setmaxxfrin(zone, cfg_obj_asuint32(obj) * 60);
652 result = ns_config_get(maps, "max-transfer-idle-in", &obj);
653 INSIST(result == ISC_R_SUCCESS);
654 dns_zone_setidlein(zone, cfg_obj_asuint32(obj) * 60);
657 result = ns_config_get(maps, "max-refresh-time", &obj);
658 INSIST(result == ISC_R_SUCCESS);
659 dns_zone_setmaxrefreshtime(zone, cfg_obj_asuint32(obj));
662 result = ns_config_get(maps, "min-refresh-time", &obj);
663 INSIST(result == ISC_R_SUCCESS);
664 dns_zone_setminrefreshtime(zone, cfg_obj_asuint32(obj));
667 result = ns_config_get(maps, "max-retry-time", &obj);
668 INSIST(result == ISC_R_SUCCESS);
669 dns_zone_setmaxretrytime(zone, cfg_obj_asuint32(obj));
672 result = ns_config_get(maps, "min-retry-time", &obj);
673 INSIST(result == ISC_R_SUCCESS);
674 dns_zone_setminretrytime(zone, cfg_obj_asuint32(obj));
677 result = ns_config_get(maps, "transfer-source", &obj);
678 INSIST(result == ISC_R_SUCCESS);
679 RETERR(dns_zone_setxfrsource4(zone, cfg_obj_assockaddr(obj)));
680 ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
683 result = ns_config_get(maps, "transfer-source-v6", &obj);
684 INSIST(result == ISC_R_SUCCESS);
685 RETERR(dns_zone_setxfrsource6(zone, cfg_obj_assockaddr(obj)));
686 ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
689 result = ns_config_get(maps, "alt-transfer-source", &obj);
690 INSIST(result == ISC_R_SUCCESS);
691 RETERR(dns_zone_setaltxfrsource4(zone, cfg_obj_assockaddr(obj)));
694 result = ns_config_get(maps, "alt-transfer-source-v6", &obj);
695 INSIST(result == ISC_R_SUCCESS);
696 RETERR(dns_zone_setaltxfrsource6(zone, cfg_obj_assockaddr(obj)));
699 (void)ns_config_get(maps, "use-alt-transfer-source", &obj);
702 * Default off when views are in use otherwise
703 * on for BIND 8 compatibility.
705 view = dns_zone_getview(zone);
706 if (view != NULL && strcmp(view->name, "_default") == 0)
711 alt = cfg_obj_asboolean(obj);
712 dns_zone_setoption(zone, DNS_ZONEOPT_USEALTXFRSRC, alt);
720 return (ISC_R_SUCCESS);
724 ns_zone_reusable(dns_zone_t *zone, const cfg_obj_t *zconfig) {
725 const cfg_obj_t *zoptions = NULL;
726 const cfg_obj_t *obj = NULL;
727 const char *cfilename;
728 const char *zfilename;
730 zoptions = cfg_tuple_get(zconfig, "options");
732 if (zonetype_fromconfig(zoptions) != dns_zone_gettype(zone))
736 (void)cfg_map_get(zoptions, "file", &obj);
738 cfilename = cfg_obj_asstring(obj);
741 zfilename = dns_zone_getfile(zone);
742 if (!((cfilename == NULL && zfilename == NULL) ||
743 (cfilename != NULL && zfilename != NULL &&
744 strcmp(cfilename, zfilename) == 0)))