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