Files
asterisk/dns.c
T

232 lines
5.8 KiB
C
Raw Normal View History

2003-09-27 00:37:07 +00:00
/*
* Asterisk -- An open source telephony toolkit.
*
2006-01-05 09:40:22 +00:00
* Copyright (C) 1999 - 2006 Thorsten Lockert
2003-09-27 00:37:07 +00:00
*
* 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.
*
2003-09-27 00:37:07 +00:00
* 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.
*/
/*! \file
*
* \brief DNS Support for Asterisk
*
* \author Thorsten Lockert <tholo@trollphone.org>
2003-09-27 00:37:07 +00:00
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include <unistd.h>
2005-06-06 20:27:51 +00:00
#include "asterisk.h"
2005-06-06 22:12:19 +00:00
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
2005-06-06 20:27:51 +00:00
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/dns.h"
#include "asterisk/endian.h"
2003-09-27 00:37:07 +00:00
#define MAX_SIZE 4096
typedef struct {
2006-01-05 09:40:22 +00:00
unsigned id :16; /*!< query identification number */
#if __BYTE_ORDER == __BIG_ENDIAN
2003-09-27 00:37:07 +00:00
/* fields in third byte */
2006-01-05 09:40:22 +00:00
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 */
2003-09-27 00:37:07 +00:00
/* fields in fourth byte */
2006-01-05 09:40:22 +00:00
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 */
2003-09-27 00:37:07 +00:00
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __PDP_ENDIAN
2003-09-27 00:37:07 +00:00
/* fields in third byte */
2006-01-05 09:40:22 +00:00
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 */
2003-09-27 00:37:07 +00:00
/* fields in fourth byte */
2006-01-05 09:40:22 +00:00
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 */
2003-09-27 00:37:07 +00:00
#endif
/* remaining bytes */
2006-01-05 09:40:22 +00:00
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 */
2003-09-27 00:37:07 +00:00
} dns_HEADER;
struct dn_answer {
unsigned short rtype;
unsigned short class;
unsigned int ttl;
unsigned short size;
} __attribute__ ((__packed__));
2005-08-07 06:34:26 +00:00
static int skip_name(char *s, int len)
2003-09-27 00:37:07 +00:00
{
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;
}
2006-01-05 09:40:22 +00:00
/*! \brief Parse DNS lookup result, call callback */
2003-09-27 00:37:07 +00:00
static int dns_parse_answer(void *context,
2005-08-07 06:34:26 +00:00
int class, int type, char *answer, int len,
int (*callback)(void *context, char *answer, int len, char *fullanswer))
2003-09-27 00:37:07 +00:00
{
2005-08-07 06:34:26 +00:00
char *fullanswer = answer;
2003-09-27 00:37:07 +00:00
struct dn_answer *ans;
dns_HEADER *h;
int res;
int x;
2004-06-22 20:11:15 +00:00
h = (dns_HEADER *)answer;
2003-09-27 00:37:07 +00:00
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) {
2003-09-27 00:37:07 +00:00
ast_log(LOG_WARNING, "Failed to parse result\n");
return -1;
}
2003-09-27 00:37:07 +00:00
if (res > 0)
return 1;
}
}
answer += ntohs(ans->size);
len -= ntohs(ans->size);
}
return 0;
}
2004-04-26 13:32:57 +00:00
#if defined(res_ninit)
#define HAS_RES_NINIT
#else
2004-06-09 01:45:08 +00:00
AST_MUTEX_DEFINE_STATIC(res_lock);
#if 0
2004-04-26 13:32:57 +00:00
#warning "Warning, res_ninit is missing... Could have reentrancy issues"
#endif
#endif
2004-04-26 13:32:57 +00:00
2006-01-05 09:40:22 +00:00
/*! \brief Lookup record in DNS
\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.
*/
2003-09-27 00:37:07 +00:00
int ast_search_dns(void *context,
2005-03-02 05:17:13 +00:00
const char *dname, int class, int type,
2005-08-07 06:34:26 +00:00
int (*callback)(void *context, char *answer, int len, char *fullanswer))
2003-09-27 00:37:07 +00:00
{
2004-04-26 13:32:57 +00:00
#ifdef HAS_RES_NINIT
2003-09-27 00:37:07 +00:00
struct __res_state dnsstate;
#endif
char answer[MAX_SIZE];
int res, ret = -1;
2004-04-26 13:32:57 +00:00
#ifdef HAS_RES_NINIT
#ifdef MAKE_VALGRIND_HAPPY
memset(&dnsstate, 0, sizeof(dnsstate));
#endif
2004-04-26 13:32:57 +00:00
res_ninit(&dnsstate);
2005-08-07 06:34:26 +00:00
res = res_nsearch(&dnsstate, dname, class, type, (unsigned char *)answer, sizeof(answer));
2003-09-27 00:37:07 +00:00
#else
ast_mutex_lock(&res_lock);
2003-09-27 00:37:07 +00:00
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) {
2005-03-02 05:17:13 +00:00
ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
2003-09-27 00:37:07 +00:00
ret = -1;
}
else if (ret == 0) {
2005-03-02 05:17:13 +00:00
ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
2003-09-27 00:37:07 +00:00
ret = 0;
}
else
ret = 1;
}
2004-04-26 13:32:57 +00:00
#ifdef HAS_RES_NINIT
res_nclose(&dnsstate);
2003-09-27 00:37:07 +00:00
#else
2003-10-26 18:50:49 +00:00
#ifndef __APPLE__
2003-09-27 00:37:07 +00:00
res_close();
2003-10-26 18:50:49 +00:00
#endif
ast_mutex_unlock(&res_lock);
2003-09-27 00:37:07 +00:00
#endif
return ret;
}