83b27c3d227fb7f7a294dfd27c8a096bdb529976
[asterisk/asterisk.git] / 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
48 #define MAX_SIZE 4096
49
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;
86
87 struct dn_answer {
88         unsigned short rtype;
89         unsigned short class;
90         unsigned int ttl;
91         unsigned short size;
92 } __attribute__ ((__packed__));
93
94 static int skip_name(char *s, int len)
95 {
96         int x = 0;
97
98         while (x < len) {
99                 if (*s == '\0') {
100                         s++;
101                         x++;
102                         break;
103                 }
104                 if ((*s & 0xc0) == 0xc0) {
105                         s += 2;
106                         x += 2;
107                         break;
108                 }
109                 x += *s + 1;
110                 s += *s + 1;
111         }
112         if (x >= len)
113                 return -1;
114         return x;
115 }
116
117 /*! \brief Parse DNS lookup result, call callback */
118 static int dns_parse_answer(void *context,
119         int class, int type, char *answer, int len,
120         int (*callback)(void *context, char *answer, int len, char *fullanswer))
121 {
122         char *fullanswer = answer;
123         struct dn_answer *ans;
124         dns_HEADER *h;
125         int res;
126         int x;
127
128         h = (dns_HEADER *)answer;
129         answer += sizeof(dns_HEADER);
130         len -= sizeof(dns_HEADER);
131
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;
136                 }
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;
142                 }
143         }
144
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;
149                 }
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;
158                 }
159                 if (len < 0) {
160                         ast_log(LOG_WARNING, "Length exceeds frame\n");
161                         return -1;
162                 }
163
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;
169                                 }
170                                 if (res > 0)
171                                         return 1;
172                         }
173                 }
174                 answer += ntohs(ans->size);
175                 len -= ntohs(ans->size);
176         }
177         return 0;
178 }
179
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
188
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.
192 */
193 int ast_search_dns(void *context,
194            const char *dname, int class, int type,
195            int (*callback)(void *context, char *answer, int len, char *fullanswer))
196 {
197 #ifdef HAS_RES_NINIT
198         struct __res_state dnsstate;
199 #endif
200         char answer[MAX_SIZE];
201         int res, ret = -1;
202
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, (unsigned char *)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;
218                 }
219                 else if (ret == 0) {
220                         ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
221                         ret = 0;
222                 }
223                 else
224                         ret = 1;
225         }
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;
235 }