1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
/*
* 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>
#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;
}
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
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, "Parse error\n");
ret = -1;
}
else if (ret == 0) {
ast_log(LOG_DEBUG, "No matches found\n");
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;
}
|