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