/contrib/bind9/lib/irs/resconf.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 637 lines · 434 code · 115 blank · 88 comment · 183 complexity · 7318eba2080ab30a8fe7906be150b35f MD5 · raw file

  1. /*
  2. * Copyright (C) 2009, 2011, 2012 Internet Systems Consortium, Inc. ("ISC")
  3. *
  4. * Permission to use, copy, modify, and/or distribute this software for any
  5. * purpose with or without fee is hereby granted, provided that the above
  6. * copyright notice and this permission notice appear in all copies.
  7. *
  8. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  9. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  10. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  12. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  13. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  14. * PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. /* $Id$ */
  17. /*! \file resconf.c */
  18. /**
  19. * Module for parsing resolv.conf files (largely derived from lwconfig.c).
  20. *
  21. * irs_resconf_load() opens the file filename and parses it to initialize
  22. * the configuration structure.
  23. *
  24. * \section lwconfig_return Return Values
  25. *
  26. * irs_resconf_load() returns #IRS_R_SUCCESS if it successfully read and
  27. * parsed filename. It returns a non-0 error code if filename could not be
  28. * opened or contained incorrect resolver statements.
  29. *
  30. * \section lwconfig_see See Also
  31. *
  32. * stdio(3), \link resolver resolver \endlink
  33. *
  34. * \section files Files
  35. *
  36. * /etc/resolv.conf
  37. */
  38. #include <config.h>
  39. #include <sys/types.h>
  40. #include <sys/socket.h>
  41. #include <ctype.h>
  42. #include <errno.h>
  43. #include <netdb.h>
  44. #include <stdlib.h>
  45. #include <stdio.h>
  46. #include <string.h>
  47. #include <isc/magic.h>
  48. #include <isc/mem.h>
  49. #include <isc/netaddr.h>
  50. #include <isc/sockaddr.h>
  51. #include <isc/util.h>
  52. #include <irs/resconf.h>
  53. #define IRS_RESCONF_MAGIC ISC_MAGIC('R', 'E', 'S', 'c')
  54. #define IRS_RESCONF_VALID(c) ISC_MAGIC_VALID(c, IRS_RESCONF_MAGIC)
  55. /*!
  56. * protocol constants
  57. */
  58. #if ! defined(NS_INADDRSZ)
  59. #define NS_INADDRSZ 4
  60. #endif
  61. #if ! defined(NS_IN6ADDRSZ)
  62. #define NS_IN6ADDRSZ 16
  63. #endif
  64. /*!
  65. * resolv.conf parameters
  66. */
  67. #define RESCONFMAXNAMESERVERS 3 /*%< max 3 "nameserver" entries */
  68. #define RESCONFMAXSEARCH 8 /*%< max 8 domains in "search" entry */
  69. #define RESCONFMAXLINELEN 256 /*%< max size of a line */
  70. #define RESCONFMAXSORTLIST 10 /*%< max 10 */
  71. /*!
  72. * configuration data structure
  73. */
  74. struct irs_resconf {
  75. /*
  76. * The configuration data is a thread-specific object, and does not
  77. * need to be locked.
  78. */
  79. unsigned int magic;
  80. isc_mem_t *mctx;
  81. isc_sockaddrlist_t nameservers;
  82. unsigned int numns; /*%< number of configured servers */
  83. char *domainname;
  84. char *search[RESCONFMAXSEARCH];
  85. isc_uint8_t searchnxt; /*%< index for next free slot */
  86. irs_resconf_searchlist_t searchlist;
  87. struct {
  88. isc_netaddr_t addr;
  89. /*% mask has a non-zero 'family' if set */
  90. isc_netaddr_t mask;
  91. } sortlist[RESCONFMAXSORTLIST];
  92. isc_uint8_t sortlistnxt;
  93. /*%< non-zero if 'options debug' set */
  94. isc_uint8_t resdebug;
  95. /*%< set to n in 'options ndots:n' */
  96. isc_uint8_t ndots;
  97. };
  98. static isc_result_t
  99. resconf_parsenameserver(irs_resconf_t *conf, FILE *fp);
  100. static isc_result_t
  101. resconf_parsedomain(irs_resconf_t *conf, FILE *fp);
  102. static isc_result_t
  103. resconf_parsesearch(irs_resconf_t *conf, FILE *fp);
  104. static isc_result_t
  105. resconf_parsesortlist(irs_resconf_t *conf, FILE *fp);
  106. static isc_result_t
  107. resconf_parseoption(irs_resconf_t *ctx, FILE *fp);
  108. /*!
  109. * Eat characters from FP until EOL or EOF. Returns EOF or '\n'
  110. */
  111. static int
  112. eatline(FILE *fp) {
  113. int ch;
  114. ch = fgetc(fp);
  115. while (ch != '\n' && ch != EOF)
  116. ch = fgetc(fp);
  117. return (ch);
  118. }
  119. /*!
  120. * Eats white space up to next newline or non-whitespace character (of
  121. * EOF). Returns the last character read. Comments are considered white
  122. * space.
  123. */
  124. static int
  125. eatwhite(FILE *fp) {
  126. int ch;
  127. ch = fgetc(fp);
  128. while (ch != '\n' && ch != EOF && isspace((unsigned char)ch))
  129. ch = fgetc(fp);
  130. if (ch == ';' || ch == '#')
  131. ch = eatline(fp);
  132. return (ch);
  133. }
  134. /*!
  135. * Skip over any leading whitespace and then read in the next sequence of
  136. * non-whitespace characters. In this context newline is not considered
  137. * whitespace. Returns EOF on end-of-file, or the character
  138. * that caused the reading to stop.
  139. */
  140. static int
  141. getword(FILE *fp, char *buffer, size_t size) {
  142. int ch;
  143. char *p = buffer;
  144. REQUIRE(buffer != NULL);
  145. REQUIRE(size > 0U);
  146. *p = '\0';
  147. ch = eatwhite(fp);
  148. if (ch == EOF)
  149. return (EOF);
  150. do {
  151. *p = '\0';
  152. if (ch == EOF || isspace((unsigned char)ch))
  153. break;
  154. else if ((size_t) (p - buffer) == size - 1)
  155. return (EOF); /* Not enough space. */
  156. *p++ = (char)ch;
  157. ch = fgetc(fp);
  158. } while (1);
  159. return (ch);
  160. }
  161. static isc_result_t
  162. add_server(isc_mem_t *mctx, const char *address_str,
  163. isc_sockaddrlist_t *nameservers)
  164. {
  165. int error;
  166. isc_sockaddr_t *address = NULL;
  167. struct addrinfo hints, *res;
  168. isc_result_t result = ISC_R_SUCCESS;
  169. res = NULL;
  170. memset(&hints, 0, sizeof(hints));
  171. hints.ai_family = AF_UNSPEC;
  172. hints.ai_socktype = SOCK_DGRAM;
  173. hints.ai_protocol = IPPROTO_UDP;
  174. hints.ai_flags = AI_NUMERICHOST;
  175. error = getaddrinfo(address_str, "53", &hints, &res);
  176. if (error != 0)
  177. return (ISC_R_BADADDRESSFORM);
  178. /* XXX: special case: treat all-0 IPv4 address as loopback */
  179. if (res->ai_family == AF_INET) {
  180. struct in_addr *v4;
  181. unsigned char zeroaddress[] = {0, 0, 0, 0};
  182. unsigned char loopaddress[] = {127, 0, 0, 1};
  183. v4 = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
  184. if (memcmp(v4, zeroaddress, 4) == 0)
  185. memcpy(v4, loopaddress, 4);
  186. }
  187. address = isc_mem_get(mctx, sizeof(*address));
  188. if (address == NULL) {
  189. result = ISC_R_NOMEMORY;
  190. goto cleanup;
  191. }
  192. if (res->ai_addrlen > sizeof(address->type)) {
  193. isc_mem_put(mctx, address, sizeof(*address));
  194. result = ISC_R_RANGE;
  195. goto cleanup;
  196. }
  197. address->length = res->ai_addrlen;
  198. memcpy(&address->type.sa, res->ai_addr, res->ai_addrlen);
  199. ISC_LINK_INIT(address, link);
  200. ISC_LIST_APPEND(*nameservers, address, link);
  201. cleanup:
  202. freeaddrinfo(res);
  203. return (result);
  204. }
  205. static isc_result_t
  206. create_addr(const char *buffer, isc_netaddr_t *addr, int convert_zero) {
  207. struct in_addr v4;
  208. struct in6_addr v6;
  209. if (inet_aton(buffer, &v4) == 1) {
  210. if (convert_zero) {
  211. unsigned char zeroaddress[] = {0, 0, 0, 0};
  212. unsigned char loopaddress[] = {127, 0, 0, 1};
  213. if (memcmp(&v4, zeroaddress, 4) == 0)
  214. memcpy(&v4, loopaddress, 4);
  215. }
  216. addr->family = AF_INET;
  217. memcpy(&addr->type.in, &v4, NS_INADDRSZ);
  218. addr->zone = 0;
  219. } else if (inet_pton(AF_INET6, buffer, &v6) == 1) {
  220. addr->family = AF_INET6;
  221. memcpy(&addr->type.in6, &v6, NS_IN6ADDRSZ);
  222. addr->zone = 0;
  223. } else
  224. return (ISC_R_BADADDRESSFORM); /* Unrecognised format. */
  225. return (ISC_R_SUCCESS);
  226. }
  227. static isc_result_t
  228. resconf_parsenameserver(irs_resconf_t *conf, FILE *fp) {
  229. char word[RESCONFMAXLINELEN];
  230. int cp;
  231. isc_result_t result;
  232. if (conf->numns == RESCONFMAXNAMESERVERS)
  233. return (ISC_R_SUCCESS);
  234. cp = getword(fp, word, sizeof(word));
  235. if (strlen(word) == 0U)
  236. return (ISC_R_UNEXPECTEDEND); /* Nothing on line. */
  237. else if (cp == ' ' || cp == '\t')
  238. cp = eatwhite(fp);
  239. if (cp != EOF && cp != '\n')
  240. return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
  241. result = add_server(conf->mctx, word, &conf->nameservers);
  242. if (result != ISC_R_SUCCESS)
  243. return (result);
  244. conf->numns++;
  245. return (ISC_R_SUCCESS);
  246. }
  247. static isc_result_t
  248. resconf_parsedomain(irs_resconf_t *conf, FILE *fp) {
  249. char word[RESCONFMAXLINELEN];
  250. int res, i;
  251. res = getword(fp, word, sizeof(word));
  252. if (strlen(word) == 0U)
  253. return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
  254. else if (res == ' ' || res == '\t')
  255. res = eatwhite(fp);
  256. if (res != EOF && res != '\n')
  257. return (ISC_R_UNEXPECTEDTOKEN); /* Extra junk on line. */
  258. if (conf->domainname != NULL)
  259. isc_mem_free(conf->mctx, conf->domainname);
  260. /*
  261. * Search and domain are mutually exclusive.
  262. */
  263. for (i = 0; i < RESCONFMAXSEARCH; i++) {
  264. if (conf->search[i] != NULL) {
  265. isc_mem_free(conf->mctx, conf->search[i]);
  266. conf->search[i] = NULL;
  267. }
  268. }
  269. conf->searchnxt = 0;
  270. conf->domainname = isc_mem_strdup(conf->mctx, word);
  271. if (conf->domainname == NULL)
  272. return (ISC_R_NOMEMORY);
  273. return (ISC_R_SUCCESS);
  274. }
  275. static isc_result_t
  276. resconf_parsesearch(irs_resconf_t *conf, FILE *fp) {
  277. int idx, delim;
  278. char word[RESCONFMAXLINELEN];
  279. if (conf->domainname != NULL) {
  280. /*
  281. * Search and domain are mutually exclusive.
  282. */
  283. isc_mem_free(conf->mctx, conf->domainname);
  284. conf->domainname = NULL;
  285. }
  286. /*
  287. * Remove any previous search definitions.
  288. */
  289. for (idx = 0; idx < RESCONFMAXSEARCH; idx++) {
  290. if (conf->search[idx] != NULL) {
  291. isc_mem_free(conf->mctx, conf->search[idx]);
  292. conf->search[idx] = NULL;
  293. }
  294. }
  295. conf->searchnxt = 0;
  296. delim = getword(fp, word, sizeof(word));
  297. if (strlen(word) == 0U)
  298. return (ISC_R_UNEXPECTEDEND); /* Nothing else on line. */
  299. idx = 0;
  300. while (strlen(word) > 0U) {
  301. if (conf->searchnxt == RESCONFMAXSEARCH)
  302. goto ignore; /* Too many domains. */
  303. conf->search[idx] = isc_mem_strdup(conf->mctx, word);
  304. if (conf->search[idx] == NULL)
  305. return (ISC_R_NOMEMORY);
  306. idx++;
  307. conf->searchnxt++;
  308. ignore:
  309. if (delim == EOF || delim == '\n')
  310. break;
  311. else
  312. delim = getword(fp, word, sizeof(word));
  313. }
  314. return (ISC_R_SUCCESS);
  315. }
  316. static isc_result_t
  317. resconf_parsesortlist(irs_resconf_t *conf, FILE *fp) {
  318. int delim, res, idx;
  319. char word[RESCONFMAXLINELEN];
  320. char *p;
  321. delim = getword(fp, word, sizeof(word));
  322. if (strlen(word) == 0U)
  323. return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
  324. while (strlen(word) > 0U) {
  325. if (conf->sortlistnxt == RESCONFMAXSORTLIST)
  326. return (ISC_R_QUOTA); /* Too many values. */
  327. p = strchr(word, '/');
  328. if (p != NULL)
  329. *p++ = '\0';
  330. idx = conf->sortlistnxt;
  331. res = create_addr(word, &conf->sortlist[idx].addr, 1);
  332. if (res != ISC_R_SUCCESS)
  333. return (res);
  334. if (p != NULL) {
  335. res = create_addr(p, &conf->sortlist[idx].mask, 0);
  336. if (res != ISC_R_SUCCESS)
  337. return (res);
  338. } else {
  339. /*
  340. * Make up a mask. (XXX: is this correct?)
  341. */
  342. conf->sortlist[idx].mask = conf->sortlist[idx].addr;
  343. memset(&conf->sortlist[idx].mask.type, 0xff,
  344. sizeof(conf->sortlist[idx].mask.type));
  345. }
  346. conf->sortlistnxt++;
  347. if (delim == EOF || delim == '\n')
  348. break;
  349. else
  350. delim = getword(fp, word, sizeof(word));
  351. }
  352. return (ISC_R_SUCCESS);
  353. }
  354. static isc_result_t
  355. resconf_parseoption(irs_resconf_t *conf, FILE *fp) {
  356. int delim;
  357. long ndots;
  358. char *p;
  359. char word[RESCONFMAXLINELEN];
  360. delim = getword(fp, word, sizeof(word));
  361. if (strlen(word) == 0U)
  362. return (ISC_R_UNEXPECTEDEND); /* Empty line after keyword. */
  363. while (strlen(word) > 0U) {
  364. if (strcmp("debug", word) == 0) {
  365. conf->resdebug = 1;
  366. } else if (strncmp("ndots:", word, 6) == 0) {
  367. ndots = strtol(word + 6, &p, 10);
  368. if (*p != '\0') /* Bad string. */
  369. return (ISC_R_UNEXPECTEDTOKEN);
  370. if (ndots < 0 || ndots > 0xff) /* Out of range. */
  371. return (ISC_R_RANGE);
  372. conf->ndots = (isc_uint8_t)ndots;
  373. }
  374. if (delim == EOF || delim == '\n')
  375. break;
  376. else
  377. delim = getword(fp, word, sizeof(word));
  378. }
  379. return (ISC_R_SUCCESS);
  380. }
  381. static isc_result_t
  382. add_search(irs_resconf_t *conf, char *domain) {
  383. irs_resconf_search_t *entry;
  384. entry = isc_mem_get(conf->mctx, sizeof(*entry));
  385. if (entry == NULL)
  386. return (ISC_R_NOMEMORY);
  387. entry->domain = domain;
  388. ISC_LINK_INIT(entry, link);
  389. ISC_LIST_APPEND(conf->searchlist, entry, link);
  390. return (ISC_R_SUCCESS);
  391. }
  392. /*% parses a file and fills in the data structure. */
  393. isc_result_t
  394. irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp)
  395. {
  396. FILE *fp = NULL;
  397. char word[256];
  398. isc_result_t rval, ret;
  399. irs_resconf_t *conf;
  400. int i, stopchar;
  401. REQUIRE(mctx != NULL);
  402. REQUIRE(filename != NULL);
  403. REQUIRE(strlen(filename) > 0U);
  404. REQUIRE(confp != NULL && *confp == NULL);
  405. conf = isc_mem_get(mctx, sizeof(*conf));
  406. if (conf == NULL)
  407. return (ISC_R_NOMEMORY);
  408. conf->mctx = mctx;
  409. ISC_LIST_INIT(conf->nameservers);
  410. conf->numns = 0;
  411. conf->domainname = NULL;
  412. conf->searchnxt = 0;
  413. conf->resdebug = 0;
  414. conf->ndots = 1;
  415. for (i = 0; i < RESCONFMAXSEARCH; i++)
  416. conf->search[i] = NULL;
  417. errno = 0;
  418. if ((fp = fopen(filename, "r")) == NULL) {
  419. isc_mem_put(mctx, conf, sizeof(*conf));
  420. return (ISC_R_INVALIDFILE);
  421. }
  422. ret = ISC_R_SUCCESS;
  423. do {
  424. stopchar = getword(fp, word, sizeof(word));
  425. if (stopchar == EOF) {
  426. rval = ISC_R_SUCCESS;
  427. POST(rval);
  428. break;
  429. }
  430. if (strlen(word) == 0U)
  431. rval = ISC_R_SUCCESS;
  432. else if (strcmp(word, "nameserver") == 0)
  433. rval = resconf_parsenameserver(conf, fp);
  434. else if (strcmp(word, "domain") == 0)
  435. rval = resconf_parsedomain(conf, fp);
  436. else if (strcmp(word, "search") == 0)
  437. rval = resconf_parsesearch(conf, fp);
  438. else if (strcmp(word, "sortlist") == 0)
  439. rval = resconf_parsesortlist(conf, fp);
  440. else if (strcmp(word, "options") == 0)
  441. rval = resconf_parseoption(conf, fp);
  442. else {
  443. /* unrecognised word. Ignore entire line */
  444. rval = ISC_R_SUCCESS;
  445. stopchar = eatline(fp);
  446. if (stopchar == EOF) {
  447. break;
  448. }
  449. }
  450. if (ret == ISC_R_SUCCESS && rval != ISC_R_SUCCESS)
  451. ret = rval;
  452. } while (1);
  453. fclose(fp);
  454. /* If we don't find a nameserver fall back to localhost */
  455. if (conf->numns == 0) {
  456. INSIST(ISC_LIST_EMPTY(conf->nameservers));
  457. /* XXX: should we catch errors? */
  458. (void)add_server(conf->mctx, "127.0.0.1", &conf->nameservers);
  459. (void)add_server(conf->mctx, "::1", &conf->nameservers);
  460. }
  461. /*
  462. * Construct unified search list from domain or configured
  463. * search list
  464. */
  465. ISC_LIST_INIT(conf->searchlist);
  466. if (conf->domainname != NULL) {
  467. ret = add_search(conf, conf->domainname);
  468. } else if (conf->searchnxt > 0) {
  469. for (i = 0; i < conf->searchnxt; i++) {
  470. ret = add_search(conf, conf->search[i]);
  471. if (ret != ISC_R_SUCCESS)
  472. break;
  473. }
  474. }
  475. conf->magic = IRS_RESCONF_MAGIC;
  476. if (ret != ISC_R_SUCCESS)
  477. irs_resconf_destroy(&conf);
  478. else
  479. *confp = conf;
  480. return (ret);
  481. }
  482. void
  483. irs_resconf_destroy(irs_resconf_t **confp) {
  484. irs_resconf_t *conf;
  485. isc_sockaddr_t *address;
  486. irs_resconf_search_t *searchentry;
  487. int i;
  488. REQUIRE(confp != NULL);
  489. conf = *confp;
  490. REQUIRE(IRS_RESCONF_VALID(conf));
  491. while ((searchentry = ISC_LIST_HEAD(conf->searchlist)) != NULL) {
  492. ISC_LIST_UNLINK(conf->searchlist, searchentry, link);
  493. isc_mem_put(conf->mctx, searchentry, sizeof(*searchentry));
  494. }
  495. while ((address = ISC_LIST_HEAD(conf->nameservers)) != NULL) {
  496. ISC_LIST_UNLINK(conf->nameservers, address, link);
  497. isc_mem_put(conf->mctx, address, sizeof(*address));
  498. }
  499. if (conf->domainname != NULL)
  500. isc_mem_free(conf->mctx, conf->domainname);
  501. for (i = 0; i < RESCONFMAXSEARCH; i++) {
  502. if (conf->search[i] != NULL)
  503. isc_mem_free(conf->mctx, conf->search[i]);
  504. }
  505. isc_mem_put(conf->mctx, conf, sizeof(*conf));
  506. *confp = NULL;
  507. }
  508. isc_sockaddrlist_t *
  509. irs_resconf_getnameservers(irs_resconf_t *conf) {
  510. REQUIRE(IRS_RESCONF_VALID(conf));
  511. return (&conf->nameservers);
  512. }
  513. irs_resconf_searchlist_t *
  514. irs_resconf_getsearchlist(irs_resconf_t *conf) {
  515. REQUIRE(IRS_RESCONF_VALID(conf));
  516. return (&conf->searchlist);
  517. }
  518. unsigned int
  519. irs_resconf_getndots(irs_resconf_t *conf) {
  520. REQUIRE(IRS_RESCONF_VALID(conf));
  521. return ((unsigned int)conf->ndots);
  522. }