ast_func_read() needs a writable copy of the function name to be passed
[asterisk-bristuff.git] / main / dns.c
blob3599de2c5a31f2135ac1ce8d7cba31360b106b30
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 !HAVE_RES_NINIT
181 AST_MUTEX_DEFINE_STATIC(res_lock);
182 #endif
184 /*! \brief Lookup record in DNS
185 \note Asterisk DNS is synchronus at this time. This means that if your DNS does
186 not work properly, Asterisk might not start properly or a channel may lock.
188 int ast_search_dns(void *context,
189 const char *dname, int class, int type,
190 int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
192 #if HAVE_RES_NINIT
193 struct __res_state dnsstate;
194 #endif
195 unsigned char answer[MAX_SIZE];
196 int res, ret = -1;
198 #if HAVE_RES_NINIT
199 res_ninit(&dnsstate);
200 res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
201 #else
202 ast_mutex_lock(&res_lock);
203 res_init();
204 res = res_search(dname, class, type, answer, sizeof(answer));
205 #endif
206 if (res > 0) {
207 if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
208 ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
209 ret = -1;
211 else if (ret == 0) {
212 ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
213 ret = 0;
215 else
216 ret = 1;
218 #if HAVE_RES_NINIT
219 res_nclose(&dnsstate);
220 #else
221 #ifndef __APPLE__
222 res_close();
223 #endif
224 ast_mutex_unlock(&res_lock);
225 #endif
227 return ret;