some fields are const
[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
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <arpa/nameser.h>
32 #include <resolv.h>
33 #include <unistd.h>
34
35 #include "asterisk.h"
36
37 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
38
39 #include "asterisk/logger.h"
40 #include "asterisk/channel.h"
41 #include "asterisk/dns.h"
42 #include "asterisk/endian.h"
43
44 #define MAX_SIZE 4096
45
46 typedef struct {
47         unsigned        id :16;         /*!< query identification number */
48 #if __BYTE_ORDER == __BIG_ENDIAN
49                         /* fields in third byte */
50         unsigned        qr: 1;          /*!< response flag */
51         unsigned        opcode: 4;      /*!< purpose of message */
52         unsigned        aa: 1;          /*!< authoritive answer */
53         unsigned        tc: 1;          /*!< truncated message */
54         unsigned        rd: 1;          /*!< recursion desired */
55                         /* fields in fourth byte */
56         unsigned        ra: 1;          /*!< recursion available */
57         unsigned        unused :1;      /*!< unused bits (MBZ as of 4.9.3a3) */
58         unsigned        ad: 1;          /*!< authentic data from named */
59         unsigned        cd: 1;          /*!< checking disabled by resolver */
60         unsigned        rcode :4;       /*!< response code */
61 #endif
62 #if __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __PDP_ENDIAN
63                         /* fields in third byte */
64         unsigned        rd :1;          /*!< recursion desired */
65         unsigned        tc :1;          /*!< truncated message */
66         unsigned        aa :1;          /*!< authoritive answer */
67         unsigned        opcode :4;      /*!< purpose of message */
68         unsigned        qr :1;          /*!< response flag */
69                         /* fields in fourth byte */
70         unsigned        rcode :4;       /*!< response code */
71         unsigned        cd: 1;          /*!< checking disabled by resolver */
72         unsigned        ad: 1;          /*!< authentic data from named */
73         unsigned        unused :1;      /*!< unused bits (MBZ as of 4.9.3a3) */
74         unsigned        ra :1;          /*!< recursion available */
75 #endif
76                         /* remaining bytes */
77         unsigned        qdcount :16;    /*!< number of question entries */
78         unsigned        ancount :16;    /*!< number of answer entries */
79         unsigned        nscount :16;    /*!< number of authority entries */
80         unsigned        arcount :16;    /*!< number of resource entries */
81 } dns_HEADER;
82
83 struct dn_answer {
84         unsigned short rtype;
85         unsigned short class;
86         unsigned int ttl;
87         unsigned short size;
88 } __attribute__ ((__packed__));
89
90 static int skip_name(char *s, int len)
91 {
92         int x = 0;
93
94         while (x < len) {
95                 if (*s == '\0') {
96                         s++;
97                         x++;
98                         break;
99                 }
100                 if ((*s & 0xc0) == 0xc0) {
101                         s += 2;
102                         x += 2;
103                         break;
104                 }
105                 x += *s + 1;
106                 s += *s + 1;
107         }
108         if (x >= len)
109                 return -1;
110         return x;
111 }
112
113 /*! \brief Parse DNS lookup result, call callback */
114 static int dns_parse_answer(void *context,
115         int class, int type, char *answer, int len,
116         int (*callback)(void *context, char *answer, int len, char *fullanswer))
117 {
118         char *fullanswer = answer;
119         struct dn_answer *ans;
120         dns_HEADER *h;
121         int res;
122         int x;
123
124         h = (dns_HEADER *)answer;
125         answer += sizeof(dns_HEADER);
126         len -= sizeof(dns_HEADER);
127
128         for (x = 0; x < ntohs(h->qdcount); x++) {
129                 if ((res = skip_name(answer, len)) < 0) {
130                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
131                         return -1;
132                 }
133                 answer += res + 4;      /* Skip name and QCODE / QCLASS */
134                 len -= res + 4;
135                 if (len < 0) {
136                         ast_log(LOG_WARNING, "Strange query size\n");
137                         return -1;
138                 }
139         }
140
141         for (x = 0; x < ntohs(h->ancount); x++) {
142                 if ((res = skip_name(answer, len)) < 0) {
143                         ast_log(LOG_WARNING, "Failed skipping name\n");
144                         return -1;
145                 }
146                 answer += res;
147                 len -= res;
148                 ans = (struct dn_answer *)answer;
149                 answer += sizeof(struct dn_answer);
150                 len -= sizeof(struct dn_answer);
151                 if (len < 0) {
152                         ast_log(LOG_WARNING, "Strange result size\n");
153                         return -1;
154                 }
155                 if (len < 0) {
156                         ast_log(LOG_WARNING, "Length exceeds frame\n");
157                         return -1;
158                 }
159
160                 if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
161                         if (callback) {
162                                 if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
163                                         ast_log(LOG_WARNING, "Failed to parse result\n");
164                                         return -1;
165                                 }
166                                 if (res > 0)
167                                         return 1;
168                         }
169                 }
170                 answer += ntohs(ans->size);
171                 len -= ntohs(ans->size);
172         }
173         return 0;
174 }
175
176 #if defined(res_ninit)
177 #define HAS_RES_NINIT
178 #else
179 AST_MUTEX_DEFINE_STATIC(res_lock);
180 #if 0
181 #warning "Warning, res_ninit is missing...  Could have reentrancy issues"
182 #endif
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, char *answer, int len, char *fullanswer))
192 {
193 #ifdef HAS_RES_NINIT
194         struct __res_state dnsstate;
195 #endif
196         char answer[MAX_SIZE];
197         int res, ret = -1;
198
199 #ifdef HAS_RES_NINIT
200 #ifdef MAKE_VALGRIND_HAPPY
201         memset(&dnsstate, 0, sizeof(dnsstate));
202 #endif  
203         res_ninit(&dnsstate);
204         res = res_nsearch(&dnsstate, dname, class, type, (unsigned char *)answer, sizeof(answer));
205 #else
206         ast_mutex_lock(&res_lock);
207         res_init();
208         res = res_search(dname, class, type, answer, sizeof(answer));
209 #endif
210         if (res > 0) {
211                 if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
212                         ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
213                         ret = -1;
214                 }
215                 else if (ret == 0) {
216                         ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
217                         ret = 0;
218                 }
219                 else
220                         ret = 1;
221         }
222 #ifdef HAS_RES_NINIT
223         res_nclose(&dnsstate);
224 #else
225 #ifndef __APPLE__
226         res_close();
227 #endif
228         ast_mutex_unlock(&res_lock);
229 #endif
230         return ret;
231 }