bug #8076 check option_debug before printing to debug channel.
[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 typedef struct {
52         unsigned        id:16;          /*!< query identification number */
53 #if __BYTE_ORDER == __BIG_ENDIAN
54                         /* fields in third byte */
55         unsigned        qr:1;           /*!< response flag */
56         unsigned        opcode:4;       /*!< purpose of message */
57         unsigned        aa:1;           /*!< authoritive answer */
58         unsigned        tc:1;           /*!< truncated message */
59         unsigned        rd:1;           /*!< recursion desired */
60                         /* fields in fourth byte */
61         unsigned        ra:1;           /*!< recursion available */
62         unsigned        unused:1;       /*!< unused bits (MBZ as of 4.9.3a3) */
63         unsigned        ad:1;           /*!< authentic data from named */
64         unsigned        cd:1;           /*!< checking disabled by resolver */
65         unsigned        rcode:4;        /*!< response code */
66 #endif
67 #if __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __PDP_ENDIAN
68                         /* fields in third byte */
69         unsigned        rd:1;           /*!< recursion desired */
70         unsigned        tc:1;           /*!< truncated message */
71         unsigned        aa:1;           /*!< authoritive answer */
72         unsigned        opcode:4;       /*!< purpose of message */
73         unsigned        qr:1;           /*!< response flag */
74                         /* fields in fourth byte */
75         unsigned        rcode:4;        /*!< response code */
76         unsigned        cd:1;           /*!< checking disabled by resolver */
77         unsigned        ad:1;           /*!< authentic data from named */
78         unsigned        unused:1;       /*!< unused bits (MBZ as of 4.9.3a3) */
79         unsigned        ra:1;           /*!< recursion available */
80 #endif
81                         /* remaining bytes */
82         unsigned        qdcount:16;     /*!< number of question entries */
83         unsigned        ancount:16;     /*!< number of answer entries */
84         unsigned        nscount:16;     /*!< number of authority entries */
85         unsigned        arcount:16;     /*!< number of resource entries */
86 } dns_HEADER;
87
88 struct dn_answer {
89         unsigned short rtype;
90         unsigned short class;
91         unsigned int ttl;
92         unsigned short size;
93 } __attribute__ ((__packed__));
94
95 static int skip_name(unsigned char *s, int len)
96 {
97         int x = 0;
98
99         while (x < len) {
100                 if (*s == '\0') {
101                         s++;
102                         x++;
103                         break;
104                 }
105                 if ((*s & 0xc0) == 0xc0) {
106                         s += 2;
107                         x += 2;
108                         break;
109                 }
110                 x += *s + 1;
111                 s += *s + 1;
112         }
113         if (x >= len)
114                 return -1;
115         return x;
116 }
117
118 /*! \brief Parse DNS lookup result, call callback */
119 static int dns_parse_answer(void *context,
120         int class, int type, unsigned char *answer, int len,
121         int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
122 {
123         unsigned char *fullanswer = answer;
124         struct dn_answer *ans;
125         dns_HEADER *h;
126         int res;
127         int x;
128
129         h = (dns_HEADER *)answer;
130         answer += sizeof(dns_HEADER);
131         len -= sizeof(dns_HEADER);
132
133         for (x = 0; x < ntohs(h->qdcount); x++) {
134                 if ((res = skip_name(answer, len)) < 0) {
135                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
136                         return -1;
137                 }
138                 answer += res + 4;      /* Skip name and QCODE / QCLASS */
139                 len -= res + 4;
140                 if (len < 0) {
141                         ast_log(LOG_WARNING, "Strange query size\n");
142                         return -1;
143                 }
144         }
145
146         for (x = 0; x < ntohs(h->ancount); x++) {
147                 if ((res = skip_name(answer, len)) < 0) {
148                         ast_log(LOG_WARNING, "Failed skipping name\n");
149                         return -1;
150                 }
151                 answer += res;
152                 len -= res;
153                 ans = (struct dn_answer *)answer;
154                 answer += sizeof(struct dn_answer);
155                 len -= sizeof(struct dn_answer);
156                 if (len < 0) {
157                         ast_log(LOG_WARNING, "Strange result size\n");
158                         return -1;
159                 }
160                 if (len < 0) {
161                         ast_log(LOG_WARNING, "Length exceeds frame\n");
162                         return -1;
163                 }
164
165                 if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
166                         if (callback) {
167                                 if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
168                                         ast_log(LOG_WARNING, "Failed to parse result\n");
169                                         return -1;
170                                 }
171                                 if (res > 0)
172                                         return 1;
173                         }
174                 }
175                 answer += ntohs(ans->size);
176                 len -= ntohs(ans->size);
177         }
178         return 0;
179 }
180
181 #if !HAVE_RES_NINIT
182 AST_MUTEX_DEFINE_STATIC(res_lock);
183 #endif
184
185 /*! \brief Lookup record in DNS 
186 \note Asterisk DNS is synchronus at this time. This means that if your DNS does
187 not work properly, Asterisk might not start properly or a channel may lock.
188 */
189 int ast_search_dns(void *context,
190            const char *dname, int class, int type,
191            int (*callback)(void *context, unsigned char *answer, int len, unsigned char *fullanswer))
192 {
193 #if HAVE_RES_NINIT
194         struct __res_state dnsstate;
195 #endif
196         unsigned char answer[MAX_SIZE];
197         int res, ret = -1;
198
199 #if HAVE_RES_NINIT
200         res_ninit(&dnsstate);
201         res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
202 #else
203         ast_mutex_lock(&res_lock);
204         res_init();
205         res = res_search(dname, class, type, answer, sizeof(answer));
206 #endif
207         if (res > 0) {
208                 if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
209                         ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
210                         ret = -1;
211                 }
212                 else if (ret == 0) {
213                         if (option_debug)
214                                 ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
215                         ret = 0;
216                 }
217                 else
218                         ret = 1;
219         }
220 #if HAVE_RES_NINIT
221         res_nclose(&dnsstate);
222 #else
223 #ifndef __APPLE__
224         res_close();
225 #endif
226         ast_mutex_unlock(&res_lock);
227 #endif
228
229         return ret;
230 }