Skip to content
Snippets Groups Projects
dns.c 10.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
    
     * Asterisk -- An open source telephony toolkit.
     *
    
    Olle Johansson's avatar
    Olle Johansson committed
     * Copyright (C) 1999 - 2006 Thorsten Lockert
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * Written by Thorsten Lockert <tholo@trollphone.org>
     *
     * Funding provided by Troll Phone Networks AS
     *
    
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     * This program is free software, distributed under the terms of
    
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    
     * \brief DNS Support for Asterisk
    
     * \author Thorsten Lockert <tholo@trollphone.org>
    
     *
     * \par Reference
     * - DNR SRV records http://www.ietf.org/rfc/rfc2782.txt
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     */
    
    
    /*** MODULEINFO
    	<support_level>core</support_level>
     ***/
    
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    
    #include "asterisk/network.h"
    #include <arpa/nameser.h>	/* res_* functions */
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <resolv.h>
    
    
    #include "asterisk/channel.h"
    #include "asterisk/dns.h"
    #include "asterisk/endian.h"
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    #define MAX_SIZE 4096
    
    
    #ifdef __PDP_ENDIAN
    #if __BYTE_ORDER == __PDP_ENDIAN
    #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
    #endif
    #endif
    #if __BYTE_ORDER == __BIG_ENDIAN
    #define DETERMINED_BYTE_ORDER __BIG_ENDIAN
    #endif
    #if __BYTE_ORDER == __LITTLE_ENDIAN
    #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
    #endif
    
    
    /* The dns_HEADER structure definition below originated
       in the arpa/nameser.h header file distributed with ISC
       BIND, which contains the following copyright and license
       notices:
    
     * ++Copyright++ 1983, 1989, 1993
     * -
     * Copyright (c) 1983, 1989, 1993
     *    The Regents of the University of California.  All rights reserved.
    
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. All advertising materials mentioning features or use of this software
     *    must display the following acknowledgement:
    
     *	This product includes software developed by the University of
     *	California, Berkeley and its contributors.
    
     * 4. Neither the name of the University nor the names of its contributors
     *    may be used to endorse or promote products derived from this software
     *    without specific prior written permission.
    
     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     * -
     * Portions Copyright (c) 1993 by Digital Equipment Corporation.
    
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies, and that
     * the name of Digital Equipment Corporation not be used in advertising or
     * publicity pertaining to distribution of the document or software without
     * specific, written prior permission.
    
     * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
     * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
     * OF MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
     * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
     * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
     * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
     * SOFTWARE.
     * -
     * --Copyright--
     */
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    typedef struct {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	unsigned	id:16;          /*!< query identification number */
    
    #if DETERMINED_BYTE_ORDER == __BIG_ENDIAN
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* fields in third byte */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	unsigned	qr:1;           /*!< response flag */
    	unsigned	opcode:4;       /*!< purpose of message */
    	unsigned	aa:1;           /*!< authoritive answer */
    	unsigned	tc:1;           /*!< truncated message */
    	unsigned	rd:1;           /*!< recursion desired */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* fields in fourth byte */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	unsigned	ra:1;           /*!< recursion available */
    	unsigned	unused:1;       /*!< unused bits (MBZ as of 4.9.3a3) */
    	unsigned	ad:1;           /*!< authentic data from named */
    	unsigned	cd:1;           /*!< checking disabled by resolver */
    	unsigned	rcode:4;        /*!< response code */
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    #if DETERMINED_BYTE_ORDER == __LITTLE_ENDIAN
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* fields in third byte */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	unsigned	rd:1;           /*!< recursion desired */
    	unsigned	tc:1;           /*!< truncated message */
    	unsigned	aa:1;           /*!< authoritive answer */
    	unsigned	opcode:4;       /*!< purpose of message */
    	unsigned	qr:1;           /*!< response flag */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* fields in fourth byte */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	unsigned	rcode:4;        /*!< response code */
    	unsigned	cd:1;           /*!< checking disabled by resolver */
    	unsigned	ad:1;           /*!< authentic data from named */
    	unsigned	unused:1;       /*!< unused bits (MBZ as of 4.9.3a3) */
    	unsigned	ra:1;           /*!< recursion available */
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    			/* remaining bytes */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	unsigned	qdcount:16;     /*!< number of question entries */
    	unsigned	ancount:16;     /*!< number of answer entries */
    	unsigned	nscount:16;     /*!< number of authority entries */
    	unsigned	arcount:16;     /*!< number of resource entries */
    
    Mark Spencer's avatar
    Mark Spencer committed
    } dns_HEADER;
    
    struct dn_answer {
    	unsigned short rtype;
    	unsigned short class;
    	unsigned int ttl;
    	unsigned short size;
    
    } __attribute__((__packed__));
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    
    static int skip_name(unsigned char *s, int len)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int x = 0;
    
    	while (x < len) {
    		if (*s == '\0') {
    			s++;
    			x++;
    			break;
    		}
    		if ((*s & 0xc0) == 0xc0) {
    			s += 2;
    			x += 2;
    			break;
    		}
    		x += *s + 1;
    		s += *s + 1;
    	}
    	if (x >= len)
    		return -1;
    	return x;
    }
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /*! \brief Parse DNS lookup result, call callback */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int dns_parse_answer(void *context,
    
    	int class, int type, unsigned char *answer, int len,
    	int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	unsigned char *fullanswer = answer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct dn_answer *ans;
    	dns_HEADER *h;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    	int x;
    
    
    James Golovich's avatar
    James Golovich committed
    	h = (dns_HEADER *)answer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	answer += sizeof(dns_HEADER);
    	len -= sizeof(dns_HEADER);
    
    	for (x = 0; x < ntohs(h->qdcount); x++) {
    		if ((res = skip_name(answer, len)) < 0) {
    			ast_log(LOG_WARNING, "Couldn't skip over name\n");
    			return -1;
    		}
    		answer += res + 4;	/* Skip name and QCODE / QCLASS */
    		len -= res + 4;
    		if (len < 0) {
    			ast_log(LOG_WARNING, "Strange query size\n");
    			return -1;
    		}
    	}
    
    	for (x = 0; x < ntohs(h->ancount); x++) {
    		if ((res = skip_name(answer, len)) < 0) {
    			ast_log(LOG_WARNING, "Failed skipping name\n");
    			return -1;
    		}
    		answer += res;
    		len -= res;
    		ans = (struct dn_answer *)answer;
    		answer += sizeof(struct dn_answer);
    		len -= sizeof(struct dn_answer);
    		if (len < 0) {
    
    			ast_log(LOG_WARNING, "Length of DNS answer exceeds frame\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    
    		if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
    			if (callback) {
    
    				if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_log(LOG_WARNING, "Failed to parse result\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    		}
    		answer += ntohs(ans->size);
    		len -= ntohs(ans->size);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    
    AST_MUTEX_DEFINE_STATIC(res_lock);
    
    /*! \brief Lookup record in DNS
    
    Olle Johansson's avatar
    Olle Johansson committed
    \note Asterisk DNS is synchronus at this time. This means that if your DNS does
    not work properly, Asterisk might not start properly or a channel may lock.
    */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_search_dns(void *context,
    
    	   const char *dname, int class, int type,
    
    	   int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct __res_state dnsstate;
    #endif
    
    	unsigned char answer[MAX_SIZE];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res, ret = -1;
    
    
    	memset(&dnsstate, 0, sizeof(dnsstate));
    
    	res_ninit(&dnsstate);
    
    	res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
    
    Mark Spencer's avatar
    Mark Spencer committed
    #else
    
    	ast_mutex_lock(&res_lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res_init();
    	res = res_search(dname, class, type, answer, sizeof(answer));
    #endif
    	if (res > 0) {
    		if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
    
    			ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ret = -1;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		} else if (res == 0) {
    
    			ast_debug(1, "No matches found in DNS for %s\n", dname);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ret = 0;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    		} else
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ret = 1;
    	}
    
    #ifdef HAVE_RES_NDESTROY
    
    	res_ndestroy(&dnsstate);
    #else
    
    	res_nclose(&dnsstate);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #else
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res_close();
    
    	ast_mutex_unlock(&res_lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ret;
    }
    
    
    struct ao2_container *ast_dns_get_nameservers(void)
    {
    #ifdef HAVE_RES_NINIT
    	struct __res_state dnsstate;
    #endif
    	struct __res_state *state;
    	struct ao2_container *nameservers;
    	int i;
    
    	nameservers = ast_str_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 3);
    	if (!nameservers) {
    		return NULL;
    	}
    
    #ifdef HAVE_RES_NINIT
    	memset(&dnsstate, 0, sizeof(dnsstate));
    	res_ninit(&dnsstate);
    	state = &dnsstate;
    #else
    	ast_mutex_lock(&res_lock);
    	res_init();
    	state = &_res;
    #endif
    
    	for (i = 0; i < state->nscount; i++) {
    		ast_str_container_add(nameservers, ast_inet_ntoa(state->nsaddr_list[i].sin_addr));
    	}
    
    #ifdef HAVE_RES_NINIT
    #ifdef HAVE_RES_NDESTROY
    	res_ndestroy(&dnsstate);
    #else
    	res_nclose(&dnsstate);
    #endif
    #else
    #ifdef HAVE_RES_CLOSE
    	res_close();
    #endif
    	ast_mutex_unlock(&res_lock);
    #endif
    
    	return nameservers;