Merged revisions 74374 via svnmerge from
[asterisk/asterisk.git] / main / dns.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006 Thorsten Lockert
5  *
6  * Written by Thorsten Lockert <tholo@trollphone.org>
7  *
8  * Funding provided by Troll Phone Networks AS
9  *
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.
15  *
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.
19  */
20
21 /*! \file
22  *
23  * \brief DNS Support for Asterisk
24  *
25  * \author Thorsten Lockert <tholo@trollphone.org>
26  *
27  * \par Reference
28  * - DNR SRV records http://www.ietf.org/rfc/rfc2782.txt
29  *
30  */
31
32 #include "asterisk.h"
33
34 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
35
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>
42
43 #include "asterisk/logger.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/dns.h"
46 #include "asterisk/endian.h"
47 #include "asterisk/options.h"
48
49 #define MAX_SIZE 4096
50
51 #ifdef __PDP_ENDIAN
52 #if __BYTE_ORDER == __PDP_ENDIAN
53 #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
54 #endif
55 #endif
56 #if __BYTE_ORDER == __BIG_ENDIAN
57 #define DETERMINED_BYTE_ORDER __BIG_ENDIAN
58 #endif
59 #if __BYTE_ORDER == __LITTLE_ENDIAN
60 #define DETERMINED_BYTE_ORDER __LITTLE_ENDIAN
61 #endif
62
63 typedef struct {
64         unsigned        id:16;          /*!< query identification number */
65 #if DETERMINED_BYTE_ORDER == __BIG_ENDIAN
66                         /* fields in third byte */
67         unsigned        qr:1;           /*!< response flag */
68         unsigned        opcode:4;       /*!< purpose of message */
69         unsigned        aa:1;           /*!< authoritive answer */
70         unsigned        tc:1;           /*!< truncated message */
71         unsigned        rd:1;           /*!< recursion desired */
72                         /* fields in fourth byte */
73         unsigned        ra:1;           /*!< recursion available */
74         unsigned        unused:1;       /*!< unused bits (MBZ as of 4.9.3a3) */
75         unsigned        ad:1;           /*!< authentic data from named */
76         unsigned        cd:1;           /*!< checking disabled by resolver */
77         unsigned        rcode:4;        /*!< response code */
78 #endif
79 #if DETERMINED_BYTE_ORDER == __LITTLE_ENDIAN
80                         /* fields in third byte */
81         unsigned        rd:1;           /*!< recursion desired */
82         unsigned        tc:1;           /*!< truncated message */
83         unsigned        aa:1;           /*!< authoritive answer */
84         unsigned        opcode:4;       /*!< purpose of message */
85         unsigned        qr:1;           /*!< response flag */
86                         /* fields in fourth byte */
87         unsigned        rcode:4;        /*!< response code */
88         unsigned        cd:1;           /*!< checking disabled by resolver */
89         unsigned        ad:1;           /*!< authentic data from named */
90         unsigned        unused:1;       /*!< unused bits (MBZ as of 4.9.3a3) */
91         unsigned        ra:1;           /*!< recursion available */
92 #endif
93                         /* remaining bytes */
94         unsigned        qdcount:16;     /*!< number of question entries */
95         unsigned        ancount:16;     /*!< number of answer entries */
96         unsigned        nscount:16;     /*!< number of authority entries */
97         unsigned        arcount:16;     /*!< number of resource entries */
98 } dns_HEADER;
99
100 struct dn_answer {
101         unsigned short rtype;
102         unsigned short class;
103         unsigned int ttl;
104         unsigned short size;
105 } __attribute__ ((__packed__));
106
107 static int skip_name(unsigned char *s, int len)
108 {
109         int x = 0;
110
111         while (x < len) {
112                 if (*s == '\0') {
113                         s++;
114                         x++;
115                         break;
116                 }
117                 if ((*s & 0xc0) == 0xc0) {
118                         s += 2;
119                         x += 2;
120                         break;
121                 }
122                 x += *s + 1;
123                 s += *s + 1;
124         }
125         if (x >= len)
126                 return -1;
127         return x;
128 }
129
130 /*! \brief Parse DNS lookup result, call callback */
131 static int dns_parse_answer(void *context,
132         int class, int type, unsigned char *answer, int len,
133         int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
134 {
135         unsigned char *fullanswer = answer;
136         struct dn_answer *ans;
137         dns_HEADER *h;
138         int res;
139         int x;
140
141         h = (dns_HEADER *)answer;
142         answer += sizeof(dns_HEADER);
143         len -= sizeof(dns_HEADER);
144
145         for (x = 0; x < ntohs(h->qdcount); x++) {
146                 if ((res = skip_name(answer, len)) < 0) {
147                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
148                         return -1;
149                 }
150                 answer += res + 4;      /* Skip name and QCODE / QCLASS */
151                 len -= res + 4;
152                 if (len < 0) {
153                         ast_log(LOG_WARNING, "Strange query size\n");
154                         return -1;
155                 }
156         }
157
158         for (x = 0; x < ntohs(h->ancount); x++) {
159                 if ((res = skip_name(answer, len)) < 0) {
160                         ast_log(LOG_WARNING, "Failed skipping name\n");
161                         return -1;
162                 }
163                 answer += res;
164                 len -= res;
165                 ans = (struct dn_answer *)answer;
166                 answer += sizeof(struct dn_answer);
167                 len -= sizeof(struct dn_answer);
168                 if (len < 0) {
169                         ast_log(LOG_WARNING, "Strange result size\n");
170                         return -1;
171                 }
172                 if (len < 0) {
173                         ast_log(LOG_WARNING, "Length exceeds frame\n");
174                         return -1;
175                 }
176
177                 if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
178                         if (callback) {
179                                 if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
180                                         ast_log(LOG_WARNING, "Failed to parse result\n");
181                                         return -1;
182                                 }
183                                 if (res > 0)
184                                         return 1;
185                         }
186                 }
187                 answer += ntohs(ans->size);
188                 len -= ntohs(ans->size);
189         }
190         return 0;
191 }
192
193 #ifndef HAVE_RES_NINIT
194 AST_MUTEX_DEFINE_STATIC(res_lock);
195 #endif
196
197 /*! \brief Lookup record in DNS 
198 \note Asterisk DNS is synchronus at this time. This means that if your DNS does
199 not work properly, Asterisk might not start properly or a channel may lock.
200 */
201 int ast_search_dns(void *context,
202            const char *dname, int class, int type,
203            int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
204 {
205 #ifdef HAVE_RES_NINIT
206         struct __res_state dnsstate;
207 #endif
208         unsigned char answer[MAX_SIZE];
209         int res, ret = -1;
210
211 #ifdef HAVE_RES_NINIT
212         res_ninit(&dnsstate);
213         res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
214 #else
215         ast_mutex_lock(&res_lock);
216         res_init();
217         res = res_search(dname, class, type, answer, sizeof(answer));
218 #endif
219         if (res > 0) {
220                 if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
221                         ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
222                         ret = -1;
223                 }
224                 else if (ret == 0) {
225                         ast_debug(1, "No matches found in DNS for %s\n", dname);
226                         ret = 0;
227                 }
228                 else
229                         ret = 1;
230         }
231 #ifdef HAVE_RES_NINIT
232 #if HAVE_RES_NDESTROY
233         res_ndestroy(&dnsstate);
234 #else
235         res_nclose(&dnsstate);
236 #endif
237 #else
238 #ifndef __APPLE__
239         res_close();
240 #endif
241         ast_mutex_unlock(&res_lock);
242 #endif
243
244         return ret;
245 }