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