mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-11-04 05:15:22 +00:00 
			
		
		
		
	git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@5490 65c4cc65-6c06-0410-ace0-fbb531ad65f3
		
			
				
	
	
		
			206 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			206 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
/*
 | 
						|
 * DNS Support for Asterisk
 | 
						|
 *
 | 
						|
 * Written by Thorsten Lockert <tholo@trollphone.org>
 | 
						|
 *
 | 
						|
 * Funding provided by Troll Phone Networks AS
 | 
						|
 *
 | 
						|
 * This program is free software, distributed under the terms of
 | 
						|
 * the GNU General Public License
 | 
						|
 */
 | 
						|
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/socket.h>
 | 
						|
#include <netinet/in.h>
 | 
						|
#include <arpa/nameser.h>
 | 
						|
#include <resolv.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include "asterisk/logger.h"
 | 
						|
#include "asterisk/channel.h"
 | 
						|
#include "asterisk/dns.h"
 | 
						|
#include "asterisk/endian.h"
 | 
						|
 | 
						|
#define MAX_SIZE 4096
 | 
						|
 | 
						|
typedef struct {
 | 
						|
	unsigned	id :16;		/* query identification number */
 | 
						|
#if __BYTE_ORDER == __BIG_ENDIAN
 | 
						|
			/* fields in third byte */
 | 
						|
	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 */
 | 
						|
			/* fields in fourth byte */
 | 
						|
	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 */
 | 
						|
#endif
 | 
						|
#if __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __PDP_ENDIAN
 | 
						|
			/* fields in third byte */
 | 
						|
	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 */
 | 
						|
			/* fields in fourth byte */
 | 
						|
	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 */
 | 
						|
#endif
 | 
						|
			/* remaining bytes */
 | 
						|
	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 */
 | 
						|
} dns_HEADER;
 | 
						|
 | 
						|
struct dn_answer {
 | 
						|
	unsigned short rtype;
 | 
						|
	unsigned short class;
 | 
						|
	unsigned int ttl;
 | 
						|
	unsigned short size;
 | 
						|
} __attribute__ ((__packed__));
 | 
						|
 | 
						|
static int skip_name(u_char *s, int len)
 | 
						|
{
 | 
						|
	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;
 | 
						|
}
 | 
						|
 | 
						|
/*--- dns_parse_answer: Parse DNS lookup result, call callback */
 | 
						|
static int dns_parse_answer(void *context,
 | 
						|
	int class, int type, u_char *answer, int len,
 | 
						|
	int (*callback)(void *context, u_char *answer, int len, u_char *fullanswer))
 | 
						|
{
 | 
						|
	u_char *fullanswer = answer;
 | 
						|
	struct dn_answer *ans;
 | 
						|
	dns_HEADER *h;
 | 
						|
	int res;
 | 
						|
	int x;
 | 
						|
 | 
						|
	h = (dns_HEADER *)answer;
 | 
						|
	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, "Strange result size\n");
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
		if (len < 0) {
 | 
						|
			ast_log(LOG_WARNING, "Length exceeds frame\n");
 | 
						|
			return -1;
 | 
						|
		}
 | 
						|
 | 
						|
		if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
 | 
						|
			if (callback) {
 | 
						|
				if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
 | 
						|
					ast_log(LOG_WARNING, "Failed to parse result\n");
 | 
						|
					return -1;
 | 
						|
				}
 | 
						|
				if (res > 0)
 | 
						|
					return 1;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		answer += ntohs(ans->size);
 | 
						|
		len -= ntohs(ans->size);
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
#if defined(res_ninit)
 | 
						|
#define HAS_RES_NINIT
 | 
						|
#else
 | 
						|
AST_MUTEX_DEFINE_STATIC(res_lock);
 | 
						|
#if 0
 | 
						|
#warning "Warning, res_ninit is missing...  Could have reentrancy issues"
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
/*--- ast_search_dns: Lookup record in DNS */
 | 
						|
int ast_search_dns(void *context,
 | 
						|
	   const char *dname, int class, int type,
 | 
						|
	   int (*callback)(void *context, u_char *answer, int len, u_char *fullanswer))
 | 
						|
{
 | 
						|
#ifdef HAS_RES_NINIT
 | 
						|
	struct __res_state dnsstate;
 | 
						|
#endif
 | 
						|
	char answer[MAX_SIZE];
 | 
						|
	int res, ret = -1;
 | 
						|
 | 
						|
#ifdef HAS_RES_NINIT
 | 
						|
	res_ninit(&dnsstate);
 | 
						|
	res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
 | 
						|
#else
 | 
						|
	ast_mutex_lock(&res_lock);
 | 
						|
	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);
 | 
						|
			ret = -1;
 | 
						|
		}
 | 
						|
		else if (ret == 0) {
 | 
						|
			ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
 | 
						|
			ret = 0;
 | 
						|
		}
 | 
						|
		else
 | 
						|
			ret = 1;
 | 
						|
	}
 | 
						|
#ifdef HAS_RES_NINIT
 | 
						|
	res_nclose(&dnsstate);
 | 
						|
#else
 | 
						|
#ifndef __APPLE__
 | 
						|
	res_close();
 | 
						|
#endif
 | 
						|
	ast_mutex_unlock(&res_lock);
 | 
						|
#endif
 | 
						|
	return ret;
 | 
						|
}
 |