dns_naptr.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /*
  2. * Asterisk -- An open source telephony toolkit.
  3. *
  4. * Copyright (C) 2015, Digium, Inc.
  5. *
  6. * Joshua Colp <jcolp@digium.com>
  7. *
  8. * See http://www.asterisk.org for more information about
  9. * the Asterisk project. Please do not directly contact
  10. * any of the maintainers of this project for assistance;
  11. * the project provides a web site, mailing lists and IRC
  12. * channels for your use.
  13. *
  14. * This program is free software, distributed under the terms of
  15. * the GNU General Public License Version 2. See the LICENSE file
  16. * at the top of the source tree.
  17. */
  18. /*! \file
  19. *
  20. * \brief DNS NAPTR Record Support
  21. *
  22. * \author Joshua Colp <jcolp@digium.com>
  23. */
  24. /*** MODULEINFO
  25. <support_level>core</support_level>
  26. ***/
  27. #include "asterisk.h"
  28. ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
  29. #include <arpa/nameser.h>
  30. #include <resolv.h>
  31. #include <regex.h>
  32. #include "asterisk/dns_core.h"
  33. #include "asterisk/dns_naptr.h"
  34. #include "asterisk/linkedlists.h"
  35. #include "asterisk/dns_internal.h"
  36. #include "asterisk/utils.h"
  37. /*!
  38. * \brief Result of analyzing NAPTR flags on a record
  39. */
  40. enum flags_result {
  41. /*! Terminal record, meaning the DDDS algorithm can be stopped */
  42. FLAGS_TERMINAL,
  43. /*! No flags provided, likely meaning another NAPTR lookup */
  44. FLAGS_EMPTY,
  45. /*! Unrecognized but valid flags. We cannot conclude what they mean */
  46. FLAGS_UNKNOWN,
  47. /*! Non-alphanumeric or invalid combination of flags */
  48. FLAGS_INVALID,
  49. };
  50. /*!
  51. * \brief Analyze and interpret NAPTR flags as per RFC 3404
  52. *
  53. * \note The flags string passed into this function is NOT NULL-terminated
  54. *
  55. * \param flags The flags string from a NAPTR record
  56. * \flags_size The size of the flags string in bytes
  57. * \return flag result
  58. */
  59. static enum flags_result interpret_flags(const char *flags, uint8_t flags_size)
  60. {
  61. int i;
  62. char known_flag_found = 0;
  63. if (flags_size == 0) {
  64. return FLAGS_EMPTY;
  65. }
  66. /* Take care of the most common (and easy) case, one character */
  67. if (flags_size == 1) {
  68. if (*flags == 's' || *flags == 'S' ||
  69. *flags == 'a' || *flags == 'A' ||
  70. *flags == 'u' || *flags == 'U') {
  71. return FLAGS_TERMINAL;
  72. } else if (!isalnum(*flags)) {
  73. return FLAGS_INVALID;
  74. } else {
  75. return FLAGS_UNKNOWN;
  76. }
  77. }
  78. /*
  79. * Multiple flags are allowed, but you cannot mix the
  80. * S, A, U, and P flags together.
  81. */
  82. for (i = 0; i < flags_size; ++i) {
  83. if (!isalnum(flags[i])) {
  84. return FLAGS_INVALID;
  85. } else if (flags[i] == 's' || flags[i] == 'S') {
  86. if (known_flag_found && known_flag_found != 's') {
  87. return FLAGS_INVALID;
  88. }
  89. known_flag_found = 's';
  90. } else if (flags[i] == 'u' || flags[i] == 'U') {
  91. if (known_flag_found && known_flag_found != 'u') {
  92. return FLAGS_INVALID;
  93. }
  94. known_flag_found = 'u';
  95. } else if (flags[i] == 'a' || flags[i] == 'A') {
  96. if (known_flag_found && known_flag_found != 'a') {
  97. return FLAGS_INVALID;
  98. }
  99. known_flag_found = 'a';
  100. } else if (flags[i] == 'p' || flags[i] == 'P') {
  101. if (known_flag_found && known_flag_found != 'p') {
  102. return FLAGS_INVALID;
  103. }
  104. known_flag_found = 'p';
  105. }
  106. }
  107. return (!known_flag_found || known_flag_found == 'p') ? FLAGS_UNKNOWN : FLAGS_TERMINAL;
  108. }
  109. /*!
  110. * \brief Analyze NAPTR services for validity as defined by RFC 3404
  111. *
  112. * \note The services string passed to this function is NOT NULL-terminated
  113. * \param services The services string parsed from a NAPTR record
  114. * \param services_size The size of the services string
  115. * \retval 0 Services are valid
  116. * \retval -1 Services are invalid
  117. */
  118. static int services_invalid(const char *services, uint8_t services_size)
  119. {
  120. const char *current_pos = services;
  121. const char *end_of_services = services + services_size;
  122. if (services_size == 0) {
  123. return 0;
  124. }
  125. /* Services are broken into sections divided by a + sign. Each section
  126. * must start with an alphabetic character, and then can only contain
  127. * alphanumeric characters. The size of any section is limited to
  128. * 32 characters
  129. */
  130. while (1) {
  131. char *plus_pos = memchr(current_pos, '+', end_of_services - current_pos);
  132. uint8_t current_size = plus_pos ? plus_pos - current_pos : end_of_services - current_pos;
  133. int i;
  134. if (!isalpha(current_pos[0])) {
  135. return -1;
  136. }
  137. if (current_size > 32) {
  138. return -1;
  139. }
  140. for (i = 1; i < current_size; ++i) {
  141. if (!isalnum(current_pos[i])) {
  142. return -1;
  143. }
  144. }
  145. if (!plus_pos) {
  146. break;
  147. }
  148. current_pos = plus_pos + 1;
  149. }
  150. return 0;
  151. }
  152. /*!
  153. * \brief Determine if flags in the regexp are invalid
  154. *
  155. * A NAPTR regexp is structured like so
  156. * /pattern/repl/FLAGS
  157. *
  158. * This ensures that the flags on the regexp are valid. Regexp
  159. * flags can either be zero or one character long. If the flags
  160. * are one character long, that character must be "i" to indicate
  161. * the regex evaluation is case-insensitive.
  162. *
  163. * \note The flags string passed to this function is not NULL-terminated
  164. * \param flags The regexp flags from the NAPTR record
  165. * \param end A pointer to the end of the flags string
  166. * \retval 0 Flags are valid
  167. * \retval -1 Flags are invalid
  168. */
  169. static int regexp_flags_invalid(const char *flags, const char *end)
  170. {
  171. if (flags >= end) {
  172. return 0;
  173. }
  174. if (end - flags > 1) {
  175. return -1;
  176. }
  177. if (*flags != 'i') {
  178. return -1;
  179. }
  180. return 0;
  181. }
  182. /*!
  183. * \brief Determine if the replacement in the regexp is invalid
  184. *
  185. * A NAPTR regexp is structured like so
  186. * /pattern/REPL/flags
  187. *
  188. * This ensures that the replacement on the regexp is valid. The regexp
  189. * replacement is free to use any character it wants, plus backreferences
  190. * and an escaped regexp delimiter.
  191. *
  192. * This function does not attempt to ensure that the backreferences refer
  193. * to valid portions of the regexp's regex pattern.
  194. *
  195. * \note The repl string passed to this function is NOT NULL-terminated
  196. *
  197. * \param repl The regexp replacement string
  198. * \param end Pointer to the end of the replacement string
  199. * \param delim The delimiter character for the regexp
  200. *
  201. * \retval 0 Replacement is valid
  202. * \retval -1 Replacement is invalid
  203. */
  204. static int regexp_repl_invalid(const char *repl, const char *end, char delim)
  205. {
  206. const char *ptr = repl;
  207. if (repl == end) {
  208. /* Kind of weird, but this is fine */
  209. return 0;
  210. }
  211. while (1) {
  212. char *backslash_pos = memchr(ptr, '\\', end - ptr);
  213. if (!backslash_pos) {
  214. break;
  215. }
  216. ast_assert(backslash_pos < end - 1);
  217. /* XXX RFC 3402 is unclear about whether other backslash-escaped characters
  218. * (such as a backslash-escaped backslash) are legal
  219. */
  220. if (!strchr("12345689", backslash_pos[1]) && backslash_pos[1] != delim) {
  221. return -1;
  222. }
  223. ptr = backslash_pos + 1;
  224. }
  225. return 0;
  226. }
  227. /*!
  228. * \brief Determine if the pattern in a regexp is invalid
  229. *
  230. * A NAPTR regexp is structured like so
  231. * /PATTERN/repl/flags
  232. *
  233. * This ensures that the pattern on the regexp is valid. The pattern is
  234. * passed to a regex compiler to determine its validity.
  235. *
  236. * \note The pattern string passed to this function is NOT NULL-terminated
  237. *
  238. * \param pattern The pattern from the NAPTR record
  239. * \param end A pointer to the end of the pattern
  240. *
  241. * \retval 0 Pattern is valid
  242. * \retval non-zero Pattern is invalid
  243. */
  244. static int regexp_pattern_invalid(const char *pattern, const char *end)
  245. {
  246. int pattern_size = end - pattern;
  247. char pattern_str[pattern_size + 1];
  248. regex_t reg;
  249. int res;
  250. /* regcomp requires a NULL-terminated string */
  251. memcpy(pattern_str, pattern, pattern_size);
  252. pattern_str[pattern_size] = '\0';
  253. res = regcomp(&reg, pattern_str, REG_EXTENDED);
  254. regfree(&reg);
  255. return res;
  256. }
  257. /*!
  258. * \brief Determine if the regexp in a NAPTR record is invalid
  259. *
  260. * The goal of this function is to divide the regexp into its
  261. * constituent parts and then let validation subroutines determine
  262. * if each part is valid. If all parts are valid, then the entire
  263. * regexp is valid.
  264. *
  265. * \note The regexp string passed to this function is NOT NULL-terminated
  266. *
  267. * \param regexp The regexp from the NAPTR record
  268. * \param regexp_size The size of the regexp string
  269. *
  270. * \retval 0 regexp is valid
  271. * \retval non-zero regexp is invalid
  272. */
  273. static int regexp_invalid(const char *regexp, uint8_t regexp_size)
  274. {
  275. char delim;
  276. const char *delim2_pos;
  277. const char *delim3_pos;
  278. const char *ptr = regexp;
  279. const char *end_of_regexp = regexp + regexp_size;
  280. const char *regex_pos;
  281. const char *repl_pos;
  282. const char *flags_pos;
  283. if (regexp_size == 0) {
  284. return 0;
  285. }
  286. /* The delimiter will be a ! or / in most cases, but the rules allow
  287. * for the delimiter to be nearly any character. It cannot be 'i' because
  288. * the delimiter cannot be the same as regexp flags. The delimiter cannot
  289. * be 1-9 because the delimiter cannot be a backreference number. RFC
  290. * 2915 specified that backslash was also not allowed as a delimiter, but
  291. * RFC 3402 does not say this. We've gone ahead and made the character
  292. * illegal for our purposes.
  293. */
  294. delim = *ptr;
  295. if (strchr("123456789\\i", delim)) {
  296. return -1;
  297. }
  298. ++ptr;
  299. regex_pos = ptr;
  300. /* Find the other two delimiters. If the delim is escaped with a backslash, it doesn't count */
  301. while (1) {
  302. delim2_pos = memchr(ptr, delim, end_of_regexp - ptr);
  303. if (!delim2_pos) {
  304. return -1;
  305. }
  306. ptr = delim2_pos + 1;
  307. if (delim2_pos[-1] != '\\') {
  308. break;
  309. }
  310. }
  311. if (ptr >= end_of_regexp) {
  312. return -1;
  313. }
  314. repl_pos = ptr;
  315. while (1) {
  316. delim3_pos = memchr(ptr, delim, end_of_regexp - ptr);
  317. if (!delim3_pos) {
  318. return -1;
  319. }
  320. ptr = delim3_pos + 1;
  321. if (delim3_pos[-1] != '\\') {
  322. break;
  323. }
  324. }
  325. flags_pos = ptr;
  326. if (regexp_flags_invalid(flags_pos, end_of_regexp) ||
  327. regexp_repl_invalid(repl_pos, delim3_pos, delim) ||
  328. regexp_pattern_invalid(regex_pos, delim2_pos)) {
  329. return -1;
  330. }
  331. return 0;
  332. }
  333. #define PAST_END_OF_RECORD ptr >= end_of_record
  334. struct ast_dns_record *dns_naptr_alloc(struct ast_dns_query *query, const char *data, const size_t size)
  335. {
  336. struct ast_dns_naptr_record *naptr;
  337. char *ptr = NULL;
  338. uint16_t order;
  339. uint16_t preference;
  340. uint8_t flags_size;
  341. char *flags;
  342. uint8_t services_size;
  343. char *services;
  344. uint8_t regexp_size;
  345. char *regexp;
  346. char replacement[256] = "";
  347. int replacement_size;
  348. const char *end_of_record;
  349. enum flags_result flags_res;
  350. ptr = dns_find_record(data, size, query->result->answer, query->result->answer_size);
  351. ast_assert(ptr != NULL);
  352. end_of_record = ptr + size;
  353. /* ORDER */
  354. /* This assignment takes a big-endian 16-bit value and stores it in the
  355. * machine's native byte order. Using this method allows us to avoid potential
  356. * alignment issues in case the order is not on a short-addressable boundary.
  357. * See http://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html for
  358. * more information
  359. */
  360. ptr += dns_parse_short((unsigned char *) ptr, &order);
  361. if (PAST_END_OF_RECORD) {
  362. return NULL;
  363. }
  364. /* PREFERENCE */
  365. ptr += dns_parse_short((unsigned char *) ptr, &preference);
  366. if (PAST_END_OF_RECORD) {
  367. return NULL;
  368. }
  369. /* FLAGS */
  370. ptr += dns_parse_string(ptr, &flags_size, &flags);
  371. if (PAST_END_OF_RECORD) {
  372. return NULL;
  373. }
  374. /* SERVICES */
  375. ptr += dns_parse_string(ptr, &services_size, &services);
  376. if (PAST_END_OF_RECORD) {
  377. return NULL;
  378. }
  379. /* REGEXP */
  380. ptr += dns_parse_string(ptr, &regexp_size, &regexp);
  381. if (PAST_END_OF_RECORD) {
  382. return NULL;
  383. }
  384. replacement_size = dn_expand((unsigned char *)query->result->answer, (unsigned char *) end_of_record, (unsigned char *) ptr, replacement, sizeof(replacement) - 1);
  385. if (replacement_size < 0) {
  386. ast_log(LOG_ERROR, "Failed to expand domain name: %s\n", strerror(errno));
  387. return NULL;
  388. }
  389. ptr += replacement_size;
  390. if (ptr != end_of_record) {
  391. ast_log(LOG_ERROR, "NAPTR record gave undersized string indications.\n");
  392. return NULL;
  393. }
  394. /* We've validated the size of the NAPTR record. Now we can validate
  395. * the individual parts
  396. */
  397. flags_res = interpret_flags(flags, flags_size);
  398. if (flags_res == FLAGS_INVALID) {
  399. ast_log(LOG_ERROR, "NAPTR Record contained invalid flags %.*s\n", flags_size, flags);
  400. return NULL;
  401. }
  402. if (services_invalid(services, services_size)) {
  403. ast_log(LOG_ERROR, "NAPTR record contained invalid services %.*s\n", services_size, services);
  404. return NULL;
  405. }
  406. if (regexp_invalid(regexp, regexp_size)) {
  407. ast_log(LOG_ERROR, "NAPTR record contained invalid regexp %.*s\n", regexp_size, regexp);
  408. return NULL;
  409. }
  410. /* replacement_size takes into account the NULL label, so a NAPTR record with no replacement
  411. * will have a replacement_size of 1.
  412. */
  413. if (regexp_size && replacement_size > 1) {
  414. ast_log(LOG_ERROR, "NAPTR record contained both a regexp and replacement\n");
  415. return NULL;
  416. }
  417. naptr = ast_calloc(1, sizeof(*naptr) + size + flags_size + 1 + services_size + 1 + regexp_size + 1 + replacement_size + 1);
  418. if (!naptr) {
  419. return NULL;
  420. }
  421. naptr->order = order;
  422. naptr->preference = preference;
  423. ptr = naptr->data;
  424. ptr += size;
  425. strncpy(ptr, flags, flags_size);
  426. ptr[flags_size] = '\0';
  427. naptr->flags = ptr;
  428. ptr += flags_size + 1;
  429. strncpy(ptr, services, services_size);
  430. ptr[services_size] = '\0';
  431. naptr->service = ptr;
  432. ptr += services_size + 1;
  433. strncpy(ptr, regexp, regexp_size);
  434. ptr[regexp_size] = '\0';
  435. naptr->regexp = ptr;
  436. ptr += regexp_size + 1;
  437. strcpy(ptr, replacement);
  438. naptr->replacement = ptr;
  439. naptr->generic.data_ptr = naptr->data;
  440. return (struct ast_dns_record *)naptr;
  441. }
  442. static int compare_order(const void *record1, const void *record2)
  443. {
  444. const struct ast_dns_naptr_record **left = (const struct ast_dns_naptr_record **)record1;
  445. const struct ast_dns_naptr_record **right = (const struct ast_dns_naptr_record **)record2;
  446. if ((*left)->order < (*right)->order) {
  447. return -1;
  448. } else if ((*left)->order > (*right)->order) {
  449. return 1;
  450. } else {
  451. return 0;
  452. }
  453. }
  454. static int compare_preference(const void *record1, const void *record2)
  455. {
  456. const struct ast_dns_naptr_record **left = (const struct ast_dns_naptr_record **)record1;
  457. const struct ast_dns_naptr_record **right = (const struct ast_dns_naptr_record **)record2;
  458. if ((*left)->preference < (*right)->preference) {
  459. return -1;
  460. } else if ((*left)->preference > (*right)->preference) {
  461. return 1;
  462. } else {
  463. return 0;
  464. }
  465. }
  466. void dns_naptr_sort(struct ast_dns_result *result)
  467. {
  468. struct ast_dns_record *current;
  469. size_t num_records = 0;
  470. struct ast_dns_naptr_record **records;
  471. int i = 0;
  472. int j = 0;
  473. int cur_order;
  474. /* Determine the number of records */
  475. AST_LIST_TRAVERSE(&result->records, current, list) {
  476. ++num_records;
  477. }
  478. /* No point in continuing if there are no records */
  479. if (num_records == 0) {
  480. return;
  481. }
  482. /* Allocate an array with that number of records */
  483. records = ast_alloca(num_records * sizeof(*records));
  484. /* Move records from the list to the array */
  485. AST_LIST_TRAVERSE_SAFE_BEGIN(&result->records, current, list) {
  486. records[i++] = (struct ast_dns_naptr_record *) current;
  487. AST_LIST_REMOVE_CURRENT(list);
  488. }
  489. AST_LIST_TRAVERSE_SAFE_END;
  490. /* Sort the array by order */
  491. qsort(records, num_records, sizeof(*records), compare_order);
  492. /* Sort subarrays by preference */
  493. for (i = 0; i < num_records; i = j) {
  494. cur_order = records[i]->order;
  495. for (j = i + 1; j < num_records; ++j) {
  496. if (records[j]->order != cur_order) {
  497. break;
  498. }
  499. }
  500. qsort(&records[i], j - i, sizeof(*records), compare_preference);
  501. }
  502. /* Place sorted records back into the original list */
  503. for (i = 0; i < num_records; ++i) {
  504. AST_LIST_INSERT_TAIL(&result->records, (struct ast_dns_record *)(records[i]), list);
  505. }
  506. }
  507. const char *ast_dns_naptr_get_flags(const struct ast_dns_record *record)
  508. {
  509. struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
  510. ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
  511. return naptr->flags;
  512. }
  513. const char *ast_dns_naptr_get_service(const struct ast_dns_record *record)
  514. {
  515. struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
  516. ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
  517. return naptr->service;
  518. }
  519. const char *ast_dns_naptr_get_regexp(const struct ast_dns_record *record)
  520. {
  521. struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
  522. ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
  523. return naptr->regexp;
  524. }
  525. const char *ast_dns_naptr_get_replacement(const struct ast_dns_record *record)
  526. {
  527. struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
  528. ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
  529. return naptr->replacement;
  530. }
  531. unsigned short ast_dns_naptr_get_order(const struct ast_dns_record *record)
  532. {
  533. struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
  534. ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
  535. return naptr->order;
  536. }
  537. unsigned short ast_dns_naptr_get_preference(const struct ast_dns_record *record)
  538. {
  539. struct ast_dns_naptr_record *naptr = (struct ast_dns_naptr_record *) record;
  540. ast_assert(ast_dns_record_get_rr_type(record) == ns_t_naptr);
  541. return naptr->preference;
  542. }