merge new_loader_completion branch, including (at least):
[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
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(unsigned 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, unsigned char *answer, int len,
120         int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
121 {
122         unsigned 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 !HAVE_RES_NINIT
181 AST_MUTEX_DEFINE_STATIC(res_lock);
182 #endif
183
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.
187 */
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))
191 {
192 #if HAVE_RES_NINIT
193         struct __res_state dnsstate;
194 #endif
195         unsigned char answer[MAX_SIZE];
196         int res, ret = -1;
197
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;
210                 }
211                 else if (ret == 0) {
212                         ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
213                         ret = 0;
214                 }
215                 else
216                         ret = 1;
217         }
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
226
227         return ret;
228 }