2 * Copyright (C) 2004, 2005 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.15 2005/09/06 02:12:39 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(cfg_obj_t *zconfig, cfg_obj_t *vconfig, cfg_obj_t *config,
59 const char *aclname, ns_aclconfctx_t *actx,
61 void (*setzacl)(dns_zone_t *, dns_acl_t *),
62 void (*clearzacl)(dns_zone_t *))
66 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 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(cfg_obj_t *zconfig, dns_zone_t *zone) {
102 cfg_obj_t *updatepolicy = NULL;
103 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 return (ISC_R_SUCCESS);
112 result = dns_ssutable_create(mctx, &table);
113 if (result != ISC_R_SUCCESS)
116 for (element = cfg_list_first(updatepolicy);
118 element = cfg_list_next(element))
120 cfg_obj_t *stmt = cfg_listelt_value(element);
121 cfg_obj_t *mode = cfg_tuple_get(stmt, "mode");
122 cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
123 cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
124 cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
125 cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
127 isc_boolean_t grant = ISC_FALSE;
128 unsigned int mtype = DNS_SSUMATCHTYPE_NAME;
129 dns_fixedname_t fname, fident;
131 dns_rdatatype_t *types;
134 str = cfg_obj_asstring(mode);
135 if (strcasecmp(str, "grant") == 0)
137 else if (strcasecmp(str, "deny") == 0)
142 str = cfg_obj_asstring(matchtype);
143 if (strcasecmp(str, "name") == 0)
144 mtype = DNS_SSUMATCHTYPE_NAME;
145 else if (strcasecmp(str, "subdomain") == 0)
146 mtype = DNS_SSUMATCHTYPE_SUBDOMAIN;
147 else if (strcasecmp(str, "wildcard") == 0)
148 mtype = DNS_SSUMATCHTYPE_WILDCARD;
149 else if (strcasecmp(str, "self") == 0)
150 mtype = DNS_SSUMATCHTYPE_SELF;
154 dns_fixedname_init(&fident);
155 str = cfg_obj_asstring(identity);
156 isc_buffer_init(&b, str, strlen(str));
157 isc_buffer_add(&b, strlen(str));
158 result = dns_name_fromtext(dns_fixedname_name(&fident), &b,
159 dns_rootname, ISC_FALSE, NULL);
160 if (result != ISC_R_SUCCESS) {
161 cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
162 "'%s' is not a valid name", str);
166 dns_fixedname_init(&fname);
167 str = cfg_obj_asstring(dname);
168 isc_buffer_init(&b, str, strlen(str));
169 isc_buffer_add(&b, strlen(str));
170 result = dns_name_fromtext(dns_fixedname_name(&fname), &b,
171 dns_rootname, ISC_FALSE, NULL);
172 if (result != ISC_R_SUCCESS) {
173 cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
174 "'%s' is not a valid name", str);
178 n = ns_config_listcount(typelist);
182 types = isc_mem_get(mctx, n * sizeof(dns_rdatatype_t));
184 result = ISC_R_NOMEMORY;
190 for (element2 = cfg_list_first(typelist);
192 element2 = cfg_list_next(element2))
199 typeobj = cfg_listelt_value(element2);
200 str = cfg_obj_asstring(typeobj);
202 r.length = strlen(str);
204 result = dns_rdatatype_fromtext(&types[i++], &r);
205 if (result != ISC_R_SUCCESS) {
206 cfg_obj_log(identity, ns_g_lctx, ISC_LOG_ERROR,
207 "'%s' is not a valid type", str);
208 isc_mem_put(mctx, types,
209 n * sizeof(dns_rdatatype_t));
215 result = dns_ssutable_addrule(table, grant,
216 dns_fixedname_name(&fident),
218 dns_fixedname_name(&fname),
221 isc_mem_put(mctx, types, n * sizeof(dns_rdatatype_t));
222 if (result != ISC_R_SUCCESS) {
228 result = ISC_R_SUCCESS;
229 dns_zone_setssutable(zone, table);
232 dns_ssutable_detach(&table);
237 * Convert a config file zone type into a server zone type.
239 static inline dns_zonetype_t
240 zonetype_fromconfig(cfg_obj_t *map) {
241 cfg_obj_t *obj = NULL;
244 result = cfg_map_get(map, "type", &obj);
245 INSIST(result == ISC_R_SUCCESS);
246 return (ns_config_getzonetype(obj));
250 * Helper function for strtoargv(). Pardon the gratuitous recursion.
253 strtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
254 char ***argvp, unsigned int n)
258 /* Discard leading whitespace. */
259 while (*s == ' ' || *s == '\t')
263 /* We have reached the end of the string. */
265 *argvp = isc_mem_get(mctx, n * sizeof(char *));
267 return (ISC_R_NOMEMORY);
270 while (*p != ' ' && *p != '\t' && *p != '\0')
275 result = strtoargvsub(mctx, p, argcp, argvp, n + 1);
276 if (result != ISC_R_SUCCESS)
280 return (ISC_R_SUCCESS);
284 * Tokenize the string "s" into whitespace-separated words,
285 * return the number of words in '*argcp' and an array
286 * of pointers to the words in '*argvp'. The caller
287 * must free the array using isc_mem_put(). The string
288 * is modified in-place.
291 strtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) {
292 return (strtoargvsub(mctx, s, argcp, argvp, 0));
296 checknames(dns_zonetype_t ztype, cfg_obj_t **maps, cfg_obj_t **objp) {
297 const char *zone = NULL;
301 case dns_zone_slave: zone = "slave"; break;
302 case dns_zone_master: zone = "master"; break;
306 result = ns_checknames_get(maps, zone, objp);
307 INSIST(result == ISC_R_SUCCESS);
311 ns_zone_configure(cfg_obj_t *config, cfg_obj_t *vconfig, cfg_obj_t *zconfig,
312 ns_aclconfctx_t *ac, dns_zone_t *zone)
316 dns_rdataclass_t zclass;
317 dns_rdataclass_t vclass;
319 cfg_obj_t *zoptions = NULL;
320 cfg_obj_t *options = NULL;
322 const char *filename = NULL;
323 dns_notifytype_t notifytype = dns_notifytype_yes;
324 isc_sockaddr_t *addrs;
325 dns_name_t **keynames;
330 static char default_dbtype[] = "rbt";
331 isc_mem_t *mctx = dns_zone_getmctx(zone);
332 dns_dialuptype_t dialup = dns_dialuptype_no;
333 dns_zonetype_t ztype;
335 isc_int32_t journal_size;
339 isc_boolean_t check = ISC_FALSE, fail = ISC_FALSE;
342 if (zconfig != NULL) {
343 zoptions = cfg_tuple_get(zconfig, "options");
344 maps[i++] = zoptions;
347 maps[i++] = cfg_tuple_get(vconfig, "options");
348 if (config != NULL) {
349 (void)cfg_map_get(config, "options", &options);
353 maps[i++] = ns_g_defaults;
357 RETERR(ns_config_getclass(cfg_tuple_get(vconfig, "class"),
358 dns_rdataclass_in, &vclass));
360 vclass = dns_rdataclass_in;
363 * Configure values common to all zone types.
366 zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
368 RETERR(ns_config_getclass(cfg_tuple_get(zconfig, "class"),
370 dns_zone_setclass(zone, zclass);
372 ztype = zonetype_fromconfig(zoptions);
373 dns_zone_settype(zone, ztype);
376 result = cfg_map_get(zoptions, "database", &obj);
377 if (result == ISC_R_SUCCESS)
378 cpval = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
380 cpval = default_dbtype;
383 return(ISC_R_NOMEMORY);
385 result = strtoargv(mctx, cpval, &dbargc, &dbargv);
386 if (result != ISC_R_SUCCESS && cpval != default_dbtype) {
387 isc_mem_free(mctx, cpval);
392 * ANSI C is strange here. There is no logical reason why (char **)
393 * cannot be promoted automatically to (const char * const *) by the
394 * compiler w/o generating a warning.
396 result = dns_zone_setdbtype(zone, dbargc, (const char * const *)dbargv);
397 isc_mem_put(mctx, dbargv, dbargc * sizeof(*dbargv));
398 if (cpval != default_dbtype)
399 isc_mem_free(mctx, cpval);
400 if (result != ISC_R_SUCCESS)
404 result = cfg_map_get(zoptions, "file", &obj);
405 if (result == ISC_R_SUCCESS)
406 filename = cfg_obj_asstring(obj);
407 RETERR(dns_zone_setfile(zone, filename));
409 if (ztype == dns_zone_slave)
410 RETERR(configure_zone_acl(zconfig, vconfig, config,
411 "allow-notify", ac, zone,
412 dns_zone_setnotifyacl,
413 dns_zone_clearnotifyacl));
415 * XXXAG This probably does not make sense for stubs.
417 RETERR(configure_zone_acl(zconfig, vconfig, config,
418 "allow-query", ac, zone,
419 dns_zone_setqueryacl,
420 dns_zone_clearqueryacl));
423 result = ns_config_get(maps, "dialup", &obj);
424 INSIST(result == ISC_R_SUCCESS);
425 if (cfg_obj_isboolean(obj)) {
426 if (cfg_obj_asboolean(obj))
427 dialup = dns_dialuptype_yes;
429 dialup = dns_dialuptype_no;
431 char *dialupstr = cfg_obj_asstring(obj);
432 if (strcasecmp(dialupstr, "notify") == 0)
433 dialup = dns_dialuptype_notify;
434 else if (strcasecmp(dialupstr, "notify-passive") == 0)
435 dialup = dns_dialuptype_notifypassive;
436 else if (strcasecmp(dialupstr, "refresh") == 0)
437 dialup = dns_dialuptype_refresh;
438 else if (strcasecmp(dialupstr, "passive") == 0)
439 dialup = dns_dialuptype_passive;
443 dns_zone_setdialup(zone, dialup);
446 result = ns_config_get(maps, "zone-statistics", &obj);
447 INSIST(result == ISC_R_SUCCESS);
448 RETERR(dns_zone_setstatistics(zone, cfg_obj_asboolean(obj)));
451 * Configure master functionality. This applies
452 * to primary masters (type "master") and slaves
453 * acting as masters (type "slave"), but not to stubs.
455 if (ztype != dns_zone_stub) {
457 result = ns_config_get(maps, "notify", &obj);
458 INSIST(result == ISC_R_SUCCESS);
459 if (cfg_obj_isboolean(obj)) {
460 if (cfg_obj_asboolean(obj))
461 notifytype = dns_notifytype_yes;
463 notifytype = dns_notifytype_no;
465 char *notifystr = cfg_obj_asstring(obj);
466 if (strcasecmp(notifystr, "explicit") == 0)
467 notifytype = dns_notifytype_explicit;
471 dns_zone_setnotifytype(zone, notifytype);
474 result = ns_config_get(maps, "also-notify", &obj);
475 if (result == ISC_R_SUCCESS) {
476 isc_sockaddr_t *addrs = NULL;
477 isc_uint32_t addrcount;
478 result = ns_config_getiplist(config, obj, 0, mctx,
480 if (result != ISC_R_SUCCESS)
482 result = dns_zone_setalsonotify(zone, addrs,
484 ns_config_putiplist(mctx, &addrs, addrcount);
485 if (result != ISC_R_SUCCESS)
488 RETERR(dns_zone_setalsonotify(zone, NULL, 0));
491 result = ns_config_get(maps, "notify-source", &obj);
492 INSIST(result == ISC_R_SUCCESS);
493 RETERR(dns_zone_setnotifysrc4(zone, cfg_obj_assockaddr(obj)));
494 ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
497 result = ns_config_get(maps, "notify-source-v6", &obj);
498 INSIST(result == ISC_R_SUCCESS);
499 RETERR(dns_zone_setnotifysrc6(zone, cfg_obj_assockaddr(obj)));
500 ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
502 RETERR(configure_zone_acl(zconfig, vconfig, config,
503 "allow-transfer", ac, zone,
505 dns_zone_clearxfracl));
508 result = ns_config_get(maps, "max-transfer-time-out", &obj);
509 INSIST(result == ISC_R_SUCCESS);
510 dns_zone_setmaxxfrout(zone, cfg_obj_asuint32(obj) * 60);
513 result = ns_config_get(maps, "max-transfer-idle-out", &obj);
514 INSIST(result == ISC_R_SUCCESS);
515 dns_zone_setidleout(zone, cfg_obj_asuint32(obj) * 60);
518 result = ns_config_get(maps, "max-journal-size", &obj);
519 INSIST(result == ISC_R_SUCCESS);
520 dns_zone_setjournalsize(zone, -1);
521 if (cfg_obj_isstring(obj)) {
522 const char *str = cfg_obj_asstring(obj);
523 INSIST(strcasecmp(str, "unlimited") == 0);
524 journal_size = ISC_UINT32_MAX / 2;
526 isc_resourcevalue_t value;
527 value = cfg_obj_asuint64(obj);
528 if (value > ISC_UINT32_MAX / 2) {
529 cfg_obj_log(obj, ns_g_lctx,
532 "%" ISC_PRINT_QUADFORMAT "d' "
537 journal_size = (isc_uint32_t)value;
539 dns_zone_setjournalsize(zone, journal_size);
542 result = ns_config_get(maps, "ixfr-from-differences", &obj);
543 INSIST(result == ISC_R_SUCCESS);
544 dns_zone_setoption(zone, DNS_ZONEOPT_IXFRFROMDIFFS,
545 cfg_obj_asboolean(obj));
547 checknames(ztype, maps, &obj);
549 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
552 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
553 fail = check = ISC_TRUE;
554 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
555 fail = check = ISC_FALSE;
558 dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMES, check);
559 dns_zone_setoption(zone, DNS_ZONEOPT_CHECKNAMESFAIL, fail);
563 * Configure update-related options. These apply to
564 * primary masters only.
566 if (ztype == dns_zone_master) {
567 dns_acl_t *updateacl;
568 RETERR(configure_zone_acl(zconfig, vconfig, config,
569 "allow-update", ac, zone,
570 dns_zone_setupdateacl,
571 dns_zone_clearupdateacl));
573 updateacl = dns_zone_getupdateacl(zone);
574 if (updateacl != NULL && dns_acl_isinsecure(updateacl))
575 isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY,
576 NS_LOGMODULE_SERVER, ISC_LOG_WARNING,
577 "zone '%s' allows updates by IP "
578 "address, which is insecure",
581 RETERR(configure_zone_ssutable(zoptions, zone));
584 result = ns_config_get(maps, "sig-validity-interval", &obj);
585 INSIST(result == ISC_R_SUCCESS);
586 dns_zone_setsigvalidityinterval(zone,
587 cfg_obj_asuint32(obj) * 86400);
590 result = ns_config_get(maps, "key-directory", &obj);
591 if (result == ISC_R_SUCCESS) {
592 filename = cfg_obj_asstring(obj);
593 if (!isc_file_isabsolute(filename)) {
594 cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
595 "key-directory '%s' "
596 "is not absolute", filename);
597 return (ISC_R_FAILURE);
599 RETERR(dns_zone_setkeydirectory(zone, filename));
602 } else if (ztype == dns_zone_slave) {
603 RETERR(configure_zone_acl(zconfig, vconfig, config,
604 "allow-update-forwarding", ac, zone,
605 dns_zone_setforwardacl,
606 dns_zone_clearforwardacl));
610 * Configure slave functionality.
616 result = cfg_map_get(zoptions, "masters", &obj);
620 RETERR(ns_config_getipandkeylist(config, obj, mctx,
623 result = dns_zone_setmasterswithkeys(zone, addrs,
625 ns_config_putipandkeylist(mctx, &addrs, &keynames,
628 result = dns_zone_setmasters(zone, NULL, 0);
634 result = ns_config_get(maps, "multi-master", &obj);
635 INSIST(result == ISC_R_SUCCESS);
636 multi = cfg_obj_asboolean(obj);
638 dns_zone_setoption(zone, DNS_ZONEOPT_MULTIMASTER, multi);
641 result = ns_config_get(maps, "max-transfer-time-in", &obj);
642 INSIST(result == ISC_R_SUCCESS);
643 dns_zone_setmaxxfrin(zone, cfg_obj_asuint32(obj) * 60);
646 result = ns_config_get(maps, "max-transfer-idle-in", &obj);
647 INSIST(result == ISC_R_SUCCESS);
648 dns_zone_setidlein(zone, cfg_obj_asuint32(obj) * 60);
651 result = ns_config_get(maps, "max-refresh-time", &obj);
652 INSIST(result == ISC_R_SUCCESS);
653 dns_zone_setmaxrefreshtime(zone, cfg_obj_asuint32(obj));
656 result = ns_config_get(maps, "min-refresh-time", &obj);
657 INSIST(result == ISC_R_SUCCESS);
658 dns_zone_setminrefreshtime(zone, cfg_obj_asuint32(obj));
661 result = ns_config_get(maps, "max-retry-time", &obj);
662 INSIST(result == ISC_R_SUCCESS);
663 dns_zone_setmaxretrytime(zone, cfg_obj_asuint32(obj));
666 result = ns_config_get(maps, "min-retry-time", &obj);
667 INSIST(result == ISC_R_SUCCESS);
668 dns_zone_setminretrytime(zone, cfg_obj_asuint32(obj));
671 result = ns_config_get(maps, "transfer-source", &obj);
672 INSIST(result == ISC_R_SUCCESS);
673 RETERR(dns_zone_setxfrsource4(zone, cfg_obj_assockaddr(obj)));
674 ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
677 result = ns_config_get(maps, "transfer-source-v6", &obj);
678 INSIST(result == ISC_R_SUCCESS);
679 RETERR(dns_zone_setxfrsource6(zone, cfg_obj_assockaddr(obj)));
680 ns_add_reserved_dispatch(ns_g_server, cfg_obj_assockaddr(obj));
683 result = ns_config_get(maps, "alt-transfer-source", &obj);
684 INSIST(result == ISC_R_SUCCESS);
685 RETERR(dns_zone_setaltxfrsource4(zone, cfg_obj_assockaddr(obj)));
688 result = ns_config_get(maps, "alt-transfer-source-v6", &obj);
689 INSIST(result == ISC_R_SUCCESS);
690 RETERR(dns_zone_setaltxfrsource6(zone, cfg_obj_assockaddr(obj)));
693 (void)ns_config_get(maps, "use-alt-transfer-source", &obj);
696 * Default off when views are in use otherwise
697 * on for BIND 8 compatibility.
699 view = dns_zone_getview(zone);
700 if (view != NULL && strcmp(view->name, "_default") == 0)
705 alt = cfg_obj_asboolean(obj);
706 dns_zone_setoption(zone, DNS_ZONEOPT_USEALTXFRSRC, alt);
714 return (ISC_R_SUCCESS);
718 ns_zone_reusable(dns_zone_t *zone, cfg_obj_t *zconfig) {
719 cfg_obj_t *zoptions = NULL;
720 cfg_obj_t *obj = NULL;
721 const char *cfilename;
722 const char *zfilename;
724 zoptions = cfg_tuple_get(zconfig, "options");
726 if (zonetype_fromconfig(zoptions) != dns_zone_gettype(zone))
730 (void)cfg_map_get(zoptions, "file", &obj);
732 cfilename = cfg_obj_asstring(obj);
735 zfilename = dns_zone_getfile(zone);
736 if (!((cfilename == NULL && zfilename == NULL) ||
737 (cfilename != NULL && zfilename != NULL &&
738 strcmp(cfilename, zfilename) == 0)))