1 /*
2 * DNS library functions
3 * Copyright (c) 2006-2008 David Bird <david@coova.com>
4 *
5 * The contents of this file may be used under the terms of the GNU
6 * General Public License Version 2, provided that the above copyright
7 * notice and this permission notice is included in all copies or
8 * substantial portions of the software.
9 *
10 */
11
12 #include "dns.h"
13 #include "garden.h"
14 #include "syserr.h"
15 #include "dhcp.h"
16 #include "options.h"
17
18 #define antidnstunnel options.dnsparanoia
19
20 extern struct dhcp_t *dhcp;
21
22 char *
dns_fullname(char * data,size_t dlen,uint8_t * res,uint8_t * opkt,size_t olen,int lvl)23 dns_fullname(char *data, size_t dlen, uint8_t *res, uint8_t *opkt, size_t olen, int lvl) {
24 char *d = data;
25 unsigned short l;
26
27 if (lvl >= 15)
28 return 0;
29
30 while ((l = *res++) != 0) {
31 if ((l & 0xC0) == 0xC0) {
32 unsigned short offset = ((l & ~0xC0) << 8) + *res;
33 if (offset > olen) {
34 log_dbg("bad value");
35 return 0;
36 }
37 /*log_dbg("skip[%d]\n",offset);*/
38 dns_fullname(d, dlen, opkt + (size_t)offset, opkt, olen, lvl+1);
39 break;
40 }
41
42 if (l >= dlen) {
43 log_dbg("bad value");
44 return 0;
45 }
46
47 /*log_dbg("part[%.*s]\n",l,res);*/
48
49 memcpy(d, res, l);
50 d += l; dlen -= l;
51 res += l;
52
53 *d = '.';
54 d += 1; dlen -= 1;
55 }
56
57 if (!lvl && data[strlen((char*)data)-1]=='.')
58 data[strlen((char*)data)-1]=0;
59
60 return data;
61 }
62
dns_getname(uint8_t ** pktp,size_t * left,char * name,size_t namesz,size_t * nameln)63 int dns_getname(uint8_t **pktp, size_t *left, char *name, size_t namesz, size_t *nameln) {
64 size_t namelen = *nameln;
65 uint8_t *p_pkt = *pktp;
66 size_t len = *left;
67 uint8_t l;
68
69 namelen = 0;
70 while (len-- && (l = name[namelen++] = *p_pkt++) != 0) {
71 if ((l & 0xC0) == 0xC0) {
72 if (namelen >= namesz) {
73 log_err(0, "name too long in DNS packet");
74 break;
75 }
76 name[namelen++] = *p_pkt++;
77 len--;
78 break;
79 }
80 }
81
82 *pktp = p_pkt;
83 *nameln = namelen;
84 *left = len;
85
86 if (!len) {
87 log_err(0, "failed to parse DNS packet");
88 return -1;
89 }
90
91 return 0;
92 }
93
94 static void
add_A_to_garden(uint8_t * p)95 add_A_to_garden(uint8_t *p) {
96 struct in_addr reqaddr;
97 pass_through pt;
98 memcpy(&reqaddr.s_addr, p, 4);
99 memset(&pt, 0, sizeof(pass_through));
100 pt.mask.s_addr = 0xffffffff;
101 pt.host = reqaddr;
102 if (pass_through_add(dhcp->pass_throughs,
103 MAX_PASS_THROUGHS,
104 &dhcp->num_pass_throughs,
105 &pt))
106 ;
107 }
108
109 int
dns_copy_res(int q,uint8_t ** pktp,size_t * left,uint8_t * opkt,size_t olen,char * question,size_t qsize)110 dns_copy_res(int q,
111 uint8_t **pktp, size_t *left,
112 uint8_t *opkt, size_t olen,
113 char *question, size_t qsize) {
114
115 #define return_error \
116 { log_dbg("%s:%d: failed parsing DNS packet",__FILE__,__LINE__); return -1; }
117
118 uint8_t *p_pkt = *pktp;
119 size_t len = *left;
120
121 uint8_t name[PKT_IP_PLEN];
122 size_t namelen = 0;
123
124 uint16_t type;
125 uint16_t class;
126 uint32_t ttl;
127 uint16_t rdlen;
128
129 uint32_t ul;
130 uint16_t us;
131
132 if (dns_getname(&p_pkt, &len, (char *)name, sizeof(name), &namelen))
133 return_error;
134
135 if (antidnstunnel && namelen > 128) {
136 log_warn(0,"dropping dns for anti-dnstunnel (namelen: %d)",namelen);
137 return -1;
138 }
139
140 if (len < 4)
141 return_error;
142
143 memcpy(&us, p_pkt, sizeof(us));
144 type = ntohs(us);
145 p_pkt += 2;
146 len -= 2;
147
148 memcpy(&us, p_pkt, sizeof(us));
149 class = ntohs(us);
150 p_pkt += 2;
151 len -= 2;
152
153 log_dbg("It was a dns record type: %d class: %d", type, class);
154
155
156 /* if dnsparanoia, checks here */
157
158 if (antidnstunnel) {
159 switch (type) {
160 case 1:/* A */
161 log_dbg("A record");
162 break;
163 case 5:/* CNAME */
164 log_dbg("CNAME record");
165 break;
166 default:
167 if (options.debug) switch(type) {
168 case 6: log_dbg("SOA record"); break;
169 case 12: log_dbg("PTR record"); break;
170 case 15: log_dbg("MX record"); break;
171 case 16: log_dbg("TXT record"); break;
172 default: log_dbg("Record type %d", type); break;
173 }
174 log_warn(0, "dropping dns for anti-dnstunnel (type %d: length %d)", type, rdlen);
175 return -1;
176 }
177 }
178
179 if (q) {
180 dns_fullname(question, qsize, *pktp, opkt, olen, 0);
181
182 log_dbg("Q: %s", question);
183
184 *pktp = p_pkt;
185 *left = len;
186
187 return 0;
188 }
189
190 if (len < 6)
191 return_error;
192
193 memcpy(&ul, p_pkt, sizeof(ul));
194 ttl = ntohl(ul);
195 p_pkt += 4;
196 len -= 4;
197
198 memcpy(&us, p_pkt, sizeof(us));
199 rdlen = ntohs(us);
200 p_pkt += 2;
201 len -= 2;
202
203 /*log_dbg("-> w ttl: %d rdlength: %d/%d", ttl, rdlen, len);*/
204
205 if (len < rdlen)
206 return_error;
207
208 /*
209 * dns records
210 */
211
212 switch (type) {
213
214 case 1:/* A */
215 log_dbg("A record");
216 if (options.uamdomains) {
217 int id;
218 for (id=0; options.uamdomains[id]; id++) {
219
220 log_dbg("checking %s [%s]", options.uamdomains[id], question);
221
222 if (strlen(question) >= strlen(options.uamdomains[id]) &&
223 !strcmp(options.uamdomains[id],
224 question + (strlen(question) - strlen(options.uamdomains[id])))) {
225 size_t offset;
226 for (offset=0; offset < rdlen; offset += 4) {
227 add_A_to_garden(p_pkt+offset);
228 }
229
230 break;
231 }
232 }
233 }
234 break;
235
236 case 5:/* CNAME */
237 {
238 char cname[256];
239 memset(cname,0,sizeof(cname));
240 dns_fullname(cname, sizeof(cname)-1, p_pkt, opkt, olen, 0);
241 log_dbg("CNAME record %s", cname);
242 }
243 break;
244
245 default:
246
247 if (options.debug) switch(type) {
248 case 6: log_dbg("SOA record"); break;
249 case 12: log_dbg("PTR record"); break;
250 case 15: log_dbg("MX record"); break;
251 case 16: log_dbg("TXT record"); break;
252 default: log_dbg("Record type %d", type); break;
253 }
254
255 if (antidnstunnel) {
256 log_warn(0, "dropping dns for anti-dnstunnel (type %d: length %d)", type, rdlen);
257 return -1;
258 }
259
260 break;
261 }
262
263 p_pkt += rdlen;
264 len -= rdlen;
265
266 *pktp = p_pkt;
267 *left = len;
268
269 return 0;
270 }
271