/contrib/bind9/lib/isccfg/parser.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 2477 lines · 2199 code · 202 blank · 76 comment · 267 complexity · 65d7ffeb7b54f4c36192aab7889403d0 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) 2000-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 <isc/buffer.h>
  21. #include <isc/dir.h>
  22. #include <isc/formatcheck.h>
  23. #include <isc/lex.h>
  24. #include <isc/log.h>
  25. #include <isc/mem.h>
  26. #include <isc/net.h>
  27. #include <isc/netaddr.h>
  28. #include <isc/netscope.h>
  29. #include <isc/print.h>
  30. #include <isc/string.h>
  31. #include <isc/sockaddr.h>
  32. #include <isc/symtab.h>
  33. #include <isc/util.h>
  34. #include <isccfg/cfg.h>
  35. #include <isccfg/grammar.h>
  36. #include <isccfg/log.h>
  37. /* Shorthand */
  38. #define CAT CFG_LOGCATEGORY_CONFIG
  39. #define MOD CFG_LOGMODULE_PARSER
  40. #define MAP_SYM 1 /* Unique type for isc_symtab */
  41. #define TOKEN_STRING(pctx) (pctx->token.value.as_textregion.base)
  42. /* Check a return value. */
  43. #define CHECK(op) \
  44. do { result = (op); \
  45. if (result != ISC_R_SUCCESS) goto cleanup; \
  46. } while (0)
  47. /* Clean up a configuration object if non-NULL. */
  48. #define CLEANUP_OBJ(obj) \
  49. do { if ((obj) != NULL) cfg_obj_destroy(pctx, &(obj)); } while (0)
  50. /*
  51. * Forward declarations of static functions.
  52. */
  53. static void
  54. free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj);
  55. static isc_result_t
  56. parse_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret);
  57. static void
  58. print_list(cfg_printer_t *pctx, const cfg_obj_t *obj);
  59. static void
  60. free_list(cfg_parser_t *pctx, cfg_obj_t *obj);
  61. static isc_result_t
  62. create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp);
  63. static isc_result_t
  64. create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
  65. cfg_obj_t **ret);
  66. static void
  67. free_string(cfg_parser_t *pctx, cfg_obj_t *obj);
  68. static isc_result_t
  69. create_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **objp);
  70. static void
  71. free_map(cfg_parser_t *pctx, cfg_obj_t *obj);
  72. static isc_result_t
  73. parse_symtab_elt(cfg_parser_t *pctx, const char *name,
  74. cfg_type_t *elttype, isc_symtab_t *symtab,
  75. isc_boolean_t callback);
  76. static void
  77. free_noop(cfg_parser_t *pctx, cfg_obj_t *obj);
  78. static isc_result_t
  79. cfg_getstringtoken(cfg_parser_t *pctx);
  80. static void
  81. parser_complain(cfg_parser_t *pctx, isc_boolean_t is_warning,
  82. unsigned int flags, const char *format, va_list args);
  83. /*
  84. * Data representations. These correspond to members of the
  85. * "value" union in struct cfg_obj (except "void", which does
  86. * not need a union member).
  87. */
  88. cfg_rep_t cfg_rep_uint32 = { "uint32", free_noop };
  89. cfg_rep_t cfg_rep_uint64 = { "uint64", free_noop };
  90. cfg_rep_t cfg_rep_string = { "string", free_string };
  91. cfg_rep_t cfg_rep_boolean = { "boolean", free_noop };
  92. cfg_rep_t cfg_rep_map = { "map", free_map };
  93. cfg_rep_t cfg_rep_list = { "list", free_list };
  94. cfg_rep_t cfg_rep_tuple = { "tuple", free_tuple };
  95. cfg_rep_t cfg_rep_sockaddr = { "sockaddr", free_noop };
  96. cfg_rep_t cfg_rep_netprefix = { "netprefix", free_noop };
  97. cfg_rep_t cfg_rep_void = { "void", free_noop };
  98. /*
  99. * Configuration type definitions.
  100. */
  101. /*%
  102. * An implicit list. These are formed by clauses that occur multiple times.
  103. */
  104. static cfg_type_t cfg_type_implicitlist = {
  105. "implicitlist", NULL, print_list, NULL, &cfg_rep_list, NULL };
  106. /* Functions. */
  107. void
  108. cfg_print_obj(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  109. obj->type->print(pctx, obj);
  110. }
  111. void
  112. cfg_print_chars(cfg_printer_t *pctx, const char *text, int len) {
  113. pctx->f(pctx->closure, text, len);
  114. }
  115. static void
  116. print_open(cfg_printer_t *pctx) {
  117. cfg_print_chars(pctx, "{\n", 2);
  118. pctx->indent++;
  119. }
  120. static void
  121. print_indent(cfg_printer_t *pctx) {
  122. int indent = pctx->indent;
  123. while (indent > 0) {
  124. cfg_print_chars(pctx, "\t", 1);
  125. indent--;
  126. }
  127. }
  128. static void
  129. print_close(cfg_printer_t *pctx) {
  130. pctx->indent--;
  131. print_indent(pctx);
  132. cfg_print_chars(pctx, "}", 1);
  133. }
  134. isc_result_t
  135. cfg_parse_obj(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  136. isc_result_t result;
  137. INSIST(ret != NULL && *ret == NULL);
  138. result = type->parse(pctx, type, ret);
  139. if (result != ISC_R_SUCCESS)
  140. return (result);
  141. INSIST(*ret != NULL);
  142. return (ISC_R_SUCCESS);
  143. }
  144. void
  145. cfg_print(const cfg_obj_t *obj,
  146. void (*f)(void *closure, const char *text, int textlen),
  147. void *closure)
  148. {
  149. cfg_printer_t pctx;
  150. pctx.f = f;
  151. pctx.closure = closure;
  152. pctx.indent = 0;
  153. obj->type->print(&pctx, obj);
  154. }
  155. /* Tuples. */
  156. isc_result_t
  157. cfg_create_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  158. isc_result_t result;
  159. const cfg_tuplefielddef_t *fields = type->of;
  160. const cfg_tuplefielddef_t *f;
  161. cfg_obj_t *obj = NULL;
  162. unsigned int nfields = 0;
  163. int i;
  164. for (f = fields; f->name != NULL; f++)
  165. nfields++;
  166. CHECK(cfg_create_obj(pctx, type, &obj));
  167. obj->value.tuple = isc_mem_get(pctx->mctx,
  168. nfields * sizeof(cfg_obj_t *));
  169. if (obj->value.tuple == NULL) {
  170. result = ISC_R_NOMEMORY;
  171. goto cleanup;
  172. }
  173. for (f = fields, i = 0; f->name != NULL; f++, i++)
  174. obj->value.tuple[i] = NULL;
  175. *ret = obj;
  176. return (ISC_R_SUCCESS);
  177. cleanup:
  178. if (obj != NULL)
  179. isc_mem_put(pctx->mctx, obj, sizeof(*obj));
  180. return (result);
  181. }
  182. isc_result_t
  183. cfg_parse_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
  184. {
  185. isc_result_t result;
  186. const cfg_tuplefielddef_t *fields = type->of;
  187. const cfg_tuplefielddef_t *f;
  188. cfg_obj_t *obj = NULL;
  189. unsigned int i;
  190. CHECK(cfg_create_tuple(pctx, type, &obj));
  191. for (f = fields, i = 0; f->name != NULL; f++, i++)
  192. CHECK(cfg_parse_obj(pctx, f->type, &obj->value.tuple[i]));
  193. *ret = obj;
  194. return (ISC_R_SUCCESS);
  195. cleanup:
  196. CLEANUP_OBJ(obj);
  197. return (result);
  198. }
  199. void
  200. cfg_print_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  201. unsigned int i;
  202. const cfg_tuplefielddef_t *fields = obj->type->of;
  203. const cfg_tuplefielddef_t *f;
  204. isc_boolean_t need_space = ISC_FALSE;
  205. for (f = fields, i = 0; f->name != NULL; f++, i++) {
  206. const cfg_obj_t *fieldobj = obj->value.tuple[i];
  207. if (need_space)
  208. cfg_print_chars(pctx, " ", 1);
  209. cfg_print_obj(pctx, fieldobj);
  210. need_space = ISC_TF(fieldobj->type->print != cfg_print_void);
  211. }
  212. }
  213. void
  214. cfg_doc_tuple(cfg_printer_t *pctx, const cfg_type_t *type) {
  215. const cfg_tuplefielddef_t *fields = type->of;
  216. const cfg_tuplefielddef_t *f;
  217. isc_boolean_t need_space = ISC_FALSE;
  218. for (f = fields; f->name != NULL; f++) {
  219. if (need_space)
  220. cfg_print_chars(pctx, " ", 1);
  221. cfg_doc_obj(pctx, f->type);
  222. need_space = ISC_TF(f->type->print != cfg_print_void);
  223. }
  224. }
  225. static void
  226. free_tuple(cfg_parser_t *pctx, cfg_obj_t *obj) {
  227. unsigned int i;
  228. const cfg_tuplefielddef_t *fields = obj->type->of;
  229. const cfg_tuplefielddef_t *f;
  230. unsigned int nfields = 0;
  231. if (obj->value.tuple == NULL)
  232. return;
  233. for (f = fields, i = 0; f->name != NULL; f++, i++) {
  234. CLEANUP_OBJ(obj->value.tuple[i]);
  235. nfields++;
  236. }
  237. isc_mem_put(pctx->mctx, obj->value.tuple,
  238. nfields * sizeof(cfg_obj_t *));
  239. }
  240. isc_boolean_t
  241. cfg_obj_istuple(const cfg_obj_t *obj) {
  242. REQUIRE(obj != NULL);
  243. return (ISC_TF(obj->type->rep == &cfg_rep_tuple));
  244. }
  245. const cfg_obj_t *
  246. cfg_tuple_get(const cfg_obj_t *tupleobj, const char* name) {
  247. unsigned int i;
  248. const cfg_tuplefielddef_t *fields;
  249. const cfg_tuplefielddef_t *f;
  250. REQUIRE(tupleobj != NULL && tupleobj->type->rep == &cfg_rep_tuple);
  251. fields = tupleobj->type->of;
  252. for (f = fields, i = 0; f->name != NULL; f++, i++) {
  253. if (strcmp(f->name, name) == 0)
  254. return (tupleobj->value.tuple[i]);
  255. }
  256. INSIST(0);
  257. return (NULL);
  258. }
  259. isc_result_t
  260. cfg_parse_special(cfg_parser_t *pctx, int special) {
  261. isc_result_t result;
  262. CHECK(cfg_gettoken(pctx, 0));
  263. if (pctx->token.type == isc_tokentype_special &&
  264. pctx->token.value.as_char == special)
  265. return (ISC_R_SUCCESS);
  266. cfg_parser_error(pctx, CFG_LOG_NEAR, "'%c' expected", special);
  267. return (ISC_R_UNEXPECTEDTOKEN);
  268. cleanup:
  269. return (result);
  270. }
  271. /*
  272. * Parse a required semicolon. If it is not there, log
  273. * an error and increment the error count but continue
  274. * parsing. Since the next token is pushed back,
  275. * care must be taken to make sure it is eventually
  276. * consumed or an infinite loop may result.
  277. */
  278. static isc_result_t
  279. parse_semicolon(cfg_parser_t *pctx) {
  280. isc_result_t result;
  281. CHECK(cfg_gettoken(pctx, 0));
  282. if (pctx->token.type == isc_tokentype_special &&
  283. pctx->token.value.as_char == ';')
  284. return (ISC_R_SUCCESS);
  285. cfg_parser_error(pctx, CFG_LOG_BEFORE, "missing ';'");
  286. cfg_ungettoken(pctx);
  287. cleanup:
  288. return (result);
  289. }
  290. /*
  291. * Parse EOF, logging and returning an error if not there.
  292. */
  293. static isc_result_t
  294. parse_eof(cfg_parser_t *pctx) {
  295. isc_result_t result;
  296. CHECK(cfg_gettoken(pctx, 0));
  297. if (pctx->token.type == isc_tokentype_eof)
  298. return (ISC_R_SUCCESS);
  299. cfg_parser_error(pctx, CFG_LOG_NEAR, "syntax error");
  300. return (ISC_R_UNEXPECTEDTOKEN);
  301. cleanup:
  302. return (result);
  303. }
  304. /* A list of files, used internally for pctx->files. */
  305. static cfg_type_t cfg_type_filelist = {
  306. "filelist", NULL, print_list, NULL, &cfg_rep_list,
  307. &cfg_type_qstring
  308. };
  309. isc_result_t
  310. cfg_parser_create(isc_mem_t *mctx, isc_log_t *lctx, cfg_parser_t **ret) {
  311. isc_result_t result;
  312. cfg_parser_t *pctx;
  313. isc_lexspecials_t specials;
  314. REQUIRE(mctx != NULL);
  315. REQUIRE(ret != NULL && *ret == NULL);
  316. pctx = isc_mem_get(mctx, sizeof(*pctx));
  317. if (pctx == NULL)
  318. return (ISC_R_NOMEMORY);
  319. result = isc_refcount_init(&pctx->references, 1);
  320. if (result != ISC_R_SUCCESS) {
  321. isc_mem_put(mctx, pctx, sizeof(*pctx));
  322. return (result);
  323. }
  324. pctx->mctx = mctx;
  325. pctx->lctx = lctx;
  326. pctx->lexer = NULL;
  327. pctx->seen_eof = ISC_FALSE;
  328. pctx->ungotten = ISC_FALSE;
  329. pctx->errors = 0;
  330. pctx->warnings = 0;
  331. pctx->open_files = NULL;
  332. pctx->closed_files = NULL;
  333. pctx->line = 0;
  334. pctx->callback = NULL;
  335. pctx->callbackarg = NULL;
  336. pctx->token.type = isc_tokentype_unknown;
  337. pctx->flags = 0;
  338. memset(specials, 0, sizeof(specials));
  339. specials['{'] = 1;
  340. specials['}'] = 1;
  341. specials[';'] = 1;
  342. specials['/'] = 1;
  343. specials['"'] = 1;
  344. specials['!'] = 1;
  345. CHECK(isc_lex_create(pctx->mctx, 1024, &pctx->lexer));
  346. isc_lex_setspecials(pctx->lexer, specials);
  347. isc_lex_setcomments(pctx->lexer, (ISC_LEXCOMMENT_C |
  348. ISC_LEXCOMMENT_CPLUSPLUS |
  349. ISC_LEXCOMMENT_SHELL));
  350. CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->open_files));
  351. CHECK(cfg_create_list(pctx, &cfg_type_filelist, &pctx->closed_files));
  352. *ret = pctx;
  353. return (ISC_R_SUCCESS);
  354. cleanup:
  355. if (pctx->lexer != NULL)
  356. isc_lex_destroy(&pctx->lexer);
  357. CLEANUP_OBJ(pctx->open_files);
  358. CLEANUP_OBJ(pctx->closed_files);
  359. isc_mem_put(mctx, pctx, sizeof(*pctx));
  360. return (result);
  361. }
  362. static isc_result_t
  363. parser_openfile(cfg_parser_t *pctx, const char *filename) {
  364. isc_result_t result;
  365. cfg_listelt_t *elt = NULL;
  366. cfg_obj_t *stringobj = NULL;
  367. result = isc_lex_openfile(pctx->lexer, filename);
  368. if (result != ISC_R_SUCCESS) {
  369. cfg_parser_error(pctx, 0, "open: %s: %s",
  370. filename, isc_result_totext(result));
  371. goto cleanup;
  372. }
  373. CHECK(create_string(pctx, filename, &cfg_type_qstring, &stringobj));
  374. CHECK(create_listelt(pctx, &elt));
  375. elt->obj = stringobj;
  376. ISC_LIST_APPEND(pctx->open_files->value.list, elt, link);
  377. return (ISC_R_SUCCESS);
  378. cleanup:
  379. CLEANUP_OBJ(stringobj);
  380. return (result);
  381. }
  382. void
  383. cfg_parser_setcallback(cfg_parser_t *pctx,
  384. cfg_parsecallback_t callback,
  385. void *arg)
  386. {
  387. pctx->callback = callback;
  388. pctx->callbackarg = arg;
  389. }
  390. /*
  391. * Parse a configuration using a pctx where a lexer has already
  392. * been set up with a source.
  393. */
  394. static isc_result_t
  395. parse2(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  396. isc_result_t result;
  397. cfg_obj_t *obj = NULL;
  398. result = cfg_parse_obj(pctx, type, &obj);
  399. if (pctx->errors != 0) {
  400. /* Errors have been logged. */
  401. if (result == ISC_R_SUCCESS)
  402. result = ISC_R_FAILURE;
  403. goto cleanup;
  404. }
  405. if (result != ISC_R_SUCCESS) {
  406. /* Parsing failed but no errors have been logged. */
  407. cfg_parser_error(pctx, 0, "parsing failed");
  408. goto cleanup;
  409. }
  410. CHECK(parse_eof(pctx));
  411. *ret = obj;
  412. return (ISC_R_SUCCESS);
  413. cleanup:
  414. CLEANUP_OBJ(obj);
  415. return (result);
  416. }
  417. isc_result_t
  418. cfg_parse_file(cfg_parser_t *pctx, const char *filename,
  419. const cfg_type_t *type, cfg_obj_t **ret)
  420. {
  421. isc_result_t result;
  422. REQUIRE(filename != NULL);
  423. CHECK(parser_openfile(pctx, filename));
  424. CHECK(parse2(pctx, type, ret));
  425. cleanup:
  426. return (result);
  427. }
  428. isc_result_t
  429. cfg_parse_buffer(cfg_parser_t *pctx, isc_buffer_t *buffer,
  430. const cfg_type_t *type, cfg_obj_t **ret)
  431. {
  432. isc_result_t result;
  433. REQUIRE(buffer != NULL);
  434. CHECK(isc_lex_openbuffer(pctx->lexer, buffer));
  435. CHECK(parse2(pctx, type, ret));
  436. cleanup:
  437. return (result);
  438. }
  439. void
  440. cfg_parser_attach(cfg_parser_t *src, cfg_parser_t **dest) {
  441. REQUIRE(src != NULL);
  442. REQUIRE(dest != NULL && *dest == NULL);
  443. isc_refcount_increment(&src->references, NULL);
  444. *dest = src;
  445. }
  446. void
  447. cfg_parser_destroy(cfg_parser_t **pctxp) {
  448. cfg_parser_t *pctx = *pctxp;
  449. unsigned int refs;
  450. isc_refcount_decrement(&pctx->references, &refs);
  451. if (refs == 0) {
  452. isc_lex_destroy(&pctx->lexer);
  453. /*
  454. * Cleaning up open_files does not
  455. * close the files; that was already done
  456. * by closing the lexer.
  457. */
  458. CLEANUP_OBJ(pctx->open_files);
  459. CLEANUP_OBJ(pctx->closed_files);
  460. isc_mem_put(pctx->mctx, pctx, sizeof(*pctx));
  461. }
  462. *pctxp = NULL;
  463. }
  464. /*
  465. * void
  466. */
  467. isc_result_t
  468. cfg_parse_void(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  469. UNUSED(type);
  470. return (cfg_create_obj(pctx, &cfg_type_void, ret));
  471. }
  472. void
  473. cfg_print_void(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  474. UNUSED(pctx);
  475. UNUSED(obj);
  476. }
  477. void
  478. cfg_doc_void(cfg_printer_t *pctx, const cfg_type_t *type) {
  479. UNUSED(pctx);
  480. UNUSED(type);
  481. }
  482. isc_boolean_t
  483. cfg_obj_isvoid(const cfg_obj_t *obj) {
  484. REQUIRE(obj != NULL);
  485. return (ISC_TF(obj->type->rep == &cfg_rep_void));
  486. }
  487. cfg_type_t cfg_type_void = {
  488. "void", cfg_parse_void, cfg_print_void, cfg_doc_void, &cfg_rep_void,
  489. NULL };
  490. /*
  491. * uint32
  492. */
  493. isc_result_t
  494. cfg_parse_uint32(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  495. isc_result_t result;
  496. cfg_obj_t *obj = NULL;
  497. UNUSED(type);
  498. CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER | ISC_LEXOPT_CNUMBER));
  499. if (pctx->token.type != isc_tokentype_number) {
  500. cfg_parser_error(pctx, CFG_LOG_NEAR, "expected number");
  501. return (ISC_R_UNEXPECTEDTOKEN);
  502. }
  503. CHECK(cfg_create_obj(pctx, &cfg_type_uint32, &obj));
  504. obj->value.uint32 = pctx->token.value.as_ulong;
  505. *ret = obj;
  506. cleanup:
  507. return (result);
  508. }
  509. void
  510. cfg_print_cstr(cfg_printer_t *pctx, const char *s) {
  511. cfg_print_chars(pctx, s, strlen(s));
  512. }
  513. void
  514. cfg_print_rawuint(cfg_printer_t *pctx, unsigned int u) {
  515. char buf[32];
  516. snprintf(buf, sizeof(buf), "%u", u);
  517. cfg_print_cstr(pctx, buf);
  518. }
  519. void
  520. cfg_print_uint32(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  521. cfg_print_rawuint(pctx, obj->value.uint32);
  522. }
  523. isc_boolean_t
  524. cfg_obj_isuint32(const cfg_obj_t *obj) {
  525. REQUIRE(obj != NULL);
  526. return (ISC_TF(obj->type->rep == &cfg_rep_uint32));
  527. }
  528. isc_uint32_t
  529. cfg_obj_asuint32(const cfg_obj_t *obj) {
  530. REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint32);
  531. return (obj->value.uint32);
  532. }
  533. cfg_type_t cfg_type_uint32 = {
  534. "integer", cfg_parse_uint32, cfg_print_uint32, cfg_doc_terminal,
  535. &cfg_rep_uint32, NULL
  536. };
  537. /*
  538. * uint64
  539. */
  540. isc_boolean_t
  541. cfg_obj_isuint64(const cfg_obj_t *obj) {
  542. REQUIRE(obj != NULL);
  543. return (ISC_TF(obj->type->rep == &cfg_rep_uint64));
  544. }
  545. isc_uint64_t
  546. cfg_obj_asuint64(const cfg_obj_t *obj) {
  547. REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_uint64);
  548. return (obj->value.uint64);
  549. }
  550. void
  551. cfg_print_uint64(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  552. char buf[32];
  553. snprintf(buf, sizeof(buf), "%" ISC_PRINT_QUADFORMAT "u",
  554. obj->value.uint64);
  555. cfg_print_cstr(pctx, buf);
  556. }
  557. cfg_type_t cfg_type_uint64 = {
  558. "64_bit_integer", NULL, cfg_print_uint64, cfg_doc_terminal,
  559. &cfg_rep_uint64, NULL
  560. };
  561. /*
  562. * qstring (quoted string), ustring (unquoted string), astring
  563. * (any string)
  564. */
  565. /* Create a string object from a null-terminated C string. */
  566. static isc_result_t
  567. create_string(cfg_parser_t *pctx, const char *contents, const cfg_type_t *type,
  568. cfg_obj_t **ret)
  569. {
  570. isc_result_t result;
  571. cfg_obj_t *obj = NULL;
  572. int len;
  573. CHECK(cfg_create_obj(pctx, type, &obj));
  574. len = strlen(contents);
  575. obj->value.string.length = len;
  576. obj->value.string.base = isc_mem_get(pctx->mctx, len + 1);
  577. if (obj->value.string.base == 0) {
  578. isc_mem_put(pctx->mctx, obj, sizeof(*obj));
  579. return (ISC_R_NOMEMORY);
  580. }
  581. memcpy(obj->value.string.base, contents, len);
  582. obj->value.string.base[len] = '\0';
  583. *ret = obj;
  584. cleanup:
  585. return (result);
  586. }
  587. isc_result_t
  588. cfg_parse_qstring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  589. isc_result_t result;
  590. UNUSED(type);
  591. CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
  592. if (pctx->token.type != isc_tokentype_qstring) {
  593. cfg_parser_error(pctx, CFG_LOG_NEAR, "expected quoted string");
  594. return (ISC_R_UNEXPECTEDTOKEN);
  595. }
  596. return (create_string(pctx,
  597. TOKEN_STRING(pctx),
  598. &cfg_type_qstring,
  599. ret));
  600. cleanup:
  601. return (result);
  602. }
  603. static isc_result_t
  604. parse_ustring(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  605. isc_result_t result;
  606. UNUSED(type);
  607. CHECK(cfg_gettoken(pctx, 0));
  608. if (pctx->token.type != isc_tokentype_string) {
  609. cfg_parser_error(pctx, CFG_LOG_NEAR, "expected unquoted string");
  610. return (ISC_R_UNEXPECTEDTOKEN);
  611. }
  612. return (create_string(pctx,
  613. TOKEN_STRING(pctx),
  614. &cfg_type_ustring,
  615. ret));
  616. cleanup:
  617. return (result);
  618. }
  619. isc_result_t
  620. cfg_parse_astring(cfg_parser_t *pctx, const cfg_type_t *type,
  621. cfg_obj_t **ret)
  622. {
  623. isc_result_t result;
  624. UNUSED(type);
  625. CHECK(cfg_getstringtoken(pctx));
  626. return (create_string(pctx,
  627. TOKEN_STRING(pctx),
  628. &cfg_type_qstring,
  629. ret));
  630. cleanup:
  631. return (result);
  632. }
  633. isc_boolean_t
  634. cfg_is_enum(const char *s, const char *const *enums) {
  635. const char * const *p;
  636. for (p = enums; *p != NULL; p++) {
  637. if (strcasecmp(*p, s) == 0)
  638. return (ISC_TRUE);
  639. }
  640. return (ISC_FALSE);
  641. }
  642. static isc_result_t
  643. check_enum(cfg_parser_t *pctx, cfg_obj_t *obj, const char *const *enums) {
  644. const char *s = obj->value.string.base;
  645. if (cfg_is_enum(s, enums))
  646. return (ISC_R_SUCCESS);
  647. cfg_parser_error(pctx, 0, "'%s' unexpected", s);
  648. return (ISC_R_UNEXPECTEDTOKEN);
  649. }
  650. isc_result_t
  651. cfg_parse_enum(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  652. isc_result_t result;
  653. cfg_obj_t *obj = NULL;
  654. CHECK(parse_ustring(pctx, NULL, &obj));
  655. CHECK(check_enum(pctx, obj, type->of));
  656. *ret = obj;
  657. return (ISC_R_SUCCESS);
  658. cleanup:
  659. CLEANUP_OBJ(obj);
  660. return (result);
  661. }
  662. void
  663. cfg_doc_enum(cfg_printer_t *pctx, const cfg_type_t *type) {
  664. const char * const *p;
  665. cfg_print_chars(pctx, "( ", 2);
  666. for (p = type->of; *p != NULL; p++) {
  667. cfg_print_cstr(pctx, *p);
  668. if (p[1] != NULL)
  669. cfg_print_chars(pctx, " | ", 3);
  670. }
  671. cfg_print_chars(pctx, " )", 2);
  672. }
  673. void
  674. cfg_print_ustring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  675. cfg_print_chars(pctx, obj->value.string.base, obj->value.string.length);
  676. }
  677. static void
  678. print_qstring(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  679. cfg_print_chars(pctx, "\"", 1);
  680. cfg_print_ustring(pctx, obj);
  681. cfg_print_chars(pctx, "\"", 1);
  682. }
  683. static void
  684. free_string(cfg_parser_t *pctx, cfg_obj_t *obj) {
  685. isc_mem_put(pctx->mctx, obj->value.string.base,
  686. obj->value.string.length + 1);
  687. }
  688. isc_boolean_t
  689. cfg_obj_isstring(const cfg_obj_t *obj) {
  690. REQUIRE(obj != NULL);
  691. return (ISC_TF(obj->type->rep == &cfg_rep_string));
  692. }
  693. const char *
  694. cfg_obj_asstring(const cfg_obj_t *obj) {
  695. REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_string);
  696. return (obj->value.string.base);
  697. }
  698. /* Quoted string only */
  699. cfg_type_t cfg_type_qstring = {
  700. "quoted_string", cfg_parse_qstring, print_qstring, cfg_doc_terminal,
  701. &cfg_rep_string, NULL
  702. };
  703. /* Unquoted string only */
  704. cfg_type_t cfg_type_ustring = {
  705. "string", parse_ustring, cfg_print_ustring, cfg_doc_terminal,
  706. &cfg_rep_string, NULL
  707. };
  708. /* Any string (quoted or unquoted); printed with quotes */
  709. cfg_type_t cfg_type_astring = {
  710. "string", cfg_parse_astring, print_qstring, cfg_doc_terminal,
  711. &cfg_rep_string, NULL
  712. };
  713. /*
  714. * Booleans
  715. */
  716. isc_boolean_t
  717. cfg_obj_isboolean(const cfg_obj_t *obj) {
  718. REQUIRE(obj != NULL);
  719. return (ISC_TF(obj->type->rep == &cfg_rep_boolean));
  720. }
  721. isc_boolean_t
  722. cfg_obj_asboolean(const cfg_obj_t *obj) {
  723. REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_boolean);
  724. return (obj->value.boolean);
  725. }
  726. isc_result_t
  727. cfg_parse_boolean(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
  728. {
  729. isc_result_t result;
  730. isc_boolean_t value;
  731. cfg_obj_t *obj = NULL;
  732. UNUSED(type);
  733. result = cfg_gettoken(pctx, 0);
  734. if (result != ISC_R_SUCCESS)
  735. return (result);
  736. if (pctx->token.type != isc_tokentype_string)
  737. goto bad_boolean;
  738. if ((strcasecmp(TOKEN_STRING(pctx), "true") == 0) ||
  739. (strcasecmp(TOKEN_STRING(pctx), "yes") == 0) ||
  740. (strcmp(TOKEN_STRING(pctx), "1") == 0)) {
  741. value = ISC_TRUE;
  742. } else if ((strcasecmp(TOKEN_STRING(pctx), "false") == 0) ||
  743. (strcasecmp(TOKEN_STRING(pctx), "no") == 0) ||
  744. (strcmp(TOKEN_STRING(pctx), "0") == 0)) {
  745. value = ISC_FALSE;
  746. } else {
  747. goto bad_boolean;
  748. }
  749. CHECK(cfg_create_obj(pctx, &cfg_type_boolean, &obj));
  750. obj->value.boolean = value;
  751. *ret = obj;
  752. return (result);
  753. bad_boolean:
  754. cfg_parser_error(pctx, CFG_LOG_NEAR, "boolean expected");
  755. return (ISC_R_UNEXPECTEDTOKEN);
  756. cleanup:
  757. return (result);
  758. }
  759. void
  760. cfg_print_boolean(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  761. if (obj->value.boolean)
  762. cfg_print_chars(pctx, "yes", 3);
  763. else
  764. cfg_print_chars(pctx, "no", 2);
  765. }
  766. cfg_type_t cfg_type_boolean = {
  767. "boolean", cfg_parse_boolean, cfg_print_boolean, cfg_doc_terminal,
  768. &cfg_rep_boolean, NULL
  769. };
  770. /*
  771. * Lists.
  772. */
  773. isc_result_t
  774. cfg_create_list(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **obj) {
  775. isc_result_t result;
  776. CHECK(cfg_create_obj(pctx, type, obj));
  777. ISC_LIST_INIT((*obj)->value.list);
  778. cleanup:
  779. return (result);
  780. }
  781. static isc_result_t
  782. create_listelt(cfg_parser_t *pctx, cfg_listelt_t **eltp) {
  783. cfg_listelt_t *elt;
  784. elt = isc_mem_get(pctx->mctx, sizeof(*elt));
  785. if (elt == NULL)
  786. return (ISC_R_NOMEMORY);
  787. elt->obj = NULL;
  788. ISC_LINK_INIT(elt, link);
  789. *eltp = elt;
  790. return (ISC_R_SUCCESS);
  791. }
  792. static void
  793. free_list_elt(cfg_parser_t *pctx, cfg_listelt_t *elt) {
  794. cfg_obj_destroy(pctx, &elt->obj);
  795. isc_mem_put(pctx->mctx, elt, sizeof(*elt));
  796. }
  797. static void
  798. free_list(cfg_parser_t *pctx, cfg_obj_t *obj) {
  799. cfg_listelt_t *elt, *next;
  800. for (elt = ISC_LIST_HEAD(obj->value.list);
  801. elt != NULL;
  802. elt = next)
  803. {
  804. next = ISC_LIST_NEXT(elt, link);
  805. free_list_elt(pctx, elt);
  806. }
  807. }
  808. isc_result_t
  809. cfg_parse_listelt(cfg_parser_t *pctx, const cfg_type_t *elttype,
  810. cfg_listelt_t **ret)
  811. {
  812. isc_result_t result;
  813. cfg_listelt_t *elt = NULL;
  814. cfg_obj_t *value = NULL;
  815. CHECK(create_listelt(pctx, &elt));
  816. result = cfg_parse_obj(pctx, elttype, &value);
  817. if (result != ISC_R_SUCCESS)
  818. goto cleanup;
  819. elt->obj = value;
  820. *ret = elt;
  821. return (ISC_R_SUCCESS);
  822. cleanup:
  823. isc_mem_put(pctx->mctx, elt, sizeof(*elt));
  824. return (result);
  825. }
  826. /*
  827. * Parse a homogeneous list whose elements are of type 'elttype'
  828. * and where each element is terminated by a semicolon.
  829. */
  830. static isc_result_t
  831. parse_list(cfg_parser_t *pctx, const cfg_type_t *listtype, cfg_obj_t **ret)
  832. {
  833. cfg_obj_t *listobj = NULL;
  834. const cfg_type_t *listof = listtype->of;
  835. isc_result_t result;
  836. cfg_listelt_t *elt = NULL;
  837. CHECK(cfg_create_list(pctx, listtype, &listobj));
  838. for (;;) {
  839. CHECK(cfg_peektoken(pctx, 0));
  840. if (pctx->token.type == isc_tokentype_special &&
  841. pctx->token.value.as_char == /*{*/ '}')
  842. break;
  843. CHECK(cfg_parse_listelt(pctx, listof, &elt));
  844. CHECK(parse_semicolon(pctx));
  845. ISC_LIST_APPEND(listobj->value.list, elt, link);
  846. elt = NULL;
  847. }
  848. *ret = listobj;
  849. return (ISC_R_SUCCESS);
  850. cleanup:
  851. if (elt != NULL)
  852. free_list_elt(pctx, elt);
  853. CLEANUP_OBJ(listobj);
  854. return (result);
  855. }
  856. static void
  857. print_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  858. const cfg_list_t *list = &obj->value.list;
  859. const cfg_listelt_t *elt;
  860. for (elt = ISC_LIST_HEAD(*list);
  861. elt != NULL;
  862. elt = ISC_LIST_NEXT(elt, link)) {
  863. print_indent(pctx);
  864. cfg_print_obj(pctx, elt->obj);
  865. cfg_print_chars(pctx, ";\n", 2);
  866. }
  867. }
  868. isc_result_t
  869. cfg_parse_bracketed_list(cfg_parser_t *pctx, const cfg_type_t *type,
  870. cfg_obj_t **ret)
  871. {
  872. isc_result_t result;
  873. CHECK(cfg_parse_special(pctx, '{'));
  874. CHECK(parse_list(pctx, type, ret));
  875. CHECK(cfg_parse_special(pctx, '}'));
  876. cleanup:
  877. return (result);
  878. }
  879. void
  880. cfg_print_bracketed_list(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  881. print_open(pctx);
  882. print_list(pctx, obj);
  883. print_close(pctx);
  884. }
  885. void
  886. cfg_doc_bracketed_list(cfg_printer_t *pctx, const cfg_type_t *type) {
  887. cfg_print_chars(pctx, "{ ", 2);
  888. cfg_doc_obj(pctx, type->of);
  889. cfg_print_chars(pctx, "; ... }", 7);
  890. }
  891. /*
  892. * Parse a homogeneous list whose elements are of type 'elttype'
  893. * and where elements are separated by space. The list ends
  894. * before the first semicolon.
  895. */
  896. isc_result_t
  897. cfg_parse_spacelist(cfg_parser_t *pctx, const cfg_type_t *listtype,
  898. cfg_obj_t **ret)
  899. {
  900. cfg_obj_t *listobj = NULL;
  901. const cfg_type_t *listof = listtype->of;
  902. isc_result_t result;
  903. CHECK(cfg_create_list(pctx, listtype, &listobj));
  904. for (;;) {
  905. cfg_listelt_t *elt = NULL;
  906. CHECK(cfg_peektoken(pctx, 0));
  907. if (pctx->token.type == isc_tokentype_special &&
  908. pctx->token.value.as_char == ';')
  909. break;
  910. CHECK(cfg_parse_listelt(pctx, listof, &elt));
  911. ISC_LIST_APPEND(listobj->value.list, elt, link);
  912. }
  913. *ret = listobj;
  914. return (ISC_R_SUCCESS);
  915. cleanup:
  916. CLEANUP_OBJ(listobj);
  917. return (result);
  918. }
  919. void
  920. cfg_print_spacelist(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  921. const cfg_list_t *list = &obj->value.list;
  922. const cfg_listelt_t *elt;
  923. for (elt = ISC_LIST_HEAD(*list);
  924. elt != NULL;
  925. elt = ISC_LIST_NEXT(elt, link)) {
  926. cfg_print_obj(pctx, elt->obj);
  927. if (ISC_LIST_NEXT(elt, link) != NULL)
  928. cfg_print_chars(pctx, " ", 1);
  929. }
  930. }
  931. isc_boolean_t
  932. cfg_obj_islist(const cfg_obj_t *obj) {
  933. REQUIRE(obj != NULL);
  934. return (ISC_TF(obj->type->rep == &cfg_rep_list));
  935. }
  936. const cfg_listelt_t *
  937. cfg_list_first(const cfg_obj_t *obj) {
  938. REQUIRE(obj == NULL || obj->type->rep == &cfg_rep_list);
  939. if (obj == NULL)
  940. return (NULL);
  941. return (ISC_LIST_HEAD(obj->value.list));
  942. }
  943. const cfg_listelt_t *
  944. cfg_list_next(const cfg_listelt_t *elt) {
  945. REQUIRE(elt != NULL);
  946. return (ISC_LIST_NEXT(elt, link));
  947. }
  948. /*
  949. * Return the length of a list object. If obj is NULL or is not
  950. * a list, return 0.
  951. */
  952. unsigned int
  953. cfg_list_length(const cfg_obj_t *obj, isc_boolean_t recurse) {
  954. const cfg_listelt_t *elt;
  955. unsigned int count = 0;
  956. if (obj == NULL || !cfg_obj_islist(obj))
  957. return (0U);
  958. for (elt = cfg_list_first(obj);
  959. elt != NULL;
  960. elt = cfg_list_next(elt)) {
  961. if (recurse && cfg_obj_islist(elt->obj)) {
  962. count += cfg_list_length(elt->obj, recurse);
  963. } else {
  964. count++;
  965. }
  966. }
  967. return (count);
  968. }
  969. cfg_obj_t *
  970. cfg_listelt_value(const cfg_listelt_t *elt) {
  971. REQUIRE(elt != NULL);
  972. return (elt->obj);
  973. }
  974. /*
  975. * Maps.
  976. */
  977. /*
  978. * Parse a map body. That's something like
  979. *
  980. * "foo 1; bar { glub; }; zap true; zap false;"
  981. *
  982. * i.e., a sequence of option names followed by values and
  983. * terminated by semicolons. Used for the top level of
  984. * the named.conf syntax, as well as for the body of the
  985. * options, view, zone, and other statements.
  986. */
  987. isc_result_t
  988. cfg_parse_mapbody(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret)
  989. {
  990. const cfg_clausedef_t * const *clausesets = type->of;
  991. isc_result_t result;
  992. const cfg_clausedef_t * const *clauseset;
  993. const cfg_clausedef_t *clause;
  994. cfg_obj_t *value = NULL;
  995. cfg_obj_t *obj = NULL;
  996. cfg_obj_t *eltobj = NULL;
  997. cfg_obj_t *includename = NULL;
  998. isc_symvalue_t symval;
  999. cfg_list_t *list = NULL;
  1000. CHECK(create_map(pctx, type, &obj));
  1001. obj->value.map.clausesets = clausesets;
  1002. for (;;) {
  1003. cfg_listelt_t *elt;
  1004. redo:
  1005. /*
  1006. * Parse the option name and see if it is known.
  1007. */
  1008. CHECK(cfg_gettoken(pctx, 0));
  1009. if (pctx->token.type != isc_tokentype_string) {
  1010. cfg_ungettoken(pctx);
  1011. break;
  1012. }
  1013. /*
  1014. * We accept "include" statements wherever a map body
  1015. * clause can occur.
  1016. */
  1017. if (strcasecmp(TOKEN_STRING(pctx), "include") == 0) {
  1018. /*
  1019. * Turn the file name into a temporary configuration
  1020. * object just so that it is not overwritten by the
  1021. * semicolon token.
  1022. */
  1023. CHECK(cfg_parse_obj(pctx, &cfg_type_qstring, &includename));
  1024. CHECK(parse_semicolon(pctx));
  1025. CHECK(parser_openfile(pctx, includename->
  1026. value.string.base));
  1027. cfg_obj_destroy(pctx, &includename);
  1028. goto redo;
  1029. }
  1030. clause = NULL;
  1031. for (clauseset = clausesets; *clauseset != NULL; clauseset++) {
  1032. for (clause = *clauseset;
  1033. clause->name != NULL;
  1034. clause++) {
  1035. if (strcasecmp(TOKEN_STRING(pctx),
  1036. clause->name) == 0)
  1037. goto done;
  1038. }
  1039. }
  1040. done:
  1041. if (clause == NULL || clause->name == NULL) {
  1042. cfg_parser_error(pctx, CFG_LOG_NOPREP, "unknown option");
  1043. /*
  1044. * Try to recover by parsing this option as an unknown
  1045. * option and discarding it.
  1046. */
  1047. CHECK(cfg_parse_obj(pctx, &cfg_type_unsupported, &eltobj));
  1048. cfg_obj_destroy(pctx, &eltobj);
  1049. CHECK(parse_semicolon(pctx));
  1050. continue;
  1051. }
  1052. /* Clause is known. */
  1053. /* Issue warnings if appropriate */
  1054. if ((clause->flags & CFG_CLAUSEFLAG_OBSOLETE) != 0)
  1055. cfg_parser_warning(pctx, 0, "option '%s' is obsolete",
  1056. clause->name);
  1057. if ((clause->flags & CFG_CLAUSEFLAG_NOTIMP) != 0)
  1058. cfg_parser_warning(pctx, 0, "option '%s' is "
  1059. "not implemented", clause->name);
  1060. if ((clause->flags & CFG_CLAUSEFLAG_NYI) != 0)
  1061. cfg_parser_warning(pctx, 0, "option '%s' is "
  1062. "not implemented", clause->name);
  1063. if ((clause->flags & CFG_CLAUSEFLAG_NOTCONFIGURED) != 0) {
  1064. cfg_parser_warning(pctx, 0, "option '%s' is not "
  1065. "configured", clause->name);
  1066. result = ISC_R_FAILURE;
  1067. goto cleanup;
  1068. }
  1069. /*
  1070. * Don't log options with CFG_CLAUSEFLAG_NEWDEFAULT
  1071. * set here - we need to log the *lack* of such an option,
  1072. * not its presence.
  1073. */
  1074. /* See if the clause already has a value; if not create one. */
  1075. result = isc_symtab_lookup(obj->value.map.symtab,
  1076. clause->name, 0, &symval);
  1077. if ((clause->flags & CFG_CLAUSEFLAG_MULTI) != 0) {
  1078. /* Multivalued clause */
  1079. cfg_obj_t *listobj = NULL;
  1080. if (result == ISC_R_NOTFOUND) {
  1081. CHECK(cfg_create_list(pctx,
  1082. &cfg_type_implicitlist,
  1083. &listobj));
  1084. symval.as_pointer = listobj;
  1085. result = isc_symtab_define(obj->value.
  1086. map.symtab,
  1087. clause->name,
  1088. 1, symval,
  1089. isc_symexists_reject);
  1090. if (result != ISC_R_SUCCESS) {
  1091. cfg_parser_error(pctx, CFG_LOG_NEAR,
  1092. "isc_symtab_define(%s) "
  1093. "failed", clause->name);
  1094. isc_mem_put(pctx->mctx, list,
  1095. sizeof(cfg_list_t));
  1096. goto cleanup;
  1097. }
  1098. } else {
  1099. INSIST(result == ISC_R_SUCCESS);
  1100. listobj = symval.as_pointer;
  1101. }
  1102. elt = NULL;
  1103. CHECK(cfg_parse_listelt(pctx, clause->type, &elt));
  1104. CHECK(parse_semicolon(pctx));
  1105. ISC_LIST_APPEND(listobj->value.list, elt, link);
  1106. } else {
  1107. /* Single-valued clause */
  1108. if (result == ISC_R_NOTFOUND) {
  1109. isc_boolean_t callback =
  1110. ISC_TF((clause->flags &
  1111. CFG_CLAUSEFLAG_CALLBACK) != 0);
  1112. CHECK(parse_symtab_elt(pctx, clause->name,
  1113. clause->type,
  1114. obj->value.map.symtab,
  1115. callback));
  1116. CHECK(parse_semicolon(pctx));
  1117. } else if (result == ISC_R_SUCCESS) {
  1118. cfg_parser_error(pctx, CFG_LOG_NEAR, "'%s' redefined",
  1119. clause->name);
  1120. result = ISC_R_EXISTS;
  1121. goto cleanup;
  1122. } else {
  1123. cfg_parser_error(pctx, CFG_LOG_NEAR,
  1124. "isc_symtab_define() failed");
  1125. goto cleanup;
  1126. }
  1127. }
  1128. }
  1129. *ret = obj;
  1130. return (ISC_R_SUCCESS);
  1131. cleanup:
  1132. CLEANUP_OBJ(value);
  1133. CLEANUP_OBJ(obj);
  1134. CLEANUP_OBJ(eltobj);
  1135. CLEANUP_OBJ(includename);
  1136. return (result);
  1137. }
  1138. static isc_result_t
  1139. parse_symtab_elt(cfg_parser_t *pctx, const char *name,
  1140. cfg_type_t *elttype, isc_symtab_t *symtab,
  1141. isc_boolean_t callback)
  1142. {
  1143. isc_result_t result;
  1144. cfg_obj_t *obj = NULL;
  1145. isc_symvalue_t symval;
  1146. CHECK(cfg_parse_obj(pctx, elttype, &obj));
  1147. if (callback && pctx->callback != NULL)
  1148. CHECK(pctx->callback(name, obj, pctx->callbackarg));
  1149. symval.as_pointer = obj;
  1150. CHECK(isc_symtab_define(symtab, name,
  1151. 1, symval,
  1152. isc_symexists_reject));
  1153. return (ISC_R_SUCCESS);
  1154. cleanup:
  1155. CLEANUP_OBJ(obj);
  1156. return (result);
  1157. }
  1158. /*
  1159. * Parse a map; e.g., "{ foo 1; bar { glub; }; zap true; zap false; }"
  1160. */
  1161. isc_result_t
  1162. cfg_parse_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  1163. isc_result_t result;
  1164. CHECK(cfg_parse_special(pctx, '{'));
  1165. CHECK(cfg_parse_mapbody(pctx, type, ret));
  1166. CHECK(cfg_parse_special(pctx, '}'));
  1167. cleanup:
  1168. return (result);
  1169. }
  1170. /*
  1171. * Subroutine for cfg_parse_named_map() and cfg_parse_addressed_map().
  1172. */
  1173. static isc_result_t
  1174. parse_any_named_map(cfg_parser_t *pctx, cfg_type_t *nametype, const cfg_type_t *type,
  1175. cfg_obj_t **ret)
  1176. {
  1177. isc_result_t result;
  1178. cfg_obj_t *idobj = NULL;
  1179. cfg_obj_t *mapobj = NULL;
  1180. CHECK(cfg_parse_obj(pctx, nametype, &idobj));
  1181. CHECK(cfg_parse_map(pctx, type, &mapobj));
  1182. mapobj->value.map.id = idobj;
  1183. idobj = NULL;
  1184. *ret = mapobj;
  1185. cleanup:
  1186. CLEANUP_OBJ(idobj);
  1187. return (result);
  1188. }
  1189. /*
  1190. * Parse a map identified by a string name. E.g., "name { foo 1; }".
  1191. * Used for the "key" and "channel" statements.
  1192. */
  1193. isc_result_t
  1194. cfg_parse_named_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  1195. return (parse_any_named_map(pctx, &cfg_type_astring, type, ret));
  1196. }
  1197. /*
  1198. * Parse a map identified by a network address.
  1199. * Used to be used for the "server" statement.
  1200. */
  1201. isc_result_t
  1202. cfg_parse_addressed_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  1203. return (parse_any_named_map(pctx, &cfg_type_netaddr, type, ret));
  1204. }
  1205. /*
  1206. * Parse a map identified by a network prefix.
  1207. * Used for the "server" statement.
  1208. */
  1209. isc_result_t
  1210. cfg_parse_netprefix_map(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  1211. return (parse_any_named_map(pctx, &cfg_type_netprefix, type, ret));
  1212. }
  1213. void
  1214. cfg_print_mapbody(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  1215. isc_result_t result = ISC_R_SUCCESS;
  1216. const cfg_clausedef_t * const *clauseset;
  1217. for (clauseset = obj->value.map.clausesets;
  1218. *clauseset != NULL;
  1219. clauseset++)
  1220. {
  1221. isc_symvalue_t symval;
  1222. const cfg_clausedef_t *clause;
  1223. for (clause = *clauseset;
  1224. clause->name != NULL;
  1225. clause++) {
  1226. result = isc_symtab_lookup(obj->value.map.symtab,
  1227. clause->name, 0, &symval);
  1228. if (result == ISC_R_SUCCESS) {
  1229. cfg_obj_t *obj = symval.as_pointer;
  1230. if (obj->type == &cfg_type_implicitlist) {
  1231. /* Multivalued. */
  1232. cfg_list_t *list = &obj->value.list;
  1233. cfg_listelt_t *elt;
  1234. for (elt = ISC_LIST_HEAD(*list);
  1235. elt != NULL;
  1236. elt = ISC_LIST_NEXT(elt, link)) {
  1237. print_indent(pctx);
  1238. cfg_print_cstr(pctx, clause->name);
  1239. cfg_print_chars(pctx, " ", 1);
  1240. cfg_print_obj(pctx, elt->obj);
  1241. cfg_print_chars(pctx, ";\n", 2);
  1242. }
  1243. } else {
  1244. /* Single-valued. */
  1245. print_indent(pctx);
  1246. cfg_print_cstr(pctx, clause->name);
  1247. cfg_print_chars(pctx, " ", 1);
  1248. cfg_print_obj(pctx, obj);
  1249. cfg_print_chars(pctx, ";\n", 2);
  1250. }
  1251. } else if (result == ISC_R_NOTFOUND) {
  1252. ; /* do nothing */
  1253. } else {
  1254. INSIST(0);
  1255. }
  1256. }
  1257. }
  1258. }
  1259. void
  1260. cfg_doc_mapbody(cfg_printer_t *pctx, const cfg_type_t *type) {
  1261. const cfg_clausedef_t * const *clauseset;
  1262. const cfg_clausedef_t *clause;
  1263. for (clauseset = type->of; *clauseset != NULL; clauseset++) {
  1264. for (clause = *clauseset;
  1265. clause->name != NULL;
  1266. clause++) {
  1267. cfg_print_cstr(pctx, clause->name);
  1268. cfg_print_chars(pctx, " ", 1);
  1269. cfg_doc_obj(pctx, clause->type);
  1270. cfg_print_chars(pctx, ";", 1);
  1271. /* XXX print flags here? */
  1272. cfg_print_chars(pctx, "\n\n", 2);
  1273. }
  1274. }
  1275. }
  1276. static struct flagtext {
  1277. unsigned int flag;
  1278. const char *text;
  1279. } flagtexts[] = {
  1280. { CFG_CLAUSEFLAG_NOTIMP, "not implemented" },
  1281. { CFG_CLAUSEFLAG_NYI, "not yet implemented" },
  1282. { CFG_CLAUSEFLAG_OBSOLETE, "obsolete" },
  1283. { CFG_CLAUSEFLAG_NEWDEFAULT, "default changed" },
  1284. { CFG_CLAUSEFLAG_TESTONLY, "test only" },
  1285. { CFG_CLAUSEFLAG_NOTCONFIGURED, "not configured" },
  1286. { 0, NULL }
  1287. };
  1288. void
  1289. cfg_print_map(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  1290. if (obj->value.map.id != NULL) {
  1291. cfg_print_obj(pctx, obj->value.map.id);
  1292. cfg_print_chars(pctx, " ", 1);
  1293. }
  1294. print_open(pctx);
  1295. cfg_print_mapbody(pctx, obj);
  1296. print_close(pctx);
  1297. }
  1298. static void
  1299. print_clause_flags(cfg_printer_t *pctx, unsigned int flags) {
  1300. struct flagtext *p;
  1301. isc_boolean_t first = ISC_TRUE;
  1302. for (p = flagtexts; p->flag != 0; p++) {
  1303. if ((flags & p->flag) != 0) {
  1304. if (first)
  1305. cfg_print_chars(pctx, " // ", 4);
  1306. else
  1307. cfg_print_chars(pctx, ", ", 2);
  1308. cfg_print_cstr(pctx, p->text);
  1309. first = ISC_FALSE;
  1310. }
  1311. }
  1312. }
  1313. void
  1314. cfg_doc_map(cfg_printer_t *pctx, const cfg_type_t *type) {
  1315. const cfg_clausedef_t * const *clauseset;
  1316. const cfg_clausedef_t *clause;
  1317. if (type->parse == cfg_parse_named_map) {
  1318. cfg_doc_obj(pctx, &cfg_type_astring);
  1319. cfg_print_chars(pctx, " ", 1);
  1320. } else if (type->parse == cfg_parse_addressed_map) {
  1321. cfg_doc_obj(pctx, &cfg_type_netaddr);
  1322. cfg_print_chars(pctx, " ", 1);
  1323. } else if (type->parse == cfg_parse_netprefix_map) {
  1324. cfg_doc_obj(pctx, &cfg_type_netprefix);
  1325. cfg_print_chars(pctx, " ", 1);
  1326. }
  1327. print_open(pctx);
  1328. for (clauseset = type->of; *clauseset != NULL; clauseset++) {
  1329. for (clause = *clauseset;
  1330. clause->name != NULL;
  1331. clause++) {
  1332. print_indent(pctx);
  1333. cfg_print_cstr(pctx, clause->name);
  1334. if (clause->type->print != cfg_print_void)
  1335. cfg_print_chars(pctx, " ", 1);
  1336. cfg_doc_obj(pctx, clause->type);
  1337. cfg_print_chars(pctx, ";", 1);
  1338. print_clause_flags(pctx, clause->flags);
  1339. cfg_print_chars(pctx, "\n", 1);
  1340. }
  1341. }
  1342. print_close(pctx);
  1343. }
  1344. isc_boolean_t
  1345. cfg_obj_ismap(const cfg_obj_t *obj) {
  1346. REQUIRE(obj != NULL);
  1347. return (ISC_TF(obj->type->rep == &cfg_rep_map));
  1348. }
  1349. isc_result_t
  1350. cfg_map_get(const cfg_obj_t *mapobj, const char* name, const cfg_obj_t **obj) {
  1351. isc_result_t result;
  1352. isc_symvalue_t val;
  1353. const cfg_map_t *map;
  1354. REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
  1355. REQUIRE(name != NULL);
  1356. REQUIRE(obj != NULL && *obj == NULL);
  1357. map = &mapobj->value.map;
  1358. result = isc_symtab_lookup(map->symtab, name, MAP_SYM, &val);
  1359. if (result != ISC_R_SUCCESS)
  1360. return (result);
  1361. *obj = val.as_pointer;
  1362. return (ISC_R_SUCCESS);
  1363. }
  1364. const cfg_obj_t *
  1365. cfg_map_getname(const cfg_obj_t *mapobj) {
  1366. REQUIRE(mapobj != NULL && mapobj->type->rep == &cfg_rep_map);
  1367. return (mapobj->value.map.id);
  1368. }
  1369. /* Parse an arbitrary token, storing its raw text representation. */
  1370. static isc_result_t
  1371. parse_token(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  1372. cfg_obj_t *obj = NULL;
  1373. isc_result_t result;
  1374. isc_region_t r;
  1375. UNUSED(type);
  1376. CHECK(cfg_create_obj(pctx, &cfg_type_token, &obj));
  1377. CHECK(cfg_gettoken(pctx, CFG_LEXOPT_QSTRING));
  1378. if (pctx->token.type == isc_tokentype_eof) {
  1379. cfg_ungettoken(pctx);
  1380. result = ISC_R_EOF;
  1381. goto cleanup;
  1382. }
  1383. isc_lex_getlasttokentext(pctx->lexer, &pctx->token, &r);
  1384. obj->value.string.base = isc_mem_get(pctx->mctx, r.length + 1);
  1385. if (obj->value.string.base == NULL) {
  1386. result = ISC_R_NOMEMORY;
  1387. goto cleanup;
  1388. }
  1389. obj->value.string.length = r.length;
  1390. memcpy(obj->value.string.base, r.base, r.length);
  1391. obj->value.string.base[r.length] = '\0';
  1392. *ret = obj;
  1393. return (result);
  1394. cleanup:
  1395. if (obj != NULL)
  1396. isc_mem_put(pctx->mctx, obj, sizeof(*obj));
  1397. return (result);
  1398. }
  1399. cfg_type_t cfg_type_token = {
  1400. "token", parse_token, cfg_print_ustring, cfg_doc_terminal,
  1401. &cfg_rep_string, NULL
  1402. };
  1403. /*
  1404. * An unsupported option. This is just a list of tokens with balanced braces
  1405. * ending in a semicolon.
  1406. */
  1407. static isc_result_t
  1408. parse_unsupported(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  1409. cfg_obj_t *listobj = NULL;
  1410. isc_result_t result;
  1411. int braces = 0;
  1412. CHECK(cfg_create_list(pctx, type, &listobj));
  1413. for (;;) {
  1414. cfg_listelt_t *elt = NULL;
  1415. CHECK(cfg_peektoken(pctx, 0));
  1416. if (pctx->token.type == isc_tokentype_special) {
  1417. if (pctx->token.value.as_char == '{')
  1418. braces++;
  1419. else if (pctx->token.value.as_char == '}')
  1420. braces--;
  1421. else if (pctx->token.value.as_char == ';')
  1422. if (braces == 0)
  1423. break;
  1424. }
  1425. if (pctx->token.type == isc_tokentype_eof || braces < 0) {
  1426. cfg_parser_error(pctx, CFG_LOG_NEAR, "unexpected token");
  1427. result = ISC_R_UNEXPECTEDTOKEN;
  1428. goto cleanup;
  1429. }
  1430. CHECK(cfg_parse_listelt(pctx, &cfg_type_token, &elt));
  1431. ISC_LIST_APPEND(listobj->value.list, elt, link);
  1432. }
  1433. INSIST(braces == 0);
  1434. *ret = listobj;
  1435. return (ISC_R_SUCCESS);
  1436. cleanup:
  1437. CLEANUP_OBJ(listobj);
  1438. return (result);
  1439. }
  1440. cfg_type_t cfg_type_unsupported = {
  1441. "unsupported", parse_unsupported, cfg_print_spacelist, cfg_doc_terminal,
  1442. &cfg_rep_list, NULL
  1443. };
  1444. /*
  1445. * Try interpreting the current token as a network address.
  1446. *
  1447. * If CFG_ADDR_WILDOK is set in flags, "*" can be used as a wildcard
  1448. * and at least one of CFG_ADDR_V4OK and CFG_ADDR_V6OK must also be set. The
  1449. * "*" is interpreted as the IPv4 wildcard address if CFG_ADDR_V4OK is
  1450. * set (including the case where CFG_ADDR_V4OK and CFG_ADDR_V6OK are both set),
  1451. * and the IPv6 wildcard address otherwise.
  1452. */
  1453. static isc_result_t
  1454. token_addr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
  1455. char *s;
  1456. struct in_addr in4a;
  1457. struct in6_addr in6a;
  1458. if (pctx->token.type != isc_tokentype_string)
  1459. return (ISC_R_UNEXPECTEDTOKEN);
  1460. s = TOKEN_STRING(pctx);
  1461. if ((flags & CFG_ADDR_WILDOK) != 0 && strcmp(s, "*") == 0) {
  1462. if ((flags & CFG_ADDR_V4OK) != 0) {
  1463. isc_netaddr_any(na);
  1464. return (ISC_R_SUCCESS);
  1465. } else if ((flags & CFG_ADDR_V6OK) != 0) {
  1466. isc_netaddr_any6(na);
  1467. return (ISC_R_SUCCESS);
  1468. } else {
  1469. INSIST(0);
  1470. }
  1471. } else {
  1472. if ((flags & (CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK)) != 0) {
  1473. if (inet_pton(AF_INET, s, &in4a) == 1) {
  1474. isc_netaddr_fromin(na, &in4a);
  1475. return (ISC_R_SUCCESS);
  1476. }
  1477. }
  1478. if ((flags & CFG_ADDR_V4PREFIXOK) != 0 &&
  1479. strlen(s) <= 15U) {
  1480. char buf[64];
  1481. int i;
  1482. strcpy(buf, s);
  1483. for (i = 0; i < 3; i++) {
  1484. strcat(buf, ".0");
  1485. if (inet_pton(AF_INET, buf, &in4a) == 1) {
  1486. isc_netaddr_fromin(na, &in4a);
  1487. return (ISC_R_SUCCESS);
  1488. }
  1489. }
  1490. }
  1491. if ((flags & CFG_ADDR_V6OK) != 0 &&
  1492. strlen(s) <= 127U) {
  1493. char buf[128]; /* see lib/bind9/getaddresses.c */
  1494. char *d; /* zone delimiter */
  1495. isc_uint32_t zone = 0; /* scope zone ID */
  1496. strcpy(buf, s);
  1497. d = strchr(buf, '%');
  1498. if (d != NULL)
  1499. *d = '\0';
  1500. if (inet_pton(AF_INET6, buf, &in6a) == 1) {
  1501. if (d != NULL) {
  1502. #ifdef ISC_PLATFORM_HAVESCOPEID
  1503. isc_result_t result;
  1504. result = isc_netscope_pton(AF_INET6,
  1505. d + 1,
  1506. &in6a,
  1507. &zone);
  1508. if (result != ISC_R_SUCCESS)
  1509. return (result);
  1510. #else
  1511. return (ISC_R_BADADDRESSFORM);
  1512. #endif
  1513. }
  1514. isc_netaddr_fromin6(na, &in6a);
  1515. isc_netaddr_setzone(na, zone);
  1516. return (ISC_R_SUCCESS);
  1517. }
  1518. }
  1519. }
  1520. return (ISC_R_UNEXPECTEDTOKEN);
  1521. }
  1522. isc_result_t
  1523. cfg_parse_rawaddr(cfg_parser_t *pctx, unsigned int flags, isc_netaddr_t *na) {
  1524. isc_result_t result;
  1525. const char *wild = "";
  1526. const char *prefix = "";
  1527. CHECK(cfg_gettoken(pctx, 0));
  1528. result = token_addr(pctx, flags, na);
  1529. if (result == ISC_R_UNEXPECTEDTOKEN) {
  1530. if ((flags & CFG_ADDR_WILDOK) != 0)
  1531. wild = " or '*'";
  1532. if ((flags & CFG_ADDR_V4PREFIXOK) != 0)
  1533. wild = " or IPv4 prefix";
  1534. if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V4OK)
  1535. cfg_parser_error(pctx, CFG_LOG_NEAR,
  1536. "expected IPv4 address%s%s",
  1537. prefix, wild);
  1538. else if ((flags & CFG_ADDR_MASK) == CFG_ADDR_V6OK)
  1539. cfg_parser_error(pctx, CFG_LOG_NEAR,
  1540. "expected IPv6 address%s%s",
  1541. prefix, wild);
  1542. else
  1543. cfg_parser_error(pctx, CFG_LOG_NEAR,
  1544. "expected IP address%s%s",
  1545. prefix, wild);
  1546. }
  1547. cleanup:
  1548. return (result);
  1549. }
  1550. isc_boolean_t
  1551. cfg_lookingat_netaddr(cfg_parser_t *pctx, unsigned int flags) {
  1552. isc_result_t result;
  1553. isc_netaddr_t na_dummy;
  1554. result = token_addr(pctx, flags, &na_dummy);
  1555. return (ISC_TF(result == ISC_R_SUCCESS));
  1556. }
  1557. isc_result_t
  1558. cfg_parse_rawport(cfg_parser_t *pctx, unsigned int flags, in_port_t *port) {
  1559. isc_result_t result;
  1560. CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
  1561. if ((flags & CFG_ADDR_WILDOK) != 0 &&
  1562. pctx->token.type == isc_tokentype_string &&
  1563. strcmp(TOKEN_STRING(pctx), "*") == 0) {
  1564. *port = 0;
  1565. return (ISC_R_SUCCESS);
  1566. }
  1567. if (pctx->token.type != isc_tokentype_number) {
  1568. cfg_parser_error(pctx, CFG_LOG_NEAR,
  1569. "expected port number or '*'");
  1570. return (ISC_R_UNEXPECTEDTOKEN);
  1571. }
  1572. if (pctx->token.value.as_ulong >= 65536U) {
  1573. cfg_parser_error(pctx, CFG_LOG_NEAR,
  1574. "port number out of range");
  1575. return (ISC_R_UNEXPECTEDTOKEN);
  1576. }
  1577. *port = (in_port_t)(pctx->token.value.as_ulong);
  1578. return (ISC_R_SUCCESS);
  1579. cleanup:
  1580. return (result);
  1581. }
  1582. void
  1583. cfg_print_rawaddr(cfg_printer_t *pctx, const isc_netaddr_t *na) {
  1584. isc_result_t result;
  1585. char text[128];
  1586. isc_buffer_t buf;
  1587. isc_buffer_init(&buf, text, sizeof(text));
  1588. result = isc_netaddr_totext(na, &buf);
  1589. RUNTIME_CHECK(result == ISC_R_SUCCESS);
  1590. cfg_print_chars(pctx, isc_buffer_base(&buf), isc_buffer_usedlength(&buf));
  1591. }
  1592. /* netaddr */
  1593. static unsigned int netaddr_flags = CFG_ADDR_V4OK | CFG_ADDR_V6OK;
  1594. static unsigned int netaddr4_flags = CFG_ADDR_V4OK;
  1595. static unsigned int netaddr4wild_flags = CFG_ADDR_V4OK | CFG_ADDR_WILDOK;
  1596. static unsigned int netaddr6_flags = CFG_ADDR_V6OK;
  1597. static unsigned int netaddr6wild_flags = CFG_ADDR_V6OK | CFG_ADDR_WILDOK;
  1598. static isc_result_t
  1599. parse_netaddr(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
  1600. isc_result_t result;
  1601. cfg_obj_t *obj = NULL;
  1602. isc_netaddr_t netaddr;
  1603. unsigned int flags = *(const unsigned int *)type->of;
  1604. CHECK(cfg_create_obj(pctx, type, &obj));
  1605. CHECK(cfg_parse_rawaddr(pctx, flags, &netaddr));
  1606. isc_sockaddr_fromnetaddr(&obj->value.sockaddr, &netaddr, 0);
  1607. *ret = obj;
  1608. return (ISC_R_SUCCESS);
  1609. cleanup:
  1610. CLEANUP_OBJ(obj);
  1611. return (result);
  1612. }
  1613. static void
  1614. cfg_doc_netaddr(cfg_printer_t *pctx, const cfg_type_t *type) {
  1615. const unsigned int *flagp = type->of;
  1616. int n = 0;
  1617. if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
  1618. cfg_print_chars(pctx, "( ", 2);
  1619. if (*flagp & CFG_ADDR_V4OK) {
  1620. cfg_print_cstr(pctx, "<ipv4_address>");
  1621. n++;
  1622. }
  1623. if (*flagp & CFG_ADDR_V6OK) {
  1624. if (n != 0)
  1625. cfg_print_chars(pctx, " | ", 3);
  1626. cfg_print_cstr(pctx, "<ipv6_address>");
  1627. n++;
  1628. }
  1629. if (*flagp & CFG_ADDR_WILDOK) {
  1630. if (n != 0)
  1631. cfg_print_chars(pctx, " | ", 3);
  1632. cfg_print_chars(pctx, "*", 1);
  1633. n++;
  1634. POST(n);
  1635. }
  1636. if (*flagp != CFG_ADDR_V4OK && *flagp != CFG_ADDR_V6OK)
  1637. cfg_print_chars(pctx, " )", 2);
  1638. }
  1639. cfg_type_t cfg_type_netaddr = {
  1640. "netaddr", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
  1641. &cfg_rep_sockaddr, &netaddr_flags
  1642. };
  1643. cfg_type_t cfg_type_netaddr4 = {
  1644. "netaddr4", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
  1645. &cfg_rep_sockaddr, &netaddr4_flags
  1646. };
  1647. cfg_type_t cfg_type_netaddr4wild = {
  1648. "netaddr4wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
  1649. &cfg_rep_sockaddr, &netaddr4wild_flags
  1650. };
  1651. cfg_type_t cfg_type_netaddr6 = {
  1652. "netaddr6", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
  1653. &cfg_rep_sockaddr, &netaddr6_flags
  1654. };
  1655. cfg_type_t cfg_type_netaddr6wild = {
  1656. "netaddr6wild", parse_netaddr, cfg_print_sockaddr, cfg_doc_netaddr,
  1657. &cfg_rep_sockaddr, &netaddr6wild_flags
  1658. };
  1659. /* netprefix */
  1660. isc_result_t
  1661. cfg_parse_netprefix(cfg_parser_t *pctx, const cfg_type_t *type,
  1662. cfg_obj_t **ret)
  1663. {
  1664. cfg_obj_t *obj = NULL;
  1665. isc_result_t result;
  1666. isc_netaddr_t netaddr;
  1667. unsigned int addrlen = 0, prefixlen;
  1668. UNUSED(type);
  1669. CHECK(cfg_parse_rawaddr(pctx, CFG_ADDR_V4OK | CFG_ADDR_V4PREFIXOK |
  1670. CFG_ADDR_V6OK, &netaddr));
  1671. switch (netaddr.family) {
  1672. case AF_INET:
  1673. addrlen = 32;
  1674. break;
  1675. case AF_INET6:
  1676. addrlen = 128;
  1677. break;
  1678. default:
  1679. INSIST(0);
  1680. break;
  1681. }
  1682. CHECK(cfg_peektoken(pctx, 0));
  1683. if (pctx->token.type == isc_tokentype_special &&
  1684. pctx->token.value.as_char == '/') {
  1685. CHECK(cfg_gettoken(pctx, 0)); /* read "/" */
  1686. CHECK(cfg_gettoken(pctx, ISC_LEXOPT_NUMBER));
  1687. if (pctx->token.type != isc_tokentype_number) {
  1688. cfg_parser_error(pctx, CFG_LOG_NEAR,
  1689. "expected prefix length");
  1690. return (ISC_R_UNEXPECTEDTOKEN);
  1691. }
  1692. prefixlen = pctx->token.value.as_ulong;
  1693. if (prefixlen > addrlen) {
  1694. cfg_parser_error(pctx, CFG_LOG_NOPREP,
  1695. "invalid prefix length");
  1696. return (ISC_R_RANGE);
  1697. }
  1698. } else {
  1699. prefixlen = addrlen;
  1700. }
  1701. CHECK(cfg_create_obj(pctx, &cfg_type_netprefix, &obj));
  1702. obj->value.netprefix.address = netaddr;
  1703. obj->value.netprefix.prefixlen = prefixlen;
  1704. *ret = obj;
  1705. return (ISC_R_SUCCESS);
  1706. cleanup:
  1707. cfg_parser_error(pctx, CFG_LOG_NEAR, "expected network prefix");
  1708. return (result);
  1709. }
  1710. static void
  1711. print_netprefix(cfg_printer_t *pctx, const cfg_obj_t *obj) {
  1712. const cfg_netprefix_t *p = &obj->value.netprefix;
  1713. cfg_print_rawaddr(pctx, &p->address);
  1714. cfg_print_chars(pctx, "/", 1);
  1715. cfg_print_rawuint(pctx, p->prefixlen);
  1716. }
  1717. isc_boolean_t
  1718. cfg_obj_isnetprefix(const cfg_obj_t *obj) {
  1719. REQUIRE(obj != NULL);
  1720. return (ISC_TF(obj->type->rep == &cfg_rep_netprefix));
  1721. }
  1722. void
  1723. cfg_obj_asnetprefix(const cfg_obj_t *obj, isc_netaddr_t *netaddr,
  1724. unsigned int *prefixlen)
  1725. {
  1726. REQUIRE(obj != NULL && obj->type->rep == &cfg_rep_netprefix);
  1727. REQUIRE(netaddr != NULL);
  1728. REQUIRE(prefixlen != NULL);
  1729. *netaddr = obj->value.netprefix.address;
  1730. *prefixlen = obj->value.netprefix.prefixlen;
  1731. }
  1732. cfg_type_t cfg_type_netprefix = {
  1733. "netprefix", cfg_parse_netpr