/contrib/bind9/lib/bind9/check.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 2810 lines · 2392 code · 269 blank · 149 comment · 823 complexity · 403e0d46707d40b5f2209939c560fcaf MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * Copyright (C) 2004-2012 Internet Systems Consortium, Inc. ("ISC")
  3. * Copyright (C) 2001-2003 Internet Software Consortium.
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15. * PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /* $Id$ */
  18. /*! \file */
  19. #include <config.h>
  20. #include <stdlib.h>
  21. #include <isc/base64.h>
  22. #include <isc/buffer.h>
  23. #include <isc/log.h>
  24. #include <isc/mem.h>
  25. #include <isc/netaddr.h>
  26. #include <isc/parseint.h>
  27. #include <isc/region.h>
  28. #include <isc/result.h>
  29. #include <isc/sockaddr.h>
  30. #include <isc/string.h>
  31. #include <isc/symtab.h>
  32. #include <isc/util.h>
  33. #include <dns/acl.h>
  34. #include <dns/fixedname.h>
  35. #include <dns/rdataclass.h>
  36. #include <dns/rdatatype.h>
  37. #include <dns/secalg.h>
  38. #include <dst/dst.h>
  39. #include <isccfg/aclconf.h>
  40. #include <isccfg/cfg.h>
  41. #include <bind9/check.h>
  42. static void
  43. freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
  44. UNUSED(type);
  45. UNUSED(value);
  46. isc_mem_free(userarg, key);
  47. }
  48. static isc_result_t
  49. check_orderent(const cfg_obj_t *ent, isc_log_t *logctx) {
  50. isc_result_t result = ISC_R_SUCCESS;
  51. isc_result_t tresult;
  52. isc_textregion_t r;
  53. dns_fixedname_t fixed;
  54. const cfg_obj_t *obj;
  55. dns_rdataclass_t rdclass;
  56. dns_rdatatype_t rdtype;
  57. isc_buffer_t b;
  58. const char *str;
  59. dns_fixedname_init(&fixed);
  60. obj = cfg_tuple_get(ent, "class");
  61. if (cfg_obj_isstring(obj)) {
  62. DE_CONST(cfg_obj_asstring(obj), r.base);
  63. r.length = strlen(r.base);
  64. tresult = dns_rdataclass_fromtext(&rdclass, &r);
  65. if (tresult != ISC_R_SUCCESS) {
  66. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  67. "rrset-order: invalid class '%s'",
  68. r.base);
  69. result = ISC_R_FAILURE;
  70. }
  71. }
  72. obj = cfg_tuple_get(ent, "type");
  73. if (cfg_obj_isstring(obj)) {
  74. DE_CONST(cfg_obj_asstring(obj), r.base);
  75. r.length = strlen(r.base);
  76. tresult = dns_rdatatype_fromtext(&rdtype, &r);
  77. if (tresult != ISC_R_SUCCESS) {
  78. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  79. "rrset-order: invalid type '%s'",
  80. r.base);
  81. result = ISC_R_FAILURE;
  82. }
  83. }
  84. obj = cfg_tuple_get(ent, "name");
  85. if (cfg_obj_isstring(obj)) {
  86. str = cfg_obj_asstring(obj);
  87. isc_buffer_init(&b, str, strlen(str));
  88. isc_buffer_add(&b, strlen(str));
  89. tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
  90. dns_rootname, 0, NULL);
  91. if (tresult != ISC_R_SUCCESS) {
  92. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  93. "rrset-order: invalid name '%s'", str);
  94. result = ISC_R_FAILURE;
  95. }
  96. }
  97. obj = cfg_tuple_get(ent, "order");
  98. if (!cfg_obj_isstring(obj) ||
  99. strcasecmp("order", cfg_obj_asstring(obj)) != 0) {
  100. cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
  101. "rrset-order: keyword 'order' missing");
  102. result = ISC_R_FAILURE;
  103. }
  104. obj = cfg_tuple_get(ent, "ordering");
  105. if (!cfg_obj_isstring(obj)) {
  106. cfg_obj_log(ent, logctx, ISC_LOG_ERROR,
  107. "rrset-order: missing ordering");
  108. result = ISC_R_FAILURE;
  109. } else if (strcasecmp(cfg_obj_asstring(obj), "fixed") == 0) {
  110. #if !DNS_RDATASET_FIXED
  111. cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
  112. "rrset-order: order 'fixed' was disabled at "
  113. "compilation time");
  114. #endif
  115. } else if (strcasecmp(cfg_obj_asstring(obj), "random") != 0 &&
  116. strcasecmp(cfg_obj_asstring(obj), "cyclic") != 0) {
  117. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  118. "rrset-order: invalid order '%s'",
  119. cfg_obj_asstring(obj));
  120. result = ISC_R_FAILURE;
  121. }
  122. return (result);
  123. }
  124. static isc_result_t
  125. check_order(const cfg_obj_t *options, isc_log_t *logctx) {
  126. isc_result_t result = ISC_R_SUCCESS;
  127. isc_result_t tresult;
  128. const cfg_listelt_t *element;
  129. const cfg_obj_t *obj = NULL;
  130. if (cfg_map_get(options, "rrset-order", &obj) != ISC_R_SUCCESS)
  131. return (result);
  132. for (element = cfg_list_first(obj);
  133. element != NULL;
  134. element = cfg_list_next(element))
  135. {
  136. tresult = check_orderent(cfg_listelt_value(element), logctx);
  137. if (tresult != ISC_R_SUCCESS)
  138. result = tresult;
  139. }
  140. return (result);
  141. }
  142. static isc_result_t
  143. check_dual_stack(const cfg_obj_t *options, isc_log_t *logctx) {
  144. const cfg_listelt_t *element;
  145. const cfg_obj_t *alternates = NULL;
  146. const cfg_obj_t *value;
  147. const cfg_obj_t *obj;
  148. const char *str;
  149. dns_fixedname_t fixed;
  150. dns_name_t *name;
  151. isc_buffer_t buffer;
  152. isc_result_t result = ISC_R_SUCCESS;
  153. isc_result_t tresult;
  154. (void)cfg_map_get(options, "dual-stack-servers", &alternates);
  155. if (alternates == NULL)
  156. return (ISC_R_SUCCESS);
  157. obj = cfg_tuple_get(alternates, "port");
  158. if (cfg_obj_isuint32(obj)) {
  159. isc_uint32_t val = cfg_obj_asuint32(obj);
  160. if (val > ISC_UINT16_MAX) {
  161. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  162. "port '%u' out of range", val);
  163. result = ISC_R_FAILURE;
  164. }
  165. }
  166. obj = cfg_tuple_get(alternates, "addresses");
  167. for (element = cfg_list_first(obj);
  168. element != NULL;
  169. element = cfg_list_next(element)) {
  170. value = cfg_listelt_value(element);
  171. if (cfg_obj_issockaddr(value))
  172. continue;
  173. obj = cfg_tuple_get(value, "name");
  174. str = cfg_obj_asstring(obj);
  175. isc_buffer_init(&buffer, str, strlen(str));
  176. isc_buffer_add(&buffer, strlen(str));
  177. dns_fixedname_init(&fixed);
  178. name = dns_fixedname_name(&fixed);
  179. tresult = dns_name_fromtext(name, &buffer, dns_rootname,
  180. 0, NULL);
  181. if (tresult != ISC_R_SUCCESS) {
  182. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  183. "bad name '%s'", str);
  184. result = ISC_R_FAILURE;
  185. }
  186. obj = cfg_tuple_get(value, "port");
  187. if (cfg_obj_isuint32(obj)) {
  188. isc_uint32_t val = cfg_obj_asuint32(obj);
  189. if (val > ISC_UINT16_MAX) {
  190. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  191. "port '%u' out of range", val);
  192. result = ISC_R_FAILURE;
  193. }
  194. }
  195. }
  196. return (result);
  197. }
  198. static isc_result_t
  199. check_forward(const cfg_obj_t *options, const cfg_obj_t *global,
  200. isc_log_t *logctx)
  201. {
  202. const cfg_obj_t *forward = NULL;
  203. const cfg_obj_t *forwarders = NULL;
  204. (void)cfg_map_get(options, "forward", &forward);
  205. (void)cfg_map_get(options, "forwarders", &forwarders);
  206. if (forwarders != NULL && global != NULL) {
  207. const char *file = cfg_obj_file(global);
  208. unsigned int line = cfg_obj_line(global);
  209. cfg_obj_log(forwarders, logctx, ISC_LOG_ERROR,
  210. "forwarders declared in root zone and "
  211. "in general configuration: %s:%u",
  212. file, line);
  213. return (ISC_R_FAILURE);
  214. }
  215. if (forward != NULL && forwarders == NULL) {
  216. cfg_obj_log(forward, logctx, ISC_LOG_ERROR,
  217. "no matching 'forwarders' statement");
  218. return (ISC_R_FAILURE);
  219. }
  220. return (ISC_R_SUCCESS);
  221. }
  222. static isc_result_t
  223. disabled_algorithms(const cfg_obj_t *disabled, isc_log_t *logctx) {
  224. isc_result_t result = ISC_R_SUCCESS;
  225. isc_result_t tresult;
  226. const cfg_listelt_t *element;
  227. const char *str;
  228. isc_buffer_t b;
  229. dns_fixedname_t fixed;
  230. dns_name_t *name;
  231. const cfg_obj_t *obj;
  232. dns_fixedname_init(&fixed);
  233. name = dns_fixedname_name(&fixed);
  234. obj = cfg_tuple_get(disabled, "name");
  235. str = cfg_obj_asstring(obj);
  236. isc_buffer_init(&b, str, strlen(str));
  237. isc_buffer_add(&b, strlen(str));
  238. tresult = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
  239. if (tresult != ISC_R_SUCCESS) {
  240. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  241. "bad domain name '%s'", str);
  242. result = tresult;
  243. }
  244. obj = cfg_tuple_get(disabled, "algorithms");
  245. for (element = cfg_list_first(obj);
  246. element != NULL;
  247. element = cfg_list_next(element))
  248. {
  249. isc_textregion_t r;
  250. dns_secalg_t alg;
  251. isc_result_t tresult;
  252. DE_CONST(cfg_obj_asstring(cfg_listelt_value(element)), r.base);
  253. r.length = strlen(r.base);
  254. tresult = dns_secalg_fromtext(&alg, &r);
  255. if (tresult != ISC_R_SUCCESS) {
  256. isc_uint8_t ui;
  257. result = isc_parse_uint8(&ui, r.base, 10);
  258. }
  259. if (tresult != ISC_R_SUCCESS) {
  260. cfg_obj_log(cfg_listelt_value(element), logctx,
  261. ISC_LOG_ERROR, "invalid algorithm '%s'",
  262. r.base);
  263. result = tresult;
  264. }
  265. }
  266. return (result);
  267. }
  268. static isc_result_t
  269. nameexist(const cfg_obj_t *obj, const char *name, int value,
  270. isc_symtab_t *symtab, const char *fmt, isc_log_t *logctx,
  271. isc_mem_t *mctx)
  272. {
  273. char *key;
  274. const char *file;
  275. unsigned int line;
  276. isc_result_t result;
  277. isc_symvalue_t symvalue;
  278. key = isc_mem_strdup(mctx, name);
  279. if (key == NULL)
  280. return (ISC_R_NOMEMORY);
  281. symvalue.as_cpointer = obj;
  282. result = isc_symtab_define(symtab, key, value, symvalue,
  283. isc_symexists_reject);
  284. if (result == ISC_R_EXISTS) {
  285. RUNTIME_CHECK(isc_symtab_lookup(symtab, key, value,
  286. &symvalue) == ISC_R_SUCCESS);
  287. file = cfg_obj_file(symvalue.as_cpointer);
  288. line = cfg_obj_line(symvalue.as_cpointer);
  289. if (file == NULL)
  290. file = "<unknown file>";
  291. cfg_obj_log(obj, logctx, ISC_LOG_ERROR, fmt, key, file, line);
  292. isc_mem_free(mctx, key);
  293. result = ISC_R_EXISTS;
  294. } else if (result != ISC_R_SUCCESS) {
  295. isc_mem_free(mctx, key);
  296. }
  297. return (result);
  298. }
  299. static isc_result_t
  300. mustbesecure(const cfg_obj_t *secure, isc_symtab_t *symtab, isc_log_t *logctx,
  301. isc_mem_t *mctx)
  302. {
  303. const cfg_obj_t *obj;
  304. char namebuf[DNS_NAME_FORMATSIZE];
  305. const char *str;
  306. dns_fixedname_t fixed;
  307. dns_name_t *name;
  308. isc_buffer_t b;
  309. isc_result_t result = ISC_R_SUCCESS;
  310. dns_fixedname_init(&fixed);
  311. name = dns_fixedname_name(&fixed);
  312. obj = cfg_tuple_get(secure, "name");
  313. str = cfg_obj_asstring(obj);
  314. isc_buffer_init(&b, str, strlen(str));
  315. isc_buffer_add(&b, strlen(str));
  316. result = dns_name_fromtext(name, &b, dns_rootname, 0, NULL);
  317. if (result != ISC_R_SUCCESS) {
  318. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  319. "bad domain name '%s'", str);
  320. } else {
  321. dns_name_format(name, namebuf, sizeof(namebuf));
  322. result = nameexist(secure, namebuf, 1, symtab,
  323. "dnssec-must-be-secure '%s': already "
  324. "exists previous definition: %s:%u",
  325. logctx, mctx);
  326. }
  327. return (result);
  328. }
  329. static isc_result_t
  330. checkacl(const char *aclname, cfg_aclconfctx_t *actx, const cfg_obj_t *zconfig,
  331. const cfg_obj_t *voptions, const cfg_obj_t *config,
  332. isc_log_t *logctx, isc_mem_t *mctx)
  333. {
  334. isc_result_t result;
  335. const cfg_obj_t *aclobj = NULL;
  336. const cfg_obj_t *options;
  337. dns_acl_t *acl = NULL;
  338. if (zconfig != NULL) {
  339. options = cfg_tuple_get(zconfig, "options");
  340. cfg_map_get(options, aclname, &aclobj);
  341. }
  342. if (voptions != NULL && aclobj == NULL)
  343. cfg_map_get(voptions, aclname, &aclobj);
  344. if (config != NULL && aclobj == NULL) {
  345. options = NULL;
  346. cfg_map_get(config, "options", &options);
  347. if (options != NULL)
  348. cfg_map_get(options, aclname, &aclobj);
  349. }
  350. if (aclobj == NULL)
  351. return (ISC_R_SUCCESS);
  352. result = cfg_acl_fromconfig(aclobj, config, logctx,
  353. actx, mctx, 0, &acl);
  354. if (acl != NULL)
  355. dns_acl_detach(&acl);
  356. return (result);
  357. }
  358. static isc_result_t
  359. check_viewacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
  360. const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx)
  361. {
  362. isc_result_t result = ISC_R_SUCCESS, tresult;
  363. int i = 0;
  364. static const char *acls[] = { "allow-query", "allow-query-on",
  365. "allow-query-cache", "allow-query-cache-on",
  366. "blackhole", "match-clients", "match-destinations",
  367. "sortlist", "filter-aaaa", NULL };
  368. while (acls[i] != NULL) {
  369. tresult = checkacl(acls[i++], actx, NULL, voptions, config,
  370. logctx, mctx);
  371. if (tresult != ISC_R_SUCCESS)
  372. result = tresult;
  373. }
  374. return (result);
  375. }
  376. static const unsigned char zeros[16];
  377. static isc_result_t
  378. check_dns64(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
  379. const cfg_obj_t *config, isc_log_t *logctx, isc_mem_t *mctx)
  380. {
  381. isc_result_t result = ISC_R_SUCCESS;
  382. const cfg_obj_t *dns64 = NULL;
  383. const cfg_obj_t *options;
  384. const cfg_listelt_t *element;
  385. const cfg_obj_t *map, *obj;
  386. isc_netaddr_t na, sa;
  387. unsigned int prefixlen;
  388. int nbytes;
  389. int i;
  390. static const char *acls[] = { "clients", "exclude", "mapped", NULL};
  391. if (voptions != NULL)
  392. cfg_map_get(voptions, "dns64", &dns64);
  393. if (config != NULL && dns64 == NULL) {
  394. options = NULL;
  395. cfg_map_get(config, "options", &options);
  396. if (options != NULL)
  397. cfg_map_get(options, "dns64", &dns64);
  398. }
  399. if (dns64 == NULL)
  400. return (ISC_R_SUCCESS);
  401. for (element = cfg_list_first(dns64);
  402. element != NULL;
  403. element = cfg_list_next(element))
  404. {
  405. map = cfg_listelt_value(element);
  406. obj = cfg_map_getname(map);
  407. cfg_obj_asnetprefix(obj, &na, &prefixlen);
  408. if (na.family != AF_INET6) {
  409. cfg_obj_log(map, logctx, ISC_LOG_ERROR,
  410. "dns64 requires a IPv6 prefix");
  411. result = ISC_R_FAILURE;
  412. continue;
  413. }
  414. if (prefixlen != 32 && prefixlen != 40 && prefixlen != 48 &&
  415. prefixlen != 56 && prefixlen != 64 && prefixlen != 96) {
  416. cfg_obj_log(map, logctx, ISC_LOG_ERROR,
  417. "bad prefix length %u [32/40/48/56/64/96]",
  418. prefixlen);
  419. result = ISC_R_FAILURE;
  420. continue;
  421. }
  422. for (i = 0; acls[i] != NULL; i++) {
  423. obj = NULL;
  424. (void)cfg_map_get(map, acls[i], &obj);
  425. if (obj != NULL) {
  426. dns_acl_t *acl = NULL;
  427. isc_result_t tresult;
  428. tresult = cfg_acl_fromconfig(obj, config,
  429. logctx, actx,
  430. mctx, 0, &acl);
  431. if (acl != NULL)
  432. dns_acl_detach(&acl);
  433. if (tresult != ISC_R_SUCCESS)
  434. result = tresult;
  435. }
  436. }
  437. obj = NULL;
  438. (void)cfg_map_get(map, "suffix", &obj);
  439. if (obj != NULL) {
  440. isc_netaddr_fromsockaddr(&sa, cfg_obj_assockaddr(obj));
  441. if (sa.family != AF_INET6) {
  442. cfg_obj_log(map, logctx, ISC_LOG_ERROR,
  443. "dns64 requires a IPv6 suffix");
  444. result = ISC_R_FAILURE;
  445. continue;
  446. }
  447. nbytes = prefixlen / 8 + 4;
  448. if (prefixlen >= 32 && prefixlen <= 64)
  449. nbytes++;
  450. if (memcmp(sa.type.in6.s6_addr, zeros, nbytes) != 0) {
  451. char netaddrbuf[ISC_NETADDR_FORMATSIZE];
  452. isc_netaddr_format(&sa, netaddrbuf,
  453. sizeof(netaddrbuf));
  454. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  455. "bad suffix '%s' leading "
  456. "%u octets not zeros",
  457. netaddrbuf, nbytes);
  458. result = ISC_R_FAILURE;
  459. }
  460. }
  461. }
  462. return (result);
  463. }
  464. /*
  465. * Check allow-recursion and allow-recursion-on acls, and also log a
  466. * warning if they're inconsistent with the "recursion" option.
  467. */
  468. static isc_result_t
  469. check_recursionacls(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
  470. const char *viewname, const cfg_obj_t *config,
  471. isc_log_t *logctx, isc_mem_t *mctx)
  472. {
  473. const cfg_obj_t *options, *aclobj, *obj = NULL;
  474. dns_acl_t *acl = NULL;
  475. isc_result_t result = ISC_R_SUCCESS, tresult;
  476. isc_boolean_t recursion;
  477. const char *forview = " for view ";
  478. int i = 0;
  479. static const char *acls[] = { "allow-recursion", "allow-recursion-on",
  480. NULL };
  481. if (voptions != NULL)
  482. cfg_map_get(voptions, "recursion", &obj);
  483. if (obj == NULL && config != NULL) {
  484. options = NULL;
  485. cfg_map_get(config, "options", &options);
  486. if (options != NULL)
  487. cfg_map_get(options, "recursion", &obj);
  488. }
  489. if (obj == NULL)
  490. recursion = ISC_TRUE;
  491. else
  492. recursion = cfg_obj_asboolean(obj);
  493. if (viewname == NULL) {
  494. viewname = "";
  495. forview = "";
  496. }
  497. for (i = 0; acls[i] != NULL; i++) {
  498. aclobj = options = NULL;
  499. acl = NULL;
  500. if (voptions != NULL)
  501. cfg_map_get(voptions, acls[i], &aclobj);
  502. if (config != NULL && aclobj == NULL) {
  503. options = NULL;
  504. cfg_map_get(config, "options", &options);
  505. if (options != NULL)
  506. cfg_map_get(options, acls[i], &aclobj);
  507. }
  508. if (aclobj == NULL)
  509. continue;
  510. tresult = cfg_acl_fromconfig(aclobj, config, logctx,
  511. actx, mctx, 0, &acl);
  512. if (tresult != ISC_R_SUCCESS)
  513. result = tresult;
  514. if (acl == NULL)
  515. continue;
  516. if (recursion == ISC_FALSE && !dns_acl_isnone(acl)) {
  517. cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING,
  518. "both \"recursion no;\" and "
  519. "\"%s\" active%s%s",
  520. acls[i], forview, viewname);
  521. }
  522. if (acl != NULL)
  523. dns_acl_detach(&acl);
  524. }
  525. return (result);
  526. }
  527. static isc_result_t
  528. check_filteraaaa(cfg_aclconfctx_t *actx, const cfg_obj_t *voptions,
  529. const char *viewname, const cfg_obj_t *config,
  530. isc_log_t *logctx, isc_mem_t *mctx)
  531. {
  532. const cfg_obj_t *options, *aclobj, *obj = NULL;
  533. dns_acl_t *acl = NULL;
  534. isc_result_t result = ISC_R_SUCCESS, tresult;
  535. dns_v4_aaaa_t filter;
  536. const char *forview = " for view ";
  537. if (voptions != NULL)
  538. cfg_map_get(voptions, "filter-aaaa-on-v4", &obj);
  539. if (obj == NULL && config != NULL) {
  540. options = NULL;
  541. cfg_map_get(config, "options", &options);
  542. if (options != NULL)
  543. cfg_map_get(options, "filter-aaaa-on-v4", &obj);
  544. }
  545. if (obj == NULL)
  546. filter = dns_v4_aaaa_ok; /* default */
  547. else if (cfg_obj_isboolean(obj))
  548. filter = cfg_obj_asboolean(obj) ? dns_v4_aaaa_filter :
  549. dns_v4_aaaa_ok;
  550. else
  551. filter = dns_v4_aaaa_break_dnssec; /* break-dnssec */
  552. if (viewname == NULL) {
  553. viewname = "";
  554. forview = "";
  555. }
  556. aclobj = options = NULL;
  557. acl = NULL;
  558. if (voptions != NULL)
  559. cfg_map_get(voptions, "filter-aaaa", &aclobj);
  560. if (config != NULL && aclobj == NULL) {
  561. options = NULL;
  562. cfg_map_get(config, "options", &options);
  563. if (options != NULL)
  564. cfg_map_get(options, "filter-aaaa", &aclobj);
  565. }
  566. if (aclobj == NULL)
  567. return (result);
  568. tresult = cfg_acl_fromconfig(aclobj, config, logctx,
  569. actx, mctx, 0, &acl);
  570. if (tresult != ISC_R_SUCCESS) {
  571. result = tresult;
  572. } else if (filter != dns_v4_aaaa_ok && dns_acl_isnone(acl)) {
  573. cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING,
  574. "both \"filter-aaaa-on-v4 %s;\" and "
  575. "\"filter-aaaa\" is 'none;'%s%s",
  576. filter == dns_v4_aaaa_break_dnssec ?
  577. "break-dnssec" : "yes", forview, viewname);
  578. result = ISC_R_FAILURE;
  579. } else if (filter == dns_v4_aaaa_ok && !dns_acl_isnone(acl)) {
  580. cfg_obj_log(aclobj, logctx, ISC_LOG_WARNING,
  581. "both \"filter-aaaa-on-v4 no;\" and "
  582. "\"filter-aaaa\" is set%s%s", forview, viewname);
  583. result = ISC_R_FAILURE;
  584. }
  585. if (acl != NULL)
  586. dns_acl_detach(&acl);
  587. return (result);
  588. }
  589. typedef struct {
  590. const char *name;
  591. unsigned int scale;
  592. unsigned int max;
  593. } intervaltable;
  594. typedef enum {
  595. optlevel_config,
  596. optlevel_options,
  597. optlevel_view,
  598. optlevel_zone
  599. } optlevel_t;
  600. static isc_result_t
  601. check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx,
  602. optlevel_t optlevel)
  603. {
  604. isc_result_t result = ISC_R_SUCCESS;
  605. isc_result_t tresult;
  606. unsigned int i;
  607. const cfg_obj_t *obj = NULL;
  608. const cfg_obj_t *resignobj = NULL;
  609. const cfg_listelt_t *element;
  610. isc_symtab_t *symtab = NULL;
  611. dns_fixedname_t fixed;
  612. const char *str;
  613. dns_name_t *name;
  614. isc_buffer_t b;
  615. static intervaltable intervals[] = {
  616. { "cleaning-interval", 60, 28 * 24 * 60 }, /* 28 days */
  617. { "heartbeat-interval", 60, 28 * 24 * 60 }, /* 28 days */
  618. { "interface-interval", 60, 28 * 24 * 60 }, /* 28 days */
  619. { "max-transfer-idle-in", 60, 28 * 24 * 60 }, /* 28 days */
  620. { "max-transfer-idle-out", 60, 28 * 24 * 60 }, /* 28 days */
  621. { "max-transfer-time-in", 60, 28 * 24 * 60 }, /* 28 days */
  622. { "max-transfer-time-out", 60, 28 * 24 * 60 }, /* 28 days */
  623. { "statistics-interval", 60, 28 * 24 * 60 }, /* 28 days */
  624. };
  625. static const char *server_contact[] = {
  626. "empty-server", "empty-contact",
  627. "dns64-server", "dns64-contact",
  628. NULL
  629. };
  630. /*
  631. * Check that fields specified in units of time other than seconds
  632. * have reasonable values.
  633. */
  634. for (i = 0; i < sizeof(intervals) / sizeof(intervals[0]); i++) {
  635. isc_uint32_t val;
  636. obj = NULL;
  637. (void)cfg_map_get(options, intervals[i].name, &obj);
  638. if (obj == NULL)
  639. continue;
  640. val = cfg_obj_asuint32(obj);
  641. if (val > intervals[i].max) {
  642. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  643. "%s '%u' is out of range (0..%u)",
  644. intervals[i].name, val,
  645. intervals[i].max);
  646. result = ISC_R_RANGE;
  647. } else if (val > (ISC_UINT32_MAX / intervals[i].scale)) {
  648. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  649. "%s '%d' is out of range",
  650. intervals[i].name, val);
  651. result = ISC_R_RANGE;
  652. }
  653. }
  654. obj = NULL;
  655. cfg_map_get(options, "sig-validity-interval", &obj);
  656. if (obj != NULL) {
  657. isc_uint32_t validity, resign = 0;
  658. validity = cfg_obj_asuint32(cfg_tuple_get(obj, "validity"));
  659. resignobj = cfg_tuple_get(obj, "re-sign");
  660. if (!cfg_obj_isvoid(resignobj))
  661. resign = cfg_obj_asuint32(resignobj);
  662. if (validity > 3660 || validity == 0) { /* 10 years */
  663. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  664. "%s '%u' is out of range (1..3660)",
  665. "sig-validity-interval", validity);
  666. result = ISC_R_RANGE;
  667. }
  668. if (!cfg_obj_isvoid(resignobj)) {
  669. if (resign > 3660 || resign == 0) { /* 10 years */
  670. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  671. "%s '%u' is out of range (1..3660)",
  672. "sig-validity-interval (re-sign)",
  673. validity);
  674. result = ISC_R_RANGE;
  675. } else if ((validity > 7 && validity < resign) ||
  676. (validity <= 7 && validity * 24 < resign)) {
  677. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  678. "validity interval (%u days) "
  679. "less than re-signing interval "
  680. "(%u %s)", validity, resign,
  681. (validity > 7) ? "days" : "hours");
  682. result = ISC_R_RANGE;
  683. }
  684. }
  685. }
  686. obj = NULL;
  687. (void)cfg_map_get(options, "preferred-glue", &obj);
  688. if (obj != NULL) {
  689. const char *str;
  690. str = cfg_obj_asstring(obj);
  691. if (strcasecmp(str, "a") != 0 &&
  692. strcasecmp(str, "aaaa") != 0 &&
  693. strcasecmp(str, "none") != 0)
  694. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  695. "preferred-glue unexpected value '%s'",
  696. str);
  697. }
  698. obj = NULL;
  699. (void)cfg_map_get(options, "root-delegation-only", &obj);
  700. if (obj != NULL) {
  701. if (!cfg_obj_isvoid(obj)) {
  702. const cfg_listelt_t *element;
  703. const cfg_obj_t *exclude;
  704. const char *str;
  705. dns_fixedname_t fixed;
  706. dns_name_t *name;
  707. isc_buffer_t b;
  708. dns_fixedname_init(&fixed);
  709. name = dns_fixedname_name(&fixed);
  710. for (element = cfg_list_first(obj);
  711. element != NULL;
  712. element = cfg_list_next(element)) {
  713. exclude = cfg_listelt_value(element);
  714. str = cfg_obj_asstring(exclude);
  715. isc_buffer_init(&b, str, strlen(str));
  716. isc_buffer_add(&b, strlen(str));
  717. tresult = dns_name_fromtext(name, &b,
  718. dns_rootname,
  719. 0, NULL);
  720. if (tresult != ISC_R_SUCCESS) {
  721. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  722. "bad domain name '%s'",
  723. str);
  724. result = tresult;
  725. }
  726. }
  727. }
  728. }
  729. /*
  730. * Set supported DNSSEC algorithms.
  731. */
  732. obj = NULL;
  733. (void)cfg_map_get(options, "disable-algorithms", &obj);
  734. if (obj != NULL) {
  735. for (element = cfg_list_first(obj);
  736. element != NULL;
  737. element = cfg_list_next(element))
  738. {
  739. obj = cfg_listelt_value(element);
  740. tresult = disabled_algorithms(obj, logctx);
  741. if (tresult != ISC_R_SUCCESS)
  742. result = tresult;
  743. }
  744. }
  745. dns_fixedname_init(&fixed);
  746. name = dns_fixedname_name(&fixed);
  747. /*
  748. * Check the DLV zone name.
  749. */
  750. obj = NULL;
  751. (void)cfg_map_get(options, "dnssec-lookaside", &obj);
  752. if (obj != NULL) {
  753. tresult = isc_symtab_create(mctx, 100, freekey, mctx,
  754. ISC_FALSE, &symtab);
  755. if (tresult != ISC_R_SUCCESS)
  756. result = tresult;
  757. for (element = cfg_list_first(obj);
  758. element != NULL;
  759. element = cfg_list_next(element))
  760. {
  761. const char *dlv;
  762. const cfg_obj_t *dlvobj, *anchor;
  763. obj = cfg_listelt_value(element);
  764. anchor = cfg_tuple_get(obj, "trust-anchor");
  765. dlvobj = cfg_tuple_get(obj, "domain");
  766. dlv = cfg_obj_asstring(dlvobj);
  767. /*
  768. * If domain is "auto" or "no" and trust anchor
  769. * is missing, skip remaining tests
  770. */
  771. if (cfg_obj_isvoid(anchor)) {
  772. if (!strcasecmp(dlv, "no") ||
  773. !strcasecmp(dlv, "auto"))
  774. continue;
  775. }
  776. isc_buffer_init(&b, dlv, strlen(dlv));
  777. isc_buffer_add(&b, strlen(dlv));
  778. tresult = dns_name_fromtext(name, &b, dns_rootname,
  779. 0, NULL);
  780. if (tresult != ISC_R_SUCCESS) {
  781. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  782. "bad domain name '%s'", dlv);
  783. result = tresult;
  784. continue;
  785. }
  786. if (symtab != NULL) {
  787. tresult = nameexist(obj, dlv, 1, symtab,
  788. "dnssec-lookaside '%s': "
  789. "already exists previous "
  790. "definition: %s:%u",
  791. logctx, mctx);
  792. if (tresult != ISC_R_SUCCESS &&
  793. result == ISC_R_SUCCESS)
  794. result = tresult;
  795. }
  796. /*
  797. * XXXMPA to be removed when multiple lookaside
  798. * namespaces are supported.
  799. */
  800. if (!dns_name_equal(dns_rootname, name)) {
  801. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  802. "dnssec-lookaside '%s': "
  803. "non-root not yet supported", dlv);
  804. if (result == ISC_R_SUCCESS)
  805. result = ISC_R_FAILURE;
  806. }
  807. if (!cfg_obj_isvoid(anchor)) {
  808. dlv = cfg_obj_asstring(anchor);
  809. isc_buffer_init(&b, dlv, strlen(dlv));
  810. isc_buffer_add(&b, strlen(dlv));
  811. tresult = dns_name_fromtext(name, &b,
  812. dns_rootname,
  813. DNS_NAME_DOWNCASE,
  814. NULL);
  815. if (tresult != ISC_R_SUCCESS) {
  816. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  817. "bad domain name '%s'",
  818. dlv);
  819. if (result == ISC_R_SUCCESS)
  820. result = tresult;
  821. }
  822. } else {
  823. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  824. "dnssec-lookaside requires "
  825. "either 'auto' or 'no', or a "
  826. "domain and trust anchor");
  827. if (result == ISC_R_SUCCESS)
  828. result = ISC_R_FAILURE;
  829. }
  830. }
  831. if (symtab != NULL)
  832. isc_symtab_destroy(&symtab);
  833. }
  834. /*
  835. * Check auto-dnssec at the view/options level
  836. */
  837. obj = NULL;
  838. (void)cfg_map_get(options, "auto-dnssec", &obj);
  839. if (obj != NULL) {
  840. const char *arg = cfg_obj_asstring(obj);
  841. if (optlevel != optlevel_zone && strcasecmp(arg, "off") != 0) {
  842. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  843. "auto-dnssec may only be activated at the "
  844. "zone level");
  845. result = ISC_R_FAILURE;
  846. }
  847. }
  848. /*
  849. * Check dnssec-must-be-secure.
  850. */
  851. obj = NULL;
  852. (void)cfg_map_get(options, "dnssec-must-be-secure", &obj);
  853. if (obj != NULL) {
  854. isc_symtab_t *symtab = NULL;
  855. tresult = isc_symtab_create(mctx, 100, freekey, mctx,
  856. ISC_FALSE, &symtab);
  857. if (tresult != ISC_R_SUCCESS)
  858. result = tresult;
  859. for (element = cfg_list_first(obj);
  860. element != NULL;
  861. element = cfg_list_next(element))
  862. {
  863. obj = cfg_listelt_value(element);
  864. tresult = mustbesecure(obj, symtab, logctx, mctx);
  865. if (tresult != ISC_R_SUCCESS)
  866. result = tresult;
  867. }
  868. if (symtab != NULL)
  869. isc_symtab_destroy(&symtab);
  870. }
  871. /*
  872. * Check server/contacts for syntactic validity.
  873. */
  874. for (i= 0; server_contact[i] != NULL; i++) {
  875. obj = NULL;
  876. (void)cfg_map_get(options, server_contact[i], &obj);
  877. if (obj != NULL) {
  878. str = cfg_obj_asstring(obj);
  879. isc_buffer_init(&b, str, strlen(str));
  880. isc_buffer_add(&b, strlen(str));
  881. tresult = dns_name_fromtext(dns_fixedname_name(&fixed),
  882. &b, dns_rootname, 0, NULL);
  883. if (tresult != ISC_R_SUCCESS) {
  884. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  885. "%s: invalid name '%s'",
  886. server_contact[i], str);
  887. result = ISC_R_FAILURE;
  888. }
  889. }
  890. }
  891. /*
  892. * Check empty zone configuration.
  893. */
  894. obj = NULL;
  895. (void)cfg_map_get(options, "disable-empty-zone", &obj);
  896. for (element = cfg_list_first(obj);
  897. element != NULL;
  898. element = cfg_list_next(element))
  899. {
  900. obj = cfg_listelt_value(element);
  901. str = cfg_obj_asstring(obj);
  902. isc_buffer_init(&b, str, strlen(str));
  903. isc_buffer_add(&b, strlen(str));
  904. tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
  905. dns_rootname, 0, NULL);
  906. if (tresult != ISC_R_SUCCESS) {
  907. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  908. "disable-empty-zone: invalid name '%s'",
  909. str);
  910. result = ISC_R_FAILURE;
  911. }
  912. }
  913. /*
  914. * Check that server-id is not too long.
  915. * 1024 bytes should be big enough.
  916. */
  917. obj = NULL;
  918. (void)cfg_map_get(options, "server-id", &obj);
  919. if (obj != NULL && cfg_obj_isstring(obj) &&
  920. strlen(cfg_obj_asstring(obj)) > 1024U) {
  921. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  922. "'server-id' too big (>1024 bytes)");
  923. result = ISC_R_FAILURE;
  924. }
  925. return (result);
  926. }
  927. static isc_result_t
  928. get_masters_def(const cfg_obj_t *cctx, const char *name, const cfg_obj_t **ret) {
  929. isc_result_t result;
  930. const cfg_obj_t *masters = NULL;
  931. const cfg_listelt_t *elt;
  932. result = cfg_map_get(cctx, "masters", &masters);
  933. if (result != ISC_R_SUCCESS)
  934. return (result);
  935. for (elt = cfg_list_first(masters);
  936. elt != NULL;
  937. elt = cfg_list_next(elt)) {
  938. const cfg_obj_t *list;
  939. const char *listname;
  940. list = cfg_listelt_value(elt);
  941. listname = cfg_obj_asstring(cfg_tuple_get(list, "name"));
  942. if (strcasecmp(listname, name) == 0) {
  943. *ret = list;
  944. return (ISC_R_SUCCESS);
  945. }
  946. }
  947. return (ISC_R_NOTFOUND);
  948. }
  949. static isc_result_t
  950. validate_masters(const cfg_obj_t *obj, const cfg_obj_t *config,
  951. isc_uint32_t *countp, isc_log_t *logctx, isc_mem_t *mctx)
  952. {
  953. isc_result_t result = ISC_R_SUCCESS;
  954. isc_result_t tresult;
  955. isc_uint32_t count = 0;
  956. isc_symtab_t *symtab = NULL;
  957. isc_symvalue_t symvalue;
  958. const cfg_listelt_t *element;
  959. const cfg_listelt_t **stack = NULL;
  960. isc_uint32_t stackcount = 0, pushed = 0;
  961. const cfg_obj_t *list;
  962. REQUIRE(countp != NULL);
  963. result = isc_symtab_create(mctx, 100, NULL, NULL, ISC_FALSE, &symtab);
  964. if (result != ISC_R_SUCCESS) {
  965. *countp = count;
  966. return (result);
  967. }
  968. newlist:
  969. list = cfg_tuple_get(obj, "addresses");
  970. element = cfg_list_first(list);
  971. resume:
  972. for ( ;
  973. element != NULL;
  974. element = cfg_list_next(element))
  975. {
  976. const char *listname;
  977. const cfg_obj_t *addr;
  978. const cfg_obj_t *key;
  979. addr = cfg_tuple_get(cfg_listelt_value(element),
  980. "masterselement");
  981. key = cfg_tuple_get(cfg_listelt_value(element), "key");
  982. if (cfg_obj_issockaddr(addr)) {
  983. count++;
  984. continue;
  985. }
  986. if (!cfg_obj_isvoid(key)) {
  987. cfg_obj_log(key, logctx, ISC_LOG_ERROR,
  988. "unexpected token '%s'",
  989. cfg_obj_asstring(key));
  990. if (result == ISC_R_SUCCESS)
  991. result = ISC_R_FAILURE;
  992. }
  993. listname = cfg_obj_asstring(addr);
  994. symvalue.as_cpointer = addr;
  995. tresult = isc_symtab_define(symtab, listname, 1, symvalue,
  996. isc_symexists_reject);
  997. if (tresult == ISC_R_EXISTS)
  998. continue;
  999. tresult = get_masters_def(config, listname, &obj);
  1000. if (tresult != ISC_R_SUCCESS) {
  1001. if (result == ISC_R_SUCCESS)
  1002. result = tresult;
  1003. cfg_obj_log(addr, logctx, ISC_LOG_ERROR,
  1004. "unable to find masters list '%s'",
  1005. listname);
  1006. continue;
  1007. }
  1008. /* Grow stack? */
  1009. if (stackcount == pushed) {
  1010. void * new;
  1011. isc_uint32_t newlen = stackcount + 16;
  1012. size_t newsize, oldsize;
  1013. newsize = newlen * sizeof(*stack);
  1014. oldsize = stackcount * sizeof(*stack);
  1015. new = isc_mem_get(mctx, newsize);
  1016. if (new == NULL)
  1017. goto cleanup;
  1018. if (stackcount != 0) {
  1019. void *ptr;
  1020. DE_CONST(stack, ptr);
  1021. memcpy(new, stack, oldsize);
  1022. isc_mem_put(mctx, ptr, oldsize);
  1023. }
  1024. stack = new;
  1025. stackcount = newlen;
  1026. }
  1027. stack[pushed++] = cfg_list_next(element);
  1028. goto newlist;
  1029. }
  1030. if (pushed != 0) {
  1031. element = stack[--pushed];
  1032. goto resume;
  1033. }
  1034. cleanup:
  1035. if (stack != NULL) {
  1036. void *ptr;
  1037. DE_CONST(stack, ptr);
  1038. isc_mem_put(mctx, ptr, stackcount * sizeof(*stack));
  1039. }
  1040. isc_symtab_destroy(&symtab);
  1041. *countp = count;
  1042. return (result);
  1043. }
  1044. static isc_result_t
  1045. check_update_policy(const cfg_obj_t *policy, isc_log_t *logctx) {
  1046. isc_result_t result = ISC_R_SUCCESS;
  1047. isc_result_t tresult;
  1048. const cfg_listelt_t *element;
  1049. const cfg_listelt_t *element2;
  1050. dns_fixedname_t fixed;
  1051. const char *str;
  1052. isc_buffer_t b;
  1053. /* Check for "update-policy local;" */
  1054. if (cfg_obj_isstring(policy) &&
  1055. strcmp("local", cfg_obj_asstring(policy)) == 0)
  1056. return (ISC_R_SUCCESS);
  1057. /* Now check the grant policy */
  1058. for (element = cfg_list_first(policy);
  1059. element != NULL;
  1060. element = cfg_list_next(element))
  1061. {
  1062. const cfg_obj_t *stmt = cfg_listelt_value(element);
  1063. const cfg_obj_t *identity = cfg_tuple_get(stmt, "identity");
  1064. const cfg_obj_t *matchtype = cfg_tuple_get(stmt, "matchtype");
  1065. const cfg_obj_t *dname = cfg_tuple_get(stmt, "name");
  1066. const cfg_obj_t *typelist = cfg_tuple_get(stmt, "types");
  1067. dns_fixedname_init(&fixed);
  1068. str = cfg_obj_asstring(identity);
  1069. isc_buffer_init(&b, str, strlen(str));
  1070. isc_buffer_add(&b, strlen(str));
  1071. tresult = dns_name_fromtext(dns_fixedname_name(&fixed), &b,
  1072. dns_rootname, 0, NULL);
  1073. if (tresult != ISC_R_SUCCESS) {
  1074. cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
  1075. "'%s' is not a valid name", str);
  1076. result = tresult;
  1077. }
  1078. if (tresult == ISC_R_SUCCESS &&
  1079. strcasecmp(cfg_obj_asstring(matchtype), "zonesub") != 0) {
  1080. dns_fixedname_init(&fixed);
  1081. str = cfg_obj_asstring(dname);
  1082. isc_buffer_init(&b, str, strlen(str));
  1083. isc_buffer_add(&b, strlen(str));
  1084. tresult = dns_name_fromtext(dns_fixedname_name(&fixed),
  1085. &b, dns_rootname, 0, NULL);
  1086. if (tresult != ISC_R_SUCCESS) {
  1087. cfg_obj_log(dname, logctx, ISC_LOG_ERROR,
  1088. "'%s' is not a valid name", str);
  1089. result = tresult;
  1090. }
  1091. }
  1092. if (tresult == ISC_R_SUCCESS &&
  1093. strcasecmp(cfg_obj_asstring(matchtype), "wildcard") == 0 &&
  1094. !dns_name_iswildcard(dns_fixedname_name(&fixed))) {
  1095. cfg_obj_log(identity, logctx, ISC_LOG_ERROR,
  1096. "'%s' is not a wildcard", str);
  1097. result = ISC_R_FAILURE;
  1098. }
  1099. for (element2 = cfg_list_first(typelist);
  1100. element2 != NULL;
  1101. element2 = cfg_list_next(element2))
  1102. {
  1103. const cfg_obj_t *typeobj;
  1104. isc_textregion_t r;
  1105. dns_rdatatype_t type;
  1106. typeobj = cfg_listelt_value(element2);
  1107. DE_CONST(cfg_obj_asstring(typeobj), r.base);
  1108. r.length = strlen(r.base);
  1109. tresult = dns_rdatatype_fromtext(&type, &r);
  1110. if (tresult != ISC_R_SUCCESS) {
  1111. cfg_obj_log(typeobj, logctx, ISC_LOG_ERROR,
  1112. "'%s' is not a valid type", r.base);
  1113. result = tresult;
  1114. }
  1115. }
  1116. }
  1117. return (result);
  1118. }
  1119. #define MASTERZONE 1
  1120. #define SLAVEZONE 2
  1121. #define STUBZONE 4
  1122. #define HINTZONE 8
  1123. #define FORWARDZONE 16
  1124. #define DELEGATIONZONE 32
  1125. #define STATICSTUBZONE 64
  1126. #define CHECKACL 128
  1127. typedef struct {
  1128. const char *name;
  1129. int allowed;
  1130. } optionstable;
  1131. static isc_result_t
  1132. check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions,
  1133. const cfg_obj_t *config, isc_symtab_t *symtab,
  1134. dns_rdataclass_t defclass, cfg_aclconfctx_t *actx,
  1135. isc_log_t *logctx, isc_mem_t *mctx)
  1136. {
  1137. const char *znamestr;
  1138. const char *typestr;
  1139. unsigned int ztype;
  1140. const cfg_obj_t *zoptions;
  1141. const cfg_obj_t *obj = NULL;
  1142. isc_result_t result = ISC_R_SUCCESS;
  1143. isc_result_t tresult;
  1144. unsigned int i;
  1145. dns_rdataclass_t zclass;
  1146. dns_fixedname_t fixedname;
  1147. dns_name_t *zname = NULL;
  1148. isc_buffer_t b;
  1149. isc_boolean_t root = ISC_FALSE;
  1150. const cfg_listelt_t *element;
  1151. static optionstable options[] = {
  1152. { "allow-query", MASTERZONE | SLAVEZONE | STUBZONE | CHECKACL |
  1153. STATICSTUBZONE },
  1154. { "allow-notify", SLAVEZONE | CHECKACL },
  1155. { "allow-transfer", MASTERZONE | SLAVEZONE | CHECKACL },
  1156. { "notify", MASTERZONE | SLAVEZONE },
  1157. { "also-notify", MASTERZONE | SLAVEZONE },
  1158. { "dialup", MASTERZONE | SLAVEZONE | STUBZONE },
  1159. { "delegation-only", HINTZONE | STUBZONE | DELEGATIONZONE },
  1160. { "forward", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE },
  1161. { "forwarders", MASTERZONE | SLAVEZONE | STUBZONE | FORWARDZONE },
  1162. { "maintain-ixfr-base", MASTERZONE | SLAVEZONE },
  1163. { "max-ixfr-log-size", MASTERZONE | SLAVEZONE },
  1164. { "notify-source", MASTERZONE | SLAVEZONE },
  1165. { "notify-source-v6", MASTERZONE | SLAVEZONE },
  1166. { "transfer-source", SLAVEZONE | STUBZONE },
  1167. { "transfer-source-v6", SLAVEZONE | STUBZONE },
  1168. { "max-transfer-time-in", SLAVEZONE | STUBZONE },
  1169. { "max-transfer-time-out", MASTERZONE | SLAVEZONE },
  1170. { "max-transfer-idle-in", SLAVEZONE | STUBZONE },
  1171. { "max-transfer-idle-out", MASTERZONE | SLAVEZONE },
  1172. { "max-retry-time", SLAVEZONE | STUBZONE },
  1173. { "min-retry-time", SLAVEZONE | STUBZONE },
  1174. { "max-refresh-time", SLAVEZONE | STUBZONE },
  1175. { "min-refresh-time", SLAVEZONE | STUBZONE },
  1176. { "dnssec-secure-to-insecure", MASTERZONE },
  1177. { "sig-validity-interval", MASTERZONE },
  1178. { "sig-re-signing-interval", MASTERZONE },
  1179. { "sig-signing-nodes", MASTERZONE },
  1180. { "sig-signing-type", MASTERZONE },
  1181. { "sig-signing-signatures", MASTERZONE },
  1182. { "zone-statistics", MASTERZONE | SLAVEZONE | STUBZONE |
  1183. STATICSTUBZONE},
  1184. { "allow-update", MASTERZONE | CHECKACL },
  1185. { "allow-update-forwarding", SLAVEZONE | CHECKACL },
  1186. { "file", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
  1187. { "journal", MASTERZONE | SLAVEZONE },
  1188. { "ixfr-base", MASTERZONE | SLAVEZONE },
  1189. { "ixfr-tmp-file", MASTERZONE | SLAVEZONE },
  1190. { "masters", SLAVEZONE | STUBZONE },
  1191. { "pubkey", MASTERZONE | SLAVEZONE | STUBZONE },
  1192. { "update-policy", MASTERZONE },
  1193. { "database", MASTERZONE | SLAVEZONE | STUBZONE },
  1194. { "key-directory", MASTERZONE },
  1195. { "check-wildcard", MASTERZONE },
  1196. { "check-mx", MASTERZONE },
  1197. { "check-dup-records", MASTERZONE },
  1198. { "integrity-check", MASTERZONE },
  1199. { "check-mx-cname", MASTERZONE },
  1200. { "check-srv-cname", MASTERZONE },
  1201. { "masterfile-format", MASTERZONE | SLAVEZONE | STUBZONE | HINTZONE },
  1202. { "update-check-ksk", MASTERZONE },
  1203. { "dnssec-dnskey-kskonly", MASTERZONE },
  1204. { "auto-dnssec", MASTERZONE },
  1205. { "try-tcp-refresh", SLAVEZONE },
  1206. { "server-addresses", STATICSTUBZONE },
  1207. { "server-names", STATICSTUBZONE },
  1208. };
  1209. static optionstable dialups[] = {
  1210. { "notify", MASTERZONE | SLAVEZONE },
  1211. { "notify-passive", SLAVEZONE },
  1212. { "refresh", SLAVEZONE | STUBZONE },
  1213. { "passive", SLAVEZONE | STUBZONE },
  1214. };
  1215. znamestr = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
  1216. zoptions = cfg_tuple_get(zconfig, "options");
  1217. obj = NULL;
  1218. (void)cfg_map_get(zoptions, "type", &obj);
  1219. if (obj == NULL) {
  1220. cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
  1221. "zone '%s': type not present", znamestr);
  1222. return (ISC_R_FAILURE);
  1223. }
  1224. typestr = cfg_obj_asstring(obj);
  1225. if (strcasecmp(typestr, "master") == 0)
  1226. ztype = MASTERZONE;
  1227. else if (strcasecmp(typestr, "slave") == 0)
  1228. ztype = SLAVEZONE;
  1229. else if (strcasecmp(typestr, "stub") == 0)
  1230. ztype = STUBZONE;
  1231. else if (strcasecmp(typestr, "static-stub") == 0)
  1232. ztype = STATICSTUBZONE;
  1233. else if (strcasecmp(typestr, "forward") == 0)
  1234. ztype = FORWARDZONE;
  1235. else if (strcasecmp(typestr, "hint") == 0)
  1236. ztype = HINTZONE;
  1237. else if (strcasecmp(typestr, "delegation-only") == 0)
  1238. ztype = DELEGATIONZONE;
  1239. else {
  1240. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1241. "zone '%s': invalid type %s",
  1242. znamestr, typestr);
  1243. return (ISC_R_FAILURE);
  1244. }
  1245. obj = cfg_tuple_get(zconfig, "class");
  1246. if (cfg_obj_isstring(obj)) {
  1247. isc_textregion_t r;
  1248. DE_CONST(cfg_obj_asstring(obj), r.base);
  1249. r.length = strlen(r.base);
  1250. result = dns_rdataclass_fromtext(&zclass, &r);
  1251. if (result != ISC_R_SUCCESS) {
  1252. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1253. "zone '%s': invalid class %s",
  1254. znamestr, r.base);
  1255. return (ISC_R_FAILURE);
  1256. }
  1257. if (zclass != defclass) {
  1258. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1259. "zone '%s': class '%s' does not "
  1260. "match view/default class",
  1261. znamestr, r.base);
  1262. return (ISC_R_FAILURE);
  1263. }
  1264. }
  1265. /*
  1266. * Look for an already existing zone.
  1267. * We need to make this canonical as isc_symtab_define()
  1268. * deals with strings.
  1269. */
  1270. dns_fixedname_init(&fixedname);
  1271. isc_buffer_init(&b, znamestr, strlen(znamestr));
  1272. isc_buffer_add(&b, strlen(znamestr));
  1273. tresult = dns_name_fromtext(dns_fixedname_name(&fixedname), &b,
  1274. dns_rootname, DNS_NAME_DOWNCASE, NULL);
  1275. if (tresult != ISC_R_SUCCESS) {
  1276. cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
  1277. "zone '%s': is not a valid name", znamestr);
  1278. result = ISC_R_FAILURE;
  1279. } else {
  1280. char namebuf[DNS_NAME_FORMATSIZE];
  1281. zname = dns_fixedname_name(&fixedname);
  1282. dns_name_format(zname, namebuf, sizeof(namebuf));
  1283. tresult = nameexist(zconfig, namebuf, ztype == HINTZONE ? 1 : 2,
  1284. symtab, "zone '%s': already exists "
  1285. "previous definition: %s:%u", logctx, mctx);
  1286. if (tresult != ISC_R_SUCCESS)
  1287. result = tresult;
  1288. if (dns_name_equal(zname, dns_rootname))
  1289. root = ISC_TRUE;
  1290. }
  1291. /*
  1292. * Look for inappropriate options for the given zone type.
  1293. * Check that ACLs expand correctly.
  1294. */
  1295. for (i = 0; i < sizeof(options) / sizeof(options[0]); i++) {
  1296. obj = NULL;
  1297. if ((options[i].allowed & ztype) == 0 &&
  1298. cfg_map_get(zoptions, options[i].name, &obj) ==
  1299. ISC_R_SUCCESS)
  1300. {
  1301. if (strcmp(options[i].name, "allow-update") != 0 ||
  1302. ztype != SLAVEZONE) {
  1303. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1304. "option '%s' is not allowed "
  1305. "in '%s' zone '%s'",
  1306. options[i].name, typestr,
  1307. znamestr);
  1308. result = ISC_R_FAILURE;
  1309. } else
  1310. cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
  1311. "option '%s' is not allowed "
  1312. "in '%s' zone '%s'",
  1313. options[i].name, typestr,
  1314. znamestr);
  1315. }
  1316. obj = NULL;
  1317. if ((options[i].allowed & ztype) != 0 &&
  1318. (options[i].allowed & CHECKACL) != 0) {
  1319. tresult = checkacl(options[i].name, actx, zconfig,
  1320. voptions, config, logctx, mctx);
  1321. if (tresult != ISC_R_SUCCESS)
  1322. result = tresult;
  1323. }
  1324. }
  1325. /*
  1326. * Slave & stub zones must have a "masters" field.
  1327. */
  1328. if (ztype == SLAVEZONE || ztype == STUBZONE) {
  1329. obj = NULL;
  1330. if (cfg_map_get(zoptions, "masters", &obj) != ISC_R_SUCCESS) {
  1331. cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
  1332. "zone '%s': missing 'masters' entry",
  1333. znamestr);
  1334. result = ISC_R_FAILURE;
  1335. } else {
  1336. isc_uint32_t count;
  1337. tresult = validate_masters(obj, config, &count,
  1338. logctx, mctx);
  1339. if (tresult != ISC_R_SUCCESS && result == ISC_R_SUCCESS)
  1340. result = tresult;
  1341. if (tresult == ISC_R_SUCCESS && count == 0) {
  1342. cfg_obj_log(zoptions, logctx, ISC_LOG_ERROR,
  1343. "zone '%s': empty 'masters' entry",
  1344. znamestr);
  1345. result = ISC_R_FAILURE;
  1346. }
  1347. }
  1348. }
  1349. /*
  1350. * Master zones can't have both "allow-update" and "update-policy".
  1351. */
  1352. if (ztype == MASTERZONE) {
  1353. isc_result_t res1, res2, res3;
  1354. const char *arg;
  1355. isc_boolean_t ddns;
  1356. obj = NULL;
  1357. res1 = cfg_map_get(zoptions, "allow-update", &obj);
  1358. obj = NULL;
  1359. res2 = cfg_map_get(zoptions, "update-policy", &obj);
  1360. if (res1 == ISC_R_SUCCESS && res2 == ISC_R_SUCCESS) {
  1361. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1362. "zone '%s': 'allow-update' is ignored "
  1363. "when 'update-policy' is present",
  1364. znamestr);
  1365. result = ISC_R_FAILURE;
  1366. } else if (res2 == ISC_R_SUCCESS &&
  1367. check_update_policy(obj, logctx) != ISC_R_SUCCESS)
  1368. result = ISC_R_FAILURE;
  1369. ddns = ISC_TF(res1 == ISC_R_SUCCESS || res2 == ISC_R_SUCCESS);
  1370. obj = NULL;
  1371. arg = "off";
  1372. res3 = cfg_map_get(zoptions, "auto-dnssec", &obj);
  1373. if (res3 == ISC_R_SUCCESS)
  1374. arg = cfg_obj_asstring(obj);
  1375. if (strcasecmp(arg, "off") != 0 && !ddns) {
  1376. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1377. "'auto-dnssec %s;' requires "
  1378. "dynamic DNS to be configured in the zone",
  1379. arg);
  1380. result = ISC_R_FAILURE;
  1381. }
  1382. if (strcasecmp(arg, "create") == 0) {
  1383. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1384. "'auto-dnssec create;' is not "
  1385. "yet implemented");
  1386. result = ISC_R_FAILURE;
  1387. }
  1388. obj = NULL;
  1389. res1 = cfg_map_get(zoptions, "sig-signing-type", &obj);
  1390. if (res1 == ISC_R_SUCCESS) {
  1391. isc_uint32_t type = cfg_obj_asuint32(obj);
  1392. if (type < 0xff00U || type > 0xffffU)
  1393. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1394. "sig-signing-type: %u out of "
  1395. "range [%u..%u]", type,
  1396. 0xff00U, 0xffffU);
  1397. result = ISC_R_FAILURE;
  1398. }
  1399. }
  1400. /*
  1401. * Check the excessively complicated "dialup" option.
  1402. */
  1403. if (ztype == MASTERZONE || ztype == SLAVEZONE || ztype == STUBZONE) {
  1404. const cfg_obj_t *dialup = NULL;
  1405. (void)cfg_map_get(zoptions, "dialup", &dialup);
  1406. if (dialup != NULL && cfg_obj_isstring(dialup)) {
  1407. const char *str = cfg_obj_asstring(dialup);
  1408. for (i = 0;
  1409. i < sizeof(dialups) / sizeof(dialups[0]);
  1410. i++)
  1411. {
  1412. if (strcasecmp(dialups[i].name, str) != 0)
  1413. continue;
  1414. if ((dialups[i].allowed & ztype) == 0) {
  1415. cfg_obj_log(obj, logctx,
  1416. ISC_LOG_ERROR,
  1417. "dialup type '%s' is not "
  1418. "allowed in '%s' "
  1419. "zone '%s'",
  1420. str, typestr, znamestr);
  1421. result = ISC_R_FAILURE;
  1422. }
  1423. break;
  1424. }
  1425. if (i == sizeof(dialups) / sizeof(dialups[0])) {
  1426. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1427. "invalid dialup type '%s' in zone "
  1428. "'%s'", str, znamestr);
  1429. result = ISC_R_FAILURE;
  1430. }
  1431. }
  1432. }
  1433. /*
  1434. * Check that forwarding is reasonable.
  1435. */
  1436. obj = NULL;
  1437. if (root) {
  1438. if (voptions != NULL)
  1439. (void)cfg_map_get(voptions, "forwarders", &obj);
  1440. if (obj == NULL) {
  1441. const cfg_obj_t *options = NULL;
  1442. (void)cfg_map_get(config, "options", &options);
  1443. if (options != NULL)
  1444. (void)cfg_map_get(options, "forwarders", &obj);
  1445. }
  1446. }
  1447. if (check_forward(zoptions, obj, logctx) != ISC_R_SUCCESS)
  1448. result = ISC_R_FAILURE;
  1449. /*
  1450. * Check validity of static stub server addresses.
  1451. */
  1452. obj = NULL;
  1453. (void)cfg_map_get(zoptions, "server-addresses", &obj);
  1454. if (ztype == STATICSTUBZONE && obj != NULL) {
  1455. for (element = cfg_list_first(obj);
  1456. element != NULL;
  1457. element = cfg_list_next(element))
  1458. {
  1459. isc_sockaddr_t sa;
  1460. isc_netaddr_t na;
  1461. obj = cfg_listelt_value(element);
  1462. sa = *cfg_obj_assockaddr(obj);
  1463. if (isc_sockaddr_getport(&sa) != 0) {
  1464. result = ISC_R_FAILURE;
  1465. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1466. "port is not configurable for "
  1467. "static stub server-addresses");
  1468. }
  1469. isc_netaddr_fromsockaddr(&na, &sa);
  1470. if (isc_netaddr_getzone(&na) != 0) {
  1471. result = ISC_R_FAILURE;
  1472. cfg_obj_log(obj, logctx, ISC_LOG_ERROR,
  1473. "scoped address is not allowed "
  1474. "for static stub "
  1475. "server-addresses");
  1476. }
  1477. }
  1478. }
  1479. /*
  1480. * Check validity of static stub server names.
  1481. */
  1482. obj = NULL;
  1483. (void)cfg_map_get(zoptions, "server-names", &obj);
  1484. if (zname != NULL && ztype == STATICSTUBZONE && obj != NULL) {
  1485. for (element = cfg_list_first(obj);
  1486. element != NULL;
  1487. element = cfg_list_next(element))
  1488. {
  1489. const char *snamestr;
  1490. dns_fixedname_t fixed_sname;
  1491. isc_buffer_t b2;
  1492. dns_name_t *sname;
  1493. obj = cfg_listelt_value(element);
  1494. snamestr = cfg_obj_asstring(obj);
  1495. dns_fixedname_init(&fixed_sname);
  1496. isc_buffer_init(&b2, snamestr, strlen(snamestr));
  1497. isc_buffer_add(&b2, strlen(snamestr));
  1498. sname = dns_fixedname_name(&fixed_sname);
  1499. tresult = dns_name_fromtext(sname, &b2, dns_rootname,
  1500. 0, NULL);
  1501. if (tresult != ISC_R_SUCCESS) {
  1502. cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
  1503. "server-name '%s' is not a valid "
  1504. "name", snamestr);
  1505. result = ISC_R_FAILURE;
  1506. } else if (dns_name_issubdomain(sname, zname)) {
  1507. cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
  1508. "server-name '%s' must not be a "
  1509. "subdomain of zone name '%s'",
  1510. snamestr, znamestr);
  1511. result = ISC_R_FAILURE;
  1512. }
  1513. }
  1514. }
  1515. /*
  1516. * Check various options.
  1517. */
  1518. tresult = check_options(zoptions, logctx, mctx, optlevel_zone);
  1519. if (tresult != ISC_R_SUCCESS)
  1520. result = tresult;
  1521. /*
  1522. * If the zone type is rbt/rbt64 then master/hint zones
  1523. * require file clauses.
  1524. */
  1525. obj = NULL;
  1526. tresult = cfg_map_get(zoptions, "database", &obj);
  1527. if (tresult == ISC_R_NOTFOUND ||
  1528. (tresult == ISC_R_SUCCESS &&
  1529. (strcmp("rbt", cfg_obj_asstring(obj)) == 0 ||
  1530. strcmp("rbt64", cfg_obj_asstring(obj)) == 0))) {
  1531. obj = NULL;
  1532. tresult = cfg_map_get(zoptions, "file", &obj);
  1533. if (tresult != ISC_R_SUCCESS &&
  1534. (ztype == MASTERZONE || ztype == HINTZONE)) {
  1535. cfg_obj_log(zconfig, logctx, ISC_LOG_ERROR,
  1536. "zone '%s': missing 'file' entry",
  1537. znamestr);
  1538. result = tresult;
  1539. }
  1540. }
  1541. return (result);
  1542. }
  1543. typedef struct keyalgorithms {
  1544. const char *name;
  1545. isc_uint16_t size;
  1546. } algorithmtable;
  1547. isc_result_t
  1548. bind9_check_key(const cfg_obj_t *key, isc_log_t *logctx) {
  1549. const cfg_obj_t *algobj = NULL;
  1550. const cfg_obj_t *secretobj = NULL;
  1551. const char *keyname = cfg_obj_asstring(cfg_map_getname(key));
  1552. const char *algorithm;
  1553. int i;
  1554. size_t len = 0;
  1555. isc_result_t result;
  1556. isc_buffer_t buf;
  1557. unsigned char secretbuf[1024];
  1558. static const algorithmtable algorithms[] = {
  1559. { "hmac-md5", 128 },
  1560. { "hmac-md5.sig-alg.reg.int", 0 },
  1561. { "hmac-md5.sig-alg.reg.int.", 0 },
  1562. { "hmac-sha1", 160 },
  1563. { "hmac-sha224", 224 },
  1564. { "hmac-sha256", 256 },
  1565. { "hmac-sha384", 384 },
  1566. { "hmac-sha512", 512 },
  1567. { NULL, 0 }
  1568. };
  1569. (void)cfg_map_get(key, "algorithm", &algobj);
  1570. (void)cfg_map_get(key, "secret", &secretobj);
  1571. if (secretobj == NULL || algobj == NULL) {
  1572. cfg_obj_log(key, logctx, ISC_LOG_ERROR,
  1573. "key '%s' must have both 'secret' and "
  1574. "'algorithm' defined",
  1575. keyname);
  1576. return (ISC_R_FAILURE);
  1577. }
  1578. isc_buffer_init(&buf, secretbuf, sizeof(secretbuf));
  1579. result = isc_base64_decodestring(cfg_obj_asstring(secretobj), &buf);
  1580. if (result != ISC_R_SUCCESS) {
  1581. cfg_obj_log(secretobj, logctx, ISC_LOG_ERROR,
  1582. "bad secret '%s'", isc_result_totext(result));
  1583. return (result);
  1584. }
  1585. algorithm = cfg_obj_asstring(algobj);
  1586. for (i = 0; algorithms[i].name != NULL; i++) {
  1587. len = strlen(algorithms[i].name);
  1588. if (strncasecmp(algorithms[i].name, algorithm, len) == 0 &&
  1589. (algorithm[len] == '\0' ||
  1590. (algorithms[i].size != 0 && algorithm[len] == '-')))
  1591. break;
  1592. }
  1593. if (algorithms[i].name == NULL) {
  1594. cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
  1595. "unknown algorithm '%s'", algorithm);
  1596. return (ISC_R_NOTFOUND);
  1597. }
  1598. if (algorithm[len] == '-') {
  1599. isc_uint16_t digestbits;
  1600. isc_result_t result;
  1601. result = isc_parse_uint16(&digestbits, algorithm + len + 1, 10);
  1602. if (result == ISC_R_SUCCESS || result == ISC_R_RANGE) {
  1603. if (result == ISC_R_RANGE ||
  1604. digestbits > algorithms[i].size) {
  1605. cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
  1606. "key '%s' digest-bits too large "
  1607. "[%u..%u]", keyname,
  1608. algorithms[i].size / 2,
  1609. algorithms[i].size);
  1610. return (ISC_R_RANGE);
  1611. }
  1612. if ((digestbits % 8) != 0) {
  1613. cfg_obj_log(algobj, logctx, ISC_LOG_ERROR,
  1614. "key '%s' digest-bits not multiple"
  1615. " of 8", keyname);
  1616. return (ISC_R_RANGE);
  1617. }
  1618. /*
  1619. * Recommended minima for hmac algorithms.
  1620. */
  1621. if ((di