add ExtenSpy variant of ChanSpy
[asterisk-bristuff.git] / dns.c
blobd214ef3e9261e6797807fe27e93e848d6d0d2cde
1 /*
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.
21 /*! \file
23 * \brief DNS Support for Asterisk
25 * \author Thorsten Lockert <tholo@trollphone.org>
27 * \par Reference
28 * - DNR SRV records http://www.ietf.org/rfc/rfc2782.txt
32 #include "asterisk.h"
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/nameser.h>
40 #include <resolv.h>
41 #include <unistd.h>
43 #include "asterisk/logger.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/dns.h"
46 #include "asterisk/endian.h"
48 #define MAX_SIZE 4096
50 typedef struct {
51 unsigned id:16; /*!< query identification number */
52 #if __BYTE_ORDER == __BIG_ENDIAN
53 /* fields in third byte */
54 unsigned qr:1; /*!< response flag */
55 unsigned opcode:4; /*!< purpose of message */
56 unsigned aa:1; /*!< authoritive answer */
57 unsigned tc:1; /*!< truncated message */
58 unsigned rd:1; /*!< recursion desired */
59 /* fields in fourth byte */
60 unsigned ra:1; /*!< recursion available */
61 unsigned unused:1; /*!< unused bits (MBZ as of 4.9.3a3) */
62 unsigned ad:1; /*!< authentic data from named */
63 unsigned cd:1; /*!< checking disabled by resolver */
64 unsigned rcode:4; /*!< response code */
65 #endif
66 #if __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __PDP_ENDIAN
67 /* fields in third byte */
68 unsigned rd:1; /*!< recursion desired */
69 unsigned tc:1; /*!< truncated message */
70 unsigned aa:1; /*!< authoritive answer */
71 unsigned opcode:4; /*!< purpose of message */
72 unsigned qr:1; /*!< response flag */
73 /* fields in fourth byte */
74 unsigned rcode:4; /*!< response code */
75 unsigned cd:1; /*!< checking disabled by resolver */
76 unsigned ad:1; /*!< authentic data from named */
77 unsigned unused:1; /*!< unused bits (MBZ as of 4.9.3a3) */
78 unsigned ra:1; /*!< recursion available */
79 #endif
80 /* remaining bytes */
81 unsigned qdcount:16; /*!< number of question entries */
82 unsigned ancount:16; /*!< number of answer entries */
83 unsigned nscount:16; /*!< number of authority entries */
84 unsigned arcount:16; /*!< number of resource entries */
85 } dns_HEADER;
87 struct dn_answer {
88 unsigned short rtype;
89 unsigned short class;
90 unsigned int ttl;
91 unsigned short size;
92 } __attribute__ ((__packed__));
94 static int skip_name(unsigned char *s, int len)
96 int x = 0;
98 while (x < len) {
99 if (*s == '\0') {
100 s++;
101 x++;
102 break;
104 if ((*s & 0xc0) == 0xc0) {
105 s += 2;
106 x += 2;
107 break;
109 x += *s + 1;
110 s += *s + 1;
112 if (x >= len)
113 return -1;
114 return x;
117 /*! \brief Parse DNS lookup result, call callback */
118 static int dns_parse_answer(void *context,
119 int class, int type, unsigned char *answer, int len,
120 int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
122 unsigned char *fullanswer = answer;
123 struct dn_answer *ans;
124 dns_HEADER *h;
125 int res;
126 int x;
128 h = (dns_HEADER *)answer;
129 answer += sizeof(dns_HEADER);
130 len -= sizeof(dns_HEADER);
132 for (x = 0; x < ntohs(h->qdcount); x++) {
133 if ((res = skip_name(answer, len)) < 0) {
134 ast_log(LOG_WARNING, "Couldn't skip over name\n");
135 return -1;
137 answer += res + 4; /* Skip name and QCODE / QCLASS */
138 len -= res + 4;
139 if (len < 0) {
140 ast_log(LOG_WARNING, "Strange query size\n");
141 return -1;
145 for (x = 0; x < ntohs(h->ancount); x++) {
146 if ((res = skip_name(answer, len)) < 0) {
147 ast_log(LOG_WARNING, "Failed skipping name\n");
148 return -1;
150 answer += res;
151 len -= res;
152 ans = (struct dn_answer *)answer;
153 answer += sizeof(struct dn_answer);
154 len -= sizeof(struct dn_answer);
155 if (len < 0) {
156 ast_log(LOG_WARNING, "Strange result size\n");
157 return -1;
159 if (len < 0) {
160 ast_log(LOG_WARNING, "Length exceeds frame\n");
161 return -1;
164 if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
165 if (callback) {
166 if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
167 ast_log(LOG_WARNING, "Failed to parse result\n");
168 return -1;
170 if (res > 0)
171 return 1;
174 answer += ntohs(ans->size);
175 len -= ntohs(ans->size);
177 return 0;
180 #if defined(res_ninit)
181 #define HAS_RES_NINIT
182 #else
183 AST_MUTEX_DEFINE_STATIC(res_lock);
184 #if 0
185 #warning "Warning, res_ninit is missing... Could have reentrancy issues"
186 #endif
187 #endif
189 /*! \brief Lookup record in DNS
190 \note Asterisk DNS is synchronus at this time. This means that if your DNS does
191 not work properly, Asterisk might not start properly or a channel may lock.
193 int ast_search_dns(void *context,
194 const char *dname, int class, int type,
195 int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
197 #ifdef HAS_RES_NINIT
198 struct __res_state dnsstate;
199 #endif
200 unsigned char answer[MAX_SIZE];
201 int res, ret = -1;
203 #ifdef HAS_RES_NINIT
204 #ifdef MAKE_VALGRIND_HAPPY
205 memset(&dnsstate, 0, sizeof(dnsstate));
206 #endif
207 res_ninit(&dnsstate);
208 res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
209 #else
210 ast_mutex_lock(&res_lock);
211 res_init();
212 res = res_search(dname, class, type, answer, sizeof(answer));
213 #endif
214 if (res > 0) {
215 if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
216 ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
217 ret = -1;
219 else if (ret == 0) {
220 ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
221 ret = 0;
223 else
224 ret = 1;
226 #ifdef HAS_RES_NINIT
227 res_nclose(&dnsstate);
228 #else
229 #ifndef __APPLE__
230 res_close();
231 #endif
232 ast_mutex_unlock(&res_lock);
233 #endif
234 return ret;