Add Q.SIG option to zapata.conf
[asterisk/asterisk.git] / dns.c
1 /*
2  * DNS Support for Asterisk
3  *
4  * Written by Thorsten Lockert <tholo@trollphone.org>
5  *
6  * Funding provided by Troll Phone Networks AS
7  *
8  * This program is free software, distributed under the terms of
9  * the GNU General Public License
10  */
11
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <netinet/in.h>
15 #include <arpa/nameser.h>
16 #include <resolv.h>
17 #include <unistd.h>
18
19 #include "asterisk/logger.h"
20 #include "asterisk/channel.h"
21 #include "asterisk/dns.h"
22 #include "asterisk/endian.h"
23
24 #define MAX_SIZE 4096
25
26 typedef struct {
27         unsigned        id :16;         /* query identification number */
28 #if __BYTE_ORDER == __BIG_ENDIAN
29                         /* fields in third byte */
30         unsigned        qr: 1;          /* response flag */
31         unsigned        opcode: 4;      /* purpose of message */
32         unsigned        aa: 1;          /* authoritive answer */
33         unsigned        tc: 1;          /* truncated message */
34         unsigned        rd: 1;          /* recursion desired */
35                         /* fields in fourth byte */
36         unsigned        ra: 1;          /* recursion available */
37         unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
38         unsigned        ad: 1;          /* authentic data from named */
39         unsigned        cd: 1;          /* checking disabled by resolver */
40         unsigned        rcode :4;       /* response code */
41 #endif
42 #if __BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __PDP_ENDIAN
43                         /* fields in third byte */
44         unsigned        rd :1;          /* recursion desired */
45         unsigned        tc :1;          /* truncated message */
46         unsigned        aa :1;          /* authoritive answer */
47         unsigned        opcode :4;      /* purpose of message */
48         unsigned        qr :1;          /* response flag */
49                         /* fields in fourth byte */
50         unsigned        rcode :4;       /* response code */
51         unsigned        cd: 1;          /* checking disabled by resolver */
52         unsigned        ad: 1;          /* authentic data from named */
53         unsigned        unused :1;      /* unused bits (MBZ as of 4.9.3a3) */
54         unsigned        ra :1;          /* recursion available */
55 #endif
56                         /* remaining bytes */
57         unsigned        qdcount :16;    /* number of question entries */
58         unsigned        ancount :16;    /* number of answer entries */
59         unsigned        nscount :16;    /* number of authority entries */
60         unsigned        arcount :16;    /* number of resource entries */
61 } dns_HEADER;
62
63 struct dn_answer {
64         unsigned short rtype;
65         unsigned short class;
66         unsigned int ttl;
67         unsigned short size;
68 } __attribute__ ((__packed__));
69
70 static int skip_name(u_char *s, int len)
71 {
72         int x = 0;
73
74         while (x < len) {
75                 if (*s == '\0') {
76                         s++;
77                         x++;
78                         break;
79                 }
80                 if ((*s & 0xc0) == 0xc0) {
81                         s += 2;
82                         x += 2;
83                         break;
84                 }
85                 x += *s + 1;
86                 s += *s + 1;
87         }
88         if (x >= len)
89                 return -1;
90         return x;
91 }
92
93 /*--- dns_parse_answer: Parse DNS lookup result, call callback */
94 static int dns_parse_answer(void *context,
95         int class, int type, u_char *answer, int len,
96         int (*callback)(void *context, u_char *answer, int len, u_char *fullanswer))
97 {
98         u_char *fullanswer = answer;
99         struct dn_answer *ans;
100         dns_HEADER *h;
101         int res;
102         int x;
103
104         h = (dns_HEADER *)answer;
105         answer += sizeof(dns_HEADER);
106         len -= sizeof(dns_HEADER);
107
108         for (x = 0; x < ntohs(h->qdcount); x++) {
109                 if ((res = skip_name(answer, len)) < 0) {
110                         ast_log(LOG_WARNING, "Couldn't skip over name\n");
111                         return -1;
112                 }
113                 answer += res + 4;      /* Skip name and QCODE / QCLASS */
114                 len -= res + 4;
115                 if (len < 0) {
116                         ast_log(LOG_WARNING, "Strange query size\n");
117                         return -1;
118                 }
119         }
120
121         for (x = 0; x < ntohs(h->ancount); x++) {
122                 if ((res = skip_name(answer, len)) < 0) {
123                         ast_log(LOG_WARNING, "Failed skipping name\n");
124                         return -1;
125                 }
126                 answer += res;
127                 len -= res;
128                 ans = (struct dn_answer *)answer;
129                 answer += sizeof(struct dn_answer);
130                 len -= sizeof(struct dn_answer);
131                 if (len < 0) {
132                         ast_log(LOG_WARNING, "Strange result size\n");
133                         return -1;
134                 }
135                 if (len < 0) {
136                         ast_log(LOG_WARNING, "Length exceeds frame\n");
137                         return -1;
138                 }
139
140                 if (ntohs(ans->class) == class && ntohs(ans->rtype) == type) {
141                         if (callback) {
142                                 if ((res = callback(context, answer, ntohs(ans->size), fullanswer)) < 0) {
143                                         ast_log(LOG_WARNING, "Failed to parse result\n");
144                                         return -1;
145                                 }
146                                 if (res > 0)
147                                         return 1;
148                         }
149                 }
150                 answer += ntohs(ans->size);
151                 len -= ntohs(ans->size);
152         }
153         return 0;
154 }
155
156 #if defined(res_ninit)
157 #define HAS_RES_NINIT
158 #else
159 AST_MUTEX_DEFINE_STATIC(res_lock);
160 #if 0
161 #warning "Warning, res_ninit is missing...  Could have reentrancy issues"
162 #endif
163 #endif
164
165 /*--- ast_search_dns: Lookup record in DNS */
166 int ast_search_dns(void *context,
167            const char *dname, int class, int type,
168            int (*callback)(void *context, u_char *answer, int len, u_char *fullanswer))
169 {
170 #ifdef HAS_RES_NINIT
171         struct __res_state dnsstate;
172 #endif
173         char answer[MAX_SIZE];
174         int res, ret = -1;
175
176 #ifdef HAS_RES_NINIT
177         res_ninit(&dnsstate);
178         res = res_nsearch(&dnsstate, dname, class, type, answer, sizeof(answer));
179 #else
180         ast_mutex_lock(&res_lock);
181         res_init();
182         res = res_search(dname, class, type, answer, sizeof(answer));
183 #endif
184         if (res > 0) {
185                 if ((res = dns_parse_answer(context, class, type, answer, res, callback)) < 0) {
186                         ast_log(LOG_WARNING, "DNS Parse error for %s\n", dname);
187                         ret = -1;
188                 }
189                 else if (ret == 0) {
190                         ast_log(LOG_DEBUG, "No matches found in DNS for %s\n", dname);
191                         ret = 0;
192                 }
193                 else
194                         ret = 1;
195         }
196 #ifdef HAS_RES_NINIT
197         res_nclose(&dnsstate);
198 #else
199 #ifndef __APPLE__
200         res_close();
201 #endif
202         ast_mutex_unlock(&res_lock);
203 #endif
204         return ret;
205 }