dns: Use ntohl for ans->ttl in dns_parse_answer_ex
[asterisk/asterisk.git] / main / dns.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006 Thorsten Lockert
5  *
6  * Written by Thorsten Lockert <tholo@trollphone.org>
7  *
8  * Funding provided by Troll Phone Networks AS
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20
21 /*! \file
22  *
23  * \brief DNS Support for Asterisk
24  *
25  * \author Thorsten Lockert <tholo@trollphone.org>
26  *
27  * \par Reference
28  * - DNR SRV records http://www.ietf.org/rfc/rfc2782.txt
29  *
30  */
31
32 /*** MODULEINFO
33         <support_level>core</support_level>
34  ***/
35
36 #include "asterisk.h"
37
38 ASTERISK_REGISTER_FILE()
39
40 #include "asterisk/network.h"
41 #include <arpa/nameser.h>       /* res_* functions */
42 #include <resolv.h>
43
44 #include "asterisk/channel.h"
45 #include "asterisk/dns.h"
46 #include "asterisk/endian.h"
47
48 /*! \brief The maximum size permitted for the answer from the DNS server */
49 #define MAX_SIZE 4096
50
51 #ifdef __PDP_ENDIAN
52 #if __BYTE_ORDER == __PDP_ENDIAN
53 #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
54 #endif
55 #endif
56 #if __BYTE_ORDER == __BIG_ENDIAN
57 #define DETERMINED_BYTE_ORDER __BIG_ENDIAN
58 #endif
59 #if __BYTE_ORDER == __LITTLE_ENDIAN
60 #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
61 #endif
62
63 #ifndef HAVE_RES_NINIT
64 AST_MUTEX_DEFINE_STATIC(res_lock);
65 #endif
66
67 /* The dns_HEADER structure definition below originated
68    in the arpa/nameser.h header file distributed with ISC
69    BIND, which contains the following copyright and license
70    notices:
71
72  * ++Copyright++ 1983, 1989, 1993
73  * -
74  * Copyright (c) 1983, 1989, 1993
75  *    The Regents of the University of California.  All rights reserved.
76  *
77  * Redistribution and use in source and binary forms, with or without
78  * modification, are permitted provided that the following conditions
79  * are met:
80  * 1. Redistributions of source code must retain the above copyright
81  *    notice, this list of conditions and the following disclaimer.
82  * 2. Redistributions in binary form must reproduce the above copyright
83  *    notice, this list of conditions and the following disclaimer in the
84  *    documentation and/or other materials provided with the distribution.
85  * 3. All advertising materials mentioning features or use of this software
86  *    must display the following acknowledgement:
87  *      This product includes software developed by the University of
88  *      California, Berkeley and its contributors.
89  * 4. Neither the name of the University nor the names of its contributors
90  *    may be used to endorse or promote products derived from this software
91  *    without specific prior written permission.
92  *
93  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
94  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
95  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
96  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
97  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
98  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
99  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
100  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
101  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
102  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
103  * SUCH DAMAGE.
104  * -
105  * Portions Copyright (c) 1993 by Digital Equipment Corporation.
106  *
107  * Permission to use, copy, modify, and distribute this software for any
108  * purpose with or without fee is hereby granted, provided that the above
109  * copyright notice and this permission notice appear in all copies, and that
110  * the name of Digital Equipment Corporation not be used in advertising or
111  * publicity pertaining to distribution of the document or software without
112  * specific, written prior permission.
113  *
114  * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
115  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
116  * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
117  * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
118  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
119  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
120  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
121  * SOFTWARE.
122  * -
123  * --Copyright--
124  */
125
126 typedef struct {
127         unsigned        id:16;          /*!< query identification number */
128 #if DETERMINED_BYTE_ORDER == __BIG_ENDIAN
129                         /* fields in third byte */
130         unsigned        qr:1;           /*!< response flag */
131         unsigned        opcode:4;       /*!< purpose of message */
132         unsigned        aa:1;           /*!< authoritive answer */
133         unsigned        tc:1;           /*!< truncated message */
134         unsigned        rd:1;           /*!< recursion desired */
135                         /* fields in fourth byte */
136         unsigned        ra:1;           /*!< recursion available */
137         unsigned        unused:1;       /*!< unused bits (MBZ as of 4.9.3a3) */
138         unsigned        ad:1;           /*!< authentic data from named */
139         unsigned        cd:1;           /*!< checking disabled by resolver */
140         unsigned        rcode:4;        /*!< response code */
141 #endif
142 #if DETERMINED_BYTE_ORDER == __LITTLE_ENDIAN
143                         /* fields in third byte */
144         unsigned        rd:1;           /*!< recursion desired */
145         unsigned        tc:1;           /*!< truncated message */
146         unsigned        aa:1;           /*!< authoritive answer */
147         unsigned        opcode:4;       /*!< purpose of message */
148         unsigned        qr:1;           /*!< response flag */
149                         /* fields in fourth byte */
150         unsigned        rcode:4;        /*!< response code */
151         unsigned        cd:1;           /*!< checking disabled by resolver */
152         unsigned        ad:1;           /*!< authentic data from named */
153         unsigned        unused:1;       /*!< unused bits (MBZ as of 4.9.3a3) */
154         unsigned        ra:1;           /*!< recursion available */
155 #endif
156                         /* remaining bytes */
157         unsigned        qdcount:16;     /*!< number of question entries */
158         unsigned        ancount:16;     /*!< number of answer entries */
159         unsigned        nscount:16;     /*!< number of authority entries */
160         unsigned        arcount:16;     /*!< number of resource entries */
161 } dns_HEADER;
162
163 struct dn_answer {
164         unsigned short rtype;       /*!< The resource record type. */
165         unsigned short class;       /*!< The resource record class. */
166         unsigned int ttl;           /*!< The resource record time to live. */
167         unsigned short size;        /*!< The resource record size. */
168 } __attribute__((__packed__));
169
170 /*!
171  * \brief Tries to find the position of the next field in the DNS response.
172  *
173  * \internal
174  *
175  * \param  s    A char pointer to the current frame in the DNS response.
176  * \param  len  The remaining available length of the DNS response.
177  *
178  * \retval The position of the next field
179  * \retval -1 if there are no remaining fields
180  */
181 static int skip_name(unsigned char *s, int len)
182 {
183         int x = 0;
184
185         while (x < len) {
186                 if (*s == '\0') {
187                         s++;
188                         x++;
189                         break;
190                 }
191
192                 if ((*s & 0xc0) == 0xc0) {
193                         s += 2;
194                         x += 2;
195                         break;
196                 }
197
198                 x += *s + 1;
199                 s += *s + 1;
200         }
201
202         /* If we are out of room to search, return failure. */
203         if (x >= len) {
204                 return AST_DNS_SEARCH_FAILURE;
205         }
206
207         /* Return the value for the current position in the DNS response. This is the start
208         position of the next field. */
209         return x;
210 }
211
212 /*!
213  * \brief Advances the position of the DNS response pointer by the size of the current field.
214  *
215  * \internal
216  *
217  * \param  dns_response   A pointer to a char pointer to the current field in the DNS response.
218  * \param  remaining_len  The remaining available length in the DNS response to search.
219  * \param  field_size     A positive value representing the size of the current field
220                           pointed to by the dns_response parameter.
221  *
222  * \retval The remaining length in the DNS response
223  * \retval -1 there are no frames remaining in the DNS response
224  */
225 static int dns_advance_field(unsigned char **dns_response, int remaining_len, int field_size)
226 {
227         if (dns_response == NULL || field_size < 0 || remaining_len < field_size) {
228                 return AST_DNS_SEARCH_FAILURE;
229         }
230
231         *dns_response += field_size;
232         remaining_len -= field_size;
233
234         return remaining_len;
235 }
236
237 #ifndef HAVE_RES_NINIT
238 /*!
239  * \brief Handles the DNS search if the system has RES_INIT.
240  *
241  * \internal
242  *
243  * \param  dname             Domain name to lookup (host, SRV domain, TXT record name).
244  * \param  rr_class          Record Class (see "man res_search").
245  * \param  rr_type           Record type (see "man res_search").
246  * \param  dns_response      The full DNS response.
247  * \param  dns_response_len  The length of the full DNS response.
248  *
249  * \retval The length of the DNS response
250  * \retval -1 on search failure
251  */
252 static int dns_search_res(const char *dname, int rr_class, int rr_type,
253         unsigned char *dns_response, int dns_response_len)
254 {
255
256         int ret = AST_DNS_SEARCH_FAILURE;
257         struct __res_state dns_state;
258
259         ast_mutex_lock(&res_lock);
260         res_init();
261         ret = res_search(&dns_state,
262                          dname,
263                          rr_class,
264                          rr_type,
265                          dns_response,
266                          dns_response_len);
267
268 #ifdef HAVE_RES_CLOSE
269         res_close();
270 #endif
271
272         ast_mutex_unlock(&res_lock);
273
274         return ret;
275 }
276 #else
277 /*!
278  * \brief Handles the DNS search if the system has RES_NINIT.
279  *
280  * \internal
281  *
282  * \param  dname             Domain name to lookup (host, SRV domain, TXT record name).
283  * \param  rr_class          Record Class (see "man res_search").
284  * \param  rr_type           Record type (see "man res_search").
285  * \param  dns_response      The full DNS response.
286  * \param  dns_response_len  The length of the full DNS response.
287  *
288  * \retval The length of the DNS response
289  * \retval -1 on search failure
290  */
291 static int dns_search_res(const char *dname, int rr_class, int rr_type,
292         unsigned char *dns_response, int dns_response_len)
293 {
294
295         int ret = AST_DNS_SEARCH_FAILURE;
296         struct __res_state dns_state;
297
298         memset(&dns_state, 0, sizeof(dns_state));
299         res_ninit(&dns_state);
300         ret = res_nsearch(&dns_state,
301                           dname,
302                           rr_class,
303                           rr_type,
304                           dns_response,
305                           dns_response_len);
306
307 #ifdef HAVE_RES_NDESTROY
308         res_ndestroy(&dns_state);
309 #else
310         res_nclose(&dns_state);
311 #endif
312
313         return ret;
314 }
315 #endif
316
317 /*!
318  * \brief Parse DNS lookup result, call callback
319  *
320  * \internal
321  *
322  * \param  context   Void pointer containing data to use in the callback functions.
323  * \param  dname     Domain name to lookup (host, SRV domain, TXT record name).
324  * \param  class     Record Class (see "man res_search").
325  * \param  type      Record type (see "man res_search").
326  * \param  answer    The full DNS response.
327  * \param  len       The length of the full DNS response.
328  * \param  callback  Callback function for handling the discovered resource records from the DNS search.
329  *
330  * \retval -1 on search failure
331  * \retval  0 on no records found
332  * \retval  1 on success
333  */
334 static int dns_parse_answer(void *context,
335         int class, int type, unsigned char *answer, int len,
336         int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
337 {
338         unsigned char *fullanswer = answer;
339         struct dn_answer *ans;
340         dns_HEADER *h;
341         int ret = 0;
342         int res;
343         int x;
344
345         h = (dns_HEADER *)answer;
346         answer += sizeof(dns_HEADER);
347         len -= sizeof(dns_HEADER);
348
349         for (x = 0; x < ntohs(h->qdcount); x++) {
350                 if ((res = skip_name(answer, len)) < 0) {
351                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
352                         return -1;
353                 }
354                 answer += res + 4;      /* Skip name and QCODE / QCLASS */
355                 len -= res + 4;
356                 if (len < 0) {
357                         ast_log(LOG_WARNING, "Strange query size\n");
358                         return -1;
359                 }
360         }
361
362         for (x = 0; x < ntohs(h->ancount); x++) {
363                 if ((res = skip_name(answer, len)) < 0) {
364                         ast_log(LOG_WARNING, "Failed skipping name\n");
365                         return -1;
366                 }
367                 answer += res;
368                 len -= res;
369                 ans = (struct dn_answer *)answer;
370                 answer += sizeof(struct dn_answer);
371                 len -= sizeof(struct dn_answer);
372                 if (len < 0) {
373                         ast_log(LOG_WARNING, "Length of DNS answer exceeds frame\n");
374                         return -1;
375                 }
376
377                 if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
378                         if (callback) {
379                                 if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
380                                         ast_log(LOG_WARNING, "Failed to parse result\n");
381                                         return -1;
382                                 }
383                                 ret = 1;
384                         }
385                 }
386                 answer += ntohs(ans->size);
387                 len -= ntohs(ans->size);
388         }
389         return ret;
390 }
391
392 /*!
393  * \brief Extended version of the DNS Parsing function.
394  *
395  * \details Parses the DNS lookup result and notifies the observer of each discovered
396  *          resource record with the provided callback.
397  *
398  * \internal
399  *
400  * \param  context           Void pointer containing data to use in the callback functions.
401  * \param  dname             Domain name to lookup (host, SRV domain, TXT record name).
402  * \param  rr_class          Record Class (see "man res_search").
403  * \param  rr_type           Record type (see "man res_search").
404  * \param  answer            The full DNS response.
405  * \param  answer_len        The length of the full DNS response.
406  * \param  response_handler  Callback function for handling the DNS response.
407  * \param  record_handler    Callback function for handling the discovered resource records from the DNS search.
408  *
409  * \retval -1 on search failure
410  * \retval  0 on no records found
411  * \retval  1 on success
412  */
413 static int dns_parse_answer_ex(void *context, int rr_class, int rr_type, unsigned char *answer, int answer_len,
414         int (*response_handler)(void *context, unsigned char *dns_response, int dns_response_len, int rcode),
415         int (*record_handler)(void *context, unsigned char *record, int record_len, int ttl))
416 {
417         unsigned char *dns_response = answer;
418         dns_HEADER *dns_header = (dns_HEADER *)answer;
419
420         struct dn_answer *ans;
421         int res, x, pos, dns_response_len, ret;
422
423         dns_response_len = answer_len;
424         ret = AST_DNS_SEARCH_NO_RECORDS;
425
426         /* Invoke the response_handler callback to notify the observer of the raw DNS response */
427         response_handler(context, dns_response, dns_response_len, ntohs(dns_header->rcode));
428
429         /* Verify there is something to parse */
430         if (answer_len == 0) {
431                 return ret;
432         }
433
434         /* Try advancing the cursor for the dns header */
435         if ((pos = dns_advance_field(&answer, answer_len, sizeof(dns_HEADER))) < 0) {
436                 ast_log(LOG_WARNING, "Length of DNS answer exceeds available search frames\n");
437                 return AST_DNS_SEARCH_FAILURE;
438         }
439
440         /* Skip domain name and QCODE / QCLASS */
441         for (x = 0; x < ntohs(dns_header->qdcount); x++) {
442                 if ((res = skip_name(answer, pos)) < 0) {
443                         ast_log(LOG_WARNING, "Failed skipping name\n");
444                         return AST_DNS_SEARCH_FAILURE;
445                 }
446
447                 /* Try advancing the cursor for the name and QCODE / QCLASS fields */
448                 if ((pos = dns_advance_field(&answer, pos, res + 4)) < 0) {
449                         return AST_DNS_SEARCH_FAILURE;
450                 }
451         }
452
453         /* Extract the individual records */
454         for (x = 0; x < ntohs(dns_header->ancount); x++) {
455                 if ((res = skip_name(answer, pos)) < 0) {
456                         ast_log(LOG_WARNING, "Failed skipping name\n");
457                         return AST_DNS_SEARCH_FAILURE;
458                 }
459
460                 /* Try advancing the cursor to the current record */
461                 if ((pos = dns_advance_field(&answer, pos, res)) < 0) {
462                         ast_log(LOG_WARNING, "Length of DNS answer exceeds available search frames\n");
463                         return AST_DNS_SEARCH_FAILURE;
464                 }
465
466                 /* Cast the current value for the answer pointer as a dn_answer struct */
467                 ans = (struct dn_answer *) answer;
468
469                 /* Try advancing the cursor to the end of the current record  */
470                 if ((pos = dns_advance_field(&answer, pos, sizeof(struct dn_answer)))  < 0) {
471                         ast_log(LOG_WARNING, "Length of DNS answer exceeds available search frames\n");
472                         return AST_DNS_SEARCH_FAILURE;
473                 }
474
475                 /* Skip over the records that do not have the same resource record class and type we care about */
476                 if (ntohs(ans->class) == rr_class && ntohs(ans->rtype) == rr_type) {
477                         /* Invoke the record handler callback to deliver the discovered record */
478                         record_handler(context, answer, ntohs(ans->size), ntohl(ans->ttl));
479                         /*At least one record was found */
480                         ret = AST_DNS_SEARCH_SUCCESS;
481                 }
482
483                 /* Try and update the field to the next record, but ignore any errors that come
484                  * back because this may be the end of the line. */
485                 pos = dns_advance_field(&answer, pos, res + ntohs(ans->size));
486         }
487
488         return ret;
489 }
490
491 /*!
492  * \brief Lookup record in DNS
493  *
494  * \note Asterisk DNS is synchronus at this time. This means that if your DNS does not
495  *       work properly, Asterisk might not start properly or a channel may lock.
496 */
497 int ast_search_dns(void *context,
498            const char *dname, int class, int type,
499            int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
500 {
501 #ifdef HAVE_RES_NINIT
502         struct __res_state dnsstate;
503 #endif
504         unsigned char answer[MAX_SIZE];
505         int res, ret = -1;
506
507 #ifdef HAVE_RES_NINIT
508         memset(&dnsstate, 0, sizeof(dnsstate));
509         res_ninit(&dnsstate);
510         res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
511 #else
512         ast_mutex_lock(&res_lock);
513         res_init();
514         res = res_search(dname, class, type, answer, sizeof(answer));
515 #endif
516         if (res > 0) {
517                 if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
518                         ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
519                         ret = -1;
520                 } else if (res == 0) {
521                         ast_debug(1, "No matches found in DNS for %s\n", dname);
522                         ret = 0;
523                 } else
524                         ret = 1;
525         }
526 #ifdef HAVE_RES_NINIT
527 #ifdef HAVE_RES_NDESTROY
528         res_ndestroy(&dnsstate);
529 #else
530         res_nclose(&dnsstate);
531 #endif
532 #else
533 #ifdef HAVE_RES_CLOSE
534         res_close();
535 #endif
536         ast_mutex_unlock(&res_lock);
537 #endif
538
539         return ret;
540 }
541
542 enum ast_dns_search_result ast_search_dns_ex(void *context, const char *dname, int rr_class, int rr_type,
543            int (*response_handler)(void *context, unsigned char *dns_response, int dns_response_len, int rcode),
544            int (*record_handler)(void *context, unsigned char *record, int record_len, int ttl))
545 {
546         int ret, dns_response_len;
547         unsigned char dns_response[MAX_SIZE];
548
549         /* Assert that the callbacks are not NULL */
550         ast_assert(response_handler != NULL);
551         ast_assert(record_handler != NULL);
552
553         /* Try the DNS search. */
554         dns_response_len = dns_search_res(dname,
555                                           rr_class,
556                                           rr_type,
557                                           dns_response,
558                                           sizeof(dns_response));
559
560         if (dns_response_len < 0) {
561                 ast_log(LOG_ERROR, "DNS search failed for %s\n", dname);
562                 response_handler(context, (unsigned char *)"", 0, ns_r_nxdomain);
563                 return AST_DNS_SEARCH_FAILURE;
564         }
565
566         /* Parse records from DNS response */
567         ret = dns_parse_answer_ex(context,
568                                   rr_class,
569                                   rr_type,
570                                   dns_response,
571                                   dns_response_len,
572                                   response_handler,
573                                   record_handler);
574
575         /* Handle the return code from parsing the DNS response */
576         if (ret == AST_DNS_SEARCH_FAILURE) {
577                 /* Parsing Error */
578                 ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
579         } else if (ret == AST_DNS_SEARCH_NO_RECORDS) {
580                 /* No results found */
581                 ast_debug(1, "DNS search yielded no results for %s\n", dname);
582         }
583
584         return ret;
585 }
586
587 struct ao2_container *ast_dns_get_nameservers(void)
588 {
589 #ifdef HAVE_RES_NINIT
590         struct __res_state dnsstate;
591 #endif
592         struct __res_state *state;
593         struct ao2_container *nameservers;
594         int i;
595
596         nameservers = ast_str_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 3);
597         if (!nameservers) {
598                 return NULL;
599         }
600
601 #ifdef HAVE_RES_NINIT
602         memset(&dnsstate, 0, sizeof(dnsstate));
603         res_ninit(&dnsstate);
604         state = &dnsstate;
605 #else
606         ast_mutex_lock(&res_lock);
607         res_init();
608         state = &_res;
609 #endif
610
611         for (i = 0; i < state->nscount; i++) {
612                 ast_str_container_add(nameservers, ast_inet_ntoa(state->nsaddr_list[i].sin_addr));
613         }
614
615 #ifdef HAVE_RES_NINIT
616 #ifdef HAVE_RES_NDESTROY
617         res_ndestroy(&dnsstate);
618 #else
619         res_nclose(&dnsstate);
620 #endif
621 #else
622 #ifdef HAVE_RES_CLOSE
623         res_close();
624 #endif
625         ast_mutex_unlock(&res_lock);
626 #endif
627
628         return nameservers;
629 }
630