2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006 Thorsten Lockert
6 * Written by Thorsten Lockert <tholo@trollphone.org>
8 * Funding provided by Troll Phone Networks AS
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.
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.
23 * \brief DNS Support for Asterisk
25 * \author Thorsten Lockert <tholo@trollphone.org>
28 * - DNR SRV records http://www.ietf.org/rfc/rfc2782.txt
33 <support_level>core</support_level>
38 #include "asterisk/network.h"
39 #include <arpa/nameser.h> /* res_* functions */
42 #include "asterisk/channel.h"
43 #include "asterisk/dns.h"
44 #include "asterisk/endian.h"
46 /*! \brief The maximum size permitted for the answer from the DNS server */
50 #if __BYTE_ORDER == __PDP_ENDIAN
51 #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
54 #if __BYTE_ORDER == __BIG_ENDIAN
55 #define DETERMINED_BYTE_ORDER __BIG_ENDIAN
57 #if __BYTE_ORDER == __LITTLE_ENDIAN
58 #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
61 #ifndef HAVE_RES_NINIT
62 AST_MUTEX_DEFINE_STATIC(res_lock);
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
70 * ++Copyright++ 1983, 1989, 1993
72 * Copyright (c) 1983, 1989, 1993
73 * The Regents of the University of California. All rights reserved.
75 * Redistribution and use in source and binary forms, with or without
76 * modification, are permitted provided that the following conditions
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.
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
103 * Portions Copyright (c) 1993 by Digital Equipment Corporation.
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.
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
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 */
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 */
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 */
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__));
169 * \brief Tries to find the position of the next field in the DNS response.
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.
176 * \retval The position of the next field
177 * \retval -1 if there are no remaining fields
179 static int skip_name(unsigned char *s, int len)
190 if ((*s & 0xc0) == 0xc0) {
200 /* If we are out of room to search, return failure. */
202 return AST_DNS_SEARCH_FAILURE;
205 /* Return the value for the current position in the DNS response. This is the start
206 position of the next field. */
211 * \brief Advances the position of the DNS response pointer by the size of the current field.
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.
220 * \retval The remaining length in the DNS response
221 * \retval -1 there are no frames remaining in the DNS response
223 static int dns_advance_field(unsigned char **dns_response, int remaining_len, int field_size)
225 if (dns_response == NULL || field_size < 0 || remaining_len < field_size) {
226 return AST_DNS_SEARCH_FAILURE;
229 *dns_response += field_size;
230 remaining_len -= field_size;
232 return remaining_len;
235 #ifndef HAVE_RES_NINIT
237 * \brief Handles the DNS search if the system has RES_INIT.
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.
247 * \retval The length of the DNS response
248 * \retval -1 on search failure
250 static int dns_search_res(const char *dname, int rr_class, int rr_type,
251 unsigned char *dns_response, int dns_response_len)
254 int ret = AST_DNS_SEARCH_FAILURE;
256 ast_mutex_lock(&res_lock);
258 ret = res_search(dname,
264 #ifdef HAVE_RES_CLOSE
268 ast_mutex_unlock(&res_lock);
274 * \brief Handles the DNS search if the system has RES_NINIT.
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.
284 * \retval The length of the DNS response
285 * \retval -1 on search failure
287 static int dns_search_res(const char *dname, int rr_class, int rr_type,
288 unsigned char *dns_response, int dns_response_len)
291 int ret = AST_DNS_SEARCH_FAILURE;
292 struct __res_state dns_state;
294 memset(&dns_state, 0, sizeof(dns_state));
295 res_ninit(&dns_state);
296 ret = res_nsearch(&dns_state,
303 #ifdef HAVE_RES_NDESTROY
304 res_ndestroy(&dns_state);
306 res_nclose(&dns_state);
314 * \brief Parse DNS lookup result, call callback
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.
326 * \retval -1 on search failure
327 * \retval 0 on no records found
328 * \retval 1 on success
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))
334 unsigned char *fullanswer = answer;
335 struct dn_answer *ans;
341 h = (dns_HEADER *)answer;
342 answer += sizeof(dns_HEADER);
343 len -= sizeof(dns_HEADER);
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");
350 answer += res + 4; /* Skip name and QCODE / QCLASS */
353 ast_log(LOG_WARNING, "Strange query size\n");
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");
365 ans = (struct dn_answer *)answer;
366 answer += sizeof(struct dn_answer);
367 len -= sizeof(struct dn_answer);
369 ast_log(LOG_WARNING, "Length of DNS answer exceeds frame\n");
373 if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
375 if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
376 ast_log(LOG_WARNING, "Failed to parse result\n");
382 answer += ntohs(ans->size);
383 len -= ntohs(ans->size);
389 * \brief Extended version of the DNS Parsing function.
391 * \details Parses the DNS lookup result and notifies the observer of each discovered
392 * resource record with the provided callback.
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.
405 * \retval -1 on search failure
406 * \retval 0 on no records found
407 * \retval 1 on success
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))
413 unsigned char *dns_response = answer;
414 dns_HEADER *dns_header = (dns_HEADER *)answer;
416 struct dn_answer *ans;
417 int res, x, pos, dns_response_len, ret;
419 dns_response_len = answer_len;
420 ret = AST_DNS_SEARCH_NO_RECORDS;
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));
425 /* Verify there is something to parse */
426 if (answer_len == 0) {
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;
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;
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;
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;
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;
462 /* Cast the current value for the answer pointer as a dn_answer struct */
463 ans = (struct dn_answer *) answer;
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;
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;
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));
488 * \brief Lookup record in DNS
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.
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))
497 #ifdef HAVE_RES_NINIT
498 struct __res_state dnsstate;
500 unsigned char answer[MAX_SIZE];
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));
508 ast_mutex_lock(&res_lock);
510 res = res_search(dname, class, type, answer, sizeof(answer));
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);
516 } else if (res == 0) {
517 ast_debug(1, "No matches found in DNS for %s\n", dname);
522 #ifdef HAVE_RES_NINIT
523 #ifdef HAVE_RES_NDESTROY
524 res_ndestroy(&dnsstate);
526 res_nclose(&dnsstate);
529 #ifdef HAVE_RES_CLOSE
532 ast_mutex_unlock(&res_lock);
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))
542 int ret, dns_response_len;
543 unsigned char dns_response[MAX_SIZE];
545 /* Assert that the callbacks are not NULL */
546 ast_assert(response_handler != NULL);
547 ast_assert(record_handler != NULL);
549 /* Try the DNS search. */
550 dns_response_len = dns_search_res(dname,
554 sizeof(dns_response));
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;
562 /* Parse records from DNS response */
563 ret = dns_parse_answer_ex(context,
571 /* Handle the return code from parsing the DNS response */
572 if (ret == AST_DNS_SEARCH_FAILURE) {
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);
583 struct ao2_container *ast_dns_get_nameservers(void)
585 #ifdef HAVE_RES_NINIT
586 struct __res_state dnsstate;
588 struct __res_state *state;
589 struct ao2_container *nameservers;
592 nameservers = ast_str_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 3);
597 #ifdef HAVE_RES_NINIT
598 memset(&dnsstate, 0, sizeof(dnsstate));
599 res_ninit(&dnsstate);
602 ast_mutex_lock(&res_lock);
607 for (i = 0; i < state->nscount; i++) {
608 ast_str_container_add(nameservers, ast_inet_ntoa(state->nsaddr_list[i].sin_addr));
611 #ifdef HAVE_RES_NINIT
612 #ifdef HAVE_RES_NDESTROY
613 res_ndestroy(&dnsstate);
615 res_nclose(&dnsstate);
618 #ifdef HAVE_RES_CLOSE
621 ast_mutex_unlock(&res_lock);