Commit | Line | Data |
---|---|---|
dc0d77d7 CE |
1 | #include "error.h" |
2 | #include "roots.h" | |
3 | #include "log.h" | |
4 | #include "case.h" | |
5 | #include "cache.h" | |
6 | #include "byte.h" | |
7 | #include "dns.h" | |
8 | #include "uint64.h" | |
9 | #include "uint32.h" | |
10 | #include "uint16.h" | |
11 | #include "dd.h" | |
12 | #include "alloc.h" | |
13 | #include "response.h" | |
14 | #include "query.h" | |
15 | ||
16 | static int flagforwardonly = 0; | |
17 | ||
18 | void query_forwardonly(void) | |
19 | { | |
20 | flagforwardonly = 1; | |
21 | } | |
22 | ||
23 | static void cachegeneric(const char type[2],const char *d,const char *data,unsigned int datalen,uint32 ttl) | |
24 | { | |
25 | unsigned int len; | |
26 | char key[257]; | |
27 | ||
28 | len = dns_domain_length(d); | |
29 | if (len > 255) return; | |
30 | ||
31 | byte_copy(key,2,type); | |
32 | byte_copy(key + 2,len,d); | |
33 | case_lowerb(key + 2,len); | |
34 | ||
35 | cache_set(key,len + 2,data,datalen,ttl); | |
36 | } | |
37 | ||
38 | static char save_buf[8192]; | |
39 | static unsigned int save_len; | |
40 | static unsigned int save_ok; | |
41 | ||
42 | static void save_start(void) | |
43 | { | |
44 | save_len = 0; | |
45 | save_ok = 1; | |
46 | } | |
47 | ||
48 | static void save_data(const char *buf,unsigned int len) | |
49 | { | |
50 | if (!save_ok) return; | |
51 | if (len > (sizeof save_buf) - save_len) { save_ok = 0; return; } | |
52 | byte_copy(save_buf + save_len,len,buf); | |
53 | save_len += len; | |
54 | } | |
55 | ||
56 | static void save_finish(const char type[2],const char *d,uint32 ttl) | |
57 | { | |
58 | if (!save_ok) return; | |
59 | cachegeneric(type,d,save_buf,save_len,ttl); | |
60 | } | |
61 | ||
62 | ||
63 | static int typematch(const char rtype[2],const char qtype[2]) | |
64 | { | |
65 | return byte_equal(qtype,2,rtype) || byte_equal(qtype,2,DNS_T_ANY); | |
66 | } | |
67 | ||
68 | static uint32 ttlget(char buf[4]) | |
69 | { | |
70 | uint32 ttl; | |
71 | ||
72 | uint32_unpack_big(buf,&ttl); | |
73 | if (ttl > 1000000000) return 0; | |
74 | if (ttl > 604800) return 604800; | |
75 | return ttl; | |
76 | } | |
77 | ||
78 | ||
79 | static void cleanup(struct query *z) | |
80 | { | |
81 | int j; | |
82 | int k; | |
83 | ||
84 | dns_transmit_free(&z->dt); | |
85 | for (j = 0;j < QUERY_MAXALIAS;++j) | |
86 | dns_domain_free(&z->alias[j]); | |
87 | for (j = 0;j < QUERY_MAXLEVEL;++j) { | |
88 | dns_domain_free(&z->name[j]); | |
89 | for (k = 0;k < QUERY_MAXNS;++k) | |
90 | dns_domain_free(&z->ns[j][k]); | |
91 | } | |
92 | } | |
93 | ||
94 | static int rqa(struct query *z) | |
95 | { | |
96 | int i; | |
97 | ||
98 | for (i = QUERY_MAXALIAS - 1;i >= 0;--i) | |
99 | if (z->alias[i]) { | |
100 | if (!response_query(z->alias[i],z->type,z->class)) return 0; | |
101 | while (i > 0) { | |
102 | if (!response_cname(z->alias[i],z->alias[i - 1],z->aliasttl[i])) return 0; | |
103 | --i; | |
104 | } | |
105 | if (!response_cname(z->alias[0],z->name[0],z->aliasttl[0])) return 0; | |
106 | return 1; | |
107 | } | |
108 | ||
109 | if (!response_query(z->name[0],z->type,z->class)) return 0; | |
110 | return 1; | |
111 | } | |
112 | ||
113 | static int globalip(char *d,char ip[4]) | |
114 | { | |
115 | if (dns_domain_equal(d,"\011localhost\0")) { | |
116 | byte_copy(ip,4,"\177\0\0\1"); | |
117 | return 1; | |
118 | } | |
119 | if (dd(d,"",ip) == 4) return 1; | |
120 | return 0; | |
121 | } | |
122 | ||
123 | static char *t1 = 0; | |
124 | static char *t2 = 0; | |
125 | static char *t3 = 0; | |
126 | static char *cname = 0; | |
127 | static char *referral = 0; | |
128 | static unsigned int *records = 0; | |
129 | ||
130 | static int smaller(char *buf,unsigned int len,unsigned int pos1,unsigned int pos2) | |
131 | { | |
132 | char header1[12]; | |
133 | char header2[12]; | |
134 | int r; | |
135 | unsigned int len1; | |
136 | unsigned int len2; | |
137 | ||
138 | pos1 = dns_packet_getname(buf,len,pos1,&t1); | |
139 | dns_packet_copy(buf,len,pos1,header1,10); | |
140 | pos2 = dns_packet_getname(buf,len,pos2,&t2); | |
141 | dns_packet_copy(buf,len,pos2,header2,10); | |
142 | ||
143 | r = byte_diff(header1,4,header2); | |
144 | if (r < 0) return 1; | |
145 | if (r > 0) return 0; | |
146 | ||
147 | len1 = dns_domain_length(t1); | |
148 | len2 = dns_domain_length(t2); | |
149 | if (len1 < len2) return 1; | |
150 | if (len1 > len2) return 0; | |
151 | ||
152 | r = case_diffb(t1,len1,t2); | |
153 | if (r < 0) return 1; | |
154 | if (r > 0) return 0; | |
155 | ||
156 | if (pos1 < pos2) return 1; | |
157 | return 0; | |
158 | } | |
159 | ||
160 | static int doit(struct query *z,int state) | |
161 | { | |
162 | char key[257]; | |
163 | char *cached; | |
164 | unsigned int cachedlen; | |
165 | char *buf; | |
166 | unsigned int len; | |
167 | const char *whichserver; | |
168 | char header[12]; | |
169 | char misc[20]; | |
170 | unsigned int rcode; | |
171 | unsigned int posanswers; | |
172 | uint16 numanswers; | |
173 | unsigned int posauthority; | |
174 | uint16 numauthority; | |
175 | unsigned int posglue; | |
176 | uint16 numglue; | |
177 | unsigned int pos; | |
178 | unsigned int pos2; | |
179 | uint16 datalen; | |
180 | char *control; | |
181 | char *d; | |
182 | const char *dtype; | |
183 | unsigned int dlen; | |
184 | int flagout; | |
185 | int flagcname; | |
186 | int flagreferral; | |
187 | int flagsoa; | |
188 | uint32 ttl; | |
189 | uint32 soattl; | |
190 | uint32 cnamettl; | |
191 | int i; | |
192 | int j; | |
193 | int k; | |
194 | int p; | |
195 | int q; | |
196 | ||
197 | errno = error_io; | |
198 | if (state == 1) goto HAVEPACKET; | |
199 | if (state == -1) { | |
200 | log_servfail(z->name[z->level]); | |
201 | goto SERVFAIL; | |
202 | } | |
203 | ||
204 | ||
205 | NEWNAME: | |
206 | if (++z->loop == 100) goto DIE; | |
207 | d = z->name[z->level]; | |
208 | dtype = z->level ? DNS_T_A : z->type; | |
209 | dlen = dns_domain_length(d); | |
210 | ||
211 | if (globalip(d,misc)) { | |
212 | if (z->level) { | |
213 | for (k = 0;k < 64;k += 4) | |
214 | if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) { | |
215 | byte_copy(z->servers[z->level - 1] + k,4,misc); | |
216 | break; | |
217 | } | |
218 | goto LOWERLEVEL; | |
219 | } | |
220 | if (!rqa(z)) goto DIE; | |
221 | if (typematch(DNS_T_A,dtype)) { | |
222 | if (!response_rstart(d,DNS_T_A,655360)) goto DIE; | |
223 | if (!response_addbytes(misc,4)) goto DIE; | |
224 | response_rfinish(RESPONSE_ANSWER); | |
225 | } | |
226 | cleanup(z); | |
227 | return 1; | |
228 | } | |
229 | ||
230 | if (dns_domain_equal(d,"\0011\0010\0010\003127\7in-addr\4arpa\0")) { | |
231 | if (z->level) goto LOWERLEVEL; | |
232 | if (!rqa(z)) goto DIE; | |
233 | if (typematch(DNS_T_PTR,dtype)) { | |
234 | if (!response_rstart(d,DNS_T_PTR,655360)) goto DIE; | |
235 | if (!response_addname("\011localhost\0")) goto DIE; | |
236 | response_rfinish(RESPONSE_ANSWER); | |
237 | } | |
238 | cleanup(z); | |
239 | log_stats(); | |
240 | return 1; | |
241 | } | |
242 | ||
243 | if (dlen <= 255) { | |
244 | byte_copy(key,2,DNS_T_ANY); | |
245 | byte_copy(key + 2,dlen,d); | |
246 | case_lowerb(key + 2,dlen); | |
247 | cached = cache_get(key,dlen + 2,&cachedlen,&ttl); | |
248 | if (cached) { | |
249 | log_cachednxdomain(d); | |
250 | goto NXDOMAIN; | |
251 | } | |
252 | ||
253 | byte_copy(key,2,DNS_T_CNAME); | |
254 | cached = cache_get(key,dlen + 2,&cachedlen,&ttl); | |
255 | if (cached) { | |
256 | if (typematch(DNS_T_CNAME,dtype)) { | |
257 | log_cachedanswer(d,DNS_T_CNAME); | |
258 | if (!rqa(z)) goto DIE; | |
259 | if (!response_cname(z->name[0],cached,ttl)) goto DIE; | |
260 | cleanup(z); | |
261 | return 1; | |
262 | } | |
263 | log_cachedcname(d,cached); | |
264 | if (!dns_domain_copy(&cname,cached)) goto DIE; | |
265 | goto CNAME; | |
266 | } | |
267 | ||
268 | if (typematch(DNS_T_NS,dtype)) { | |
269 | byte_copy(key,2,DNS_T_NS); | |
270 | cached = cache_get(key,dlen + 2,&cachedlen,&ttl); | |
271 | if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { | |
272 | log_cachedanswer(d,DNS_T_NS); | |
273 | if (!rqa(z)) goto DIE; | |
274 | pos = 0; | |
275 | while (pos = dns_packet_getname(cached,cachedlen,pos,&t2)) { | |
276 | if (!response_rstart(d,DNS_T_NS,ttl)) goto DIE; | |
277 | if (!response_addname(t2)) goto DIE; | |
278 | response_rfinish(RESPONSE_ANSWER); | |
279 | } | |
280 | cleanup(z); | |
281 | return 1; | |
282 | } | |
283 | } | |
284 | ||
285 | if (typematch(DNS_T_PTR,dtype)) { | |
286 | byte_copy(key,2,DNS_T_PTR); | |
287 | cached = cache_get(key,dlen + 2,&cachedlen,&ttl); | |
288 | if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { | |
289 | log_cachedanswer(d,DNS_T_PTR); | |
290 | if (!rqa(z)) goto DIE; | |
291 | pos = 0; | |
292 | while (pos = dns_packet_getname(cached,cachedlen,pos,&t2)) { | |
293 | if (!response_rstart(d,DNS_T_PTR,ttl)) goto DIE; | |
294 | if (!response_addname(t2)) goto DIE; | |
295 | response_rfinish(RESPONSE_ANSWER); | |
296 | } | |
297 | cleanup(z); | |
298 | return 1; | |
299 | } | |
300 | } | |
301 | ||
302 | if (typematch(DNS_T_MX,dtype)) { | |
303 | byte_copy(key,2,DNS_T_MX); | |
304 | cached = cache_get(key,dlen + 2,&cachedlen,&ttl); | |
305 | if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { | |
306 | log_cachedanswer(d,DNS_T_MX); | |
307 | if (!rqa(z)) goto DIE; | |
308 | pos = 0; | |
309 | while (pos = dns_packet_copy(cached,cachedlen,pos,misc,2)) { | |
310 | pos = dns_packet_getname(cached,cachedlen,pos,&t2); | |
311 | if (!pos) break; | |
312 | if (!response_rstart(d,DNS_T_MX,ttl)) goto DIE; | |
313 | if (!response_addbytes(misc,2)) goto DIE; | |
314 | if (!response_addname(t2)) goto DIE; | |
315 | response_rfinish(RESPONSE_ANSWER); | |
316 | } | |
317 | cleanup(z); | |
318 | return 1; | |
319 | } | |
320 | } | |
321 | ||
322 | if (typematch(DNS_T_A,dtype)) { | |
323 | byte_copy(key,2,DNS_T_A); | |
324 | cached = cache_get(key,dlen + 2,&cachedlen,&ttl); | |
325 | if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { | |
326 | if (z->level) { | |
327 | log_cachedanswer(d,DNS_T_A); | |
328 | while (cachedlen >= 4) { | |
329 | for (k = 0;k < 64;k += 4) | |
330 | if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) { | |
331 | byte_copy(z->servers[z->level - 1] + k,4,cached); | |
332 | break; | |
333 | } | |
334 | cached += 4; | |
335 | cachedlen -= 4; | |
336 | } | |
337 | goto LOWERLEVEL; | |
338 | } | |
339 | ||
340 | log_cachedanswer(d,DNS_T_A); | |
341 | if (!rqa(z)) goto DIE; | |
342 | while (cachedlen >= 4) { | |
343 | if (!response_rstart(d,DNS_T_A,ttl)) goto DIE; | |
344 | if (!response_addbytes(cached,4)) goto DIE; | |
345 | response_rfinish(RESPONSE_ANSWER); | |
346 | cached += 4; | |
347 | cachedlen -= 4; | |
348 | } | |
349 | cleanup(z); | |
350 | return 1; | |
351 | } | |
352 | } | |
353 | ||
354 | if (!typematch(DNS_T_ANY,dtype) && !typematch(DNS_T_AXFR,dtype) && !typematch(DNS_T_CNAME,dtype) && !typematch(DNS_T_NS,dtype) && !typematch(DNS_T_PTR,dtype) && !typematch(DNS_T_A,dtype) && !typematch(DNS_T_MX,dtype)) { | |
355 | byte_copy(key,2,dtype); | |
356 | cached = cache_get(key,dlen + 2,&cachedlen,&ttl); | |
357 | if (cached && (cachedlen || byte_diff(dtype,2,DNS_T_ANY))) { | |
358 | log_cachedanswer(d,dtype); | |
359 | if (!rqa(z)) goto DIE; | |
360 | while (cachedlen >= 2) { | |
361 | uint16_unpack_big(cached,&datalen); | |
362 | cached += 2; | |
363 | cachedlen -= 2; | |
364 | if (datalen > cachedlen) goto DIE; | |
365 | if (!response_rstart(d,dtype,ttl)) goto DIE; | |
366 | if (!response_addbytes(cached,datalen)) goto DIE; | |
367 | response_rfinish(RESPONSE_ANSWER); | |
368 | cached += datalen; | |
369 | cachedlen -= datalen; | |
370 | } | |
371 | cleanup(z); | |
372 | return 1; | |
373 | } | |
374 | } | |
375 | } | |
376 | ||
377 | for (;;) { | |
378 | if (roots(z->servers[z->level],d)) { | |
379 | for (j = 0;j < QUERY_MAXNS;++j) | |
380 | dns_domain_free(&z->ns[z->level][j]); | |
381 | z->control[z->level] = d; | |
382 | break; | |
383 | } | |
384 | ||
385 | if (!flagforwardonly && (z->level < 2)) | |
386 | if (dlen < 255) { | |
387 | byte_copy(key,2,DNS_T_NS); | |
388 | byte_copy(key + 2,dlen,d); | |
389 | case_lowerb(key + 2,dlen); | |
390 | cached = cache_get(key,dlen + 2,&cachedlen,&ttl); | |
391 | if (cached && cachedlen) { | |
392 | z->control[z->level] = d; | |
393 | byte_zero(z->servers[z->level],64); | |
394 | for (j = 0;j < QUERY_MAXNS;++j) | |
395 | dns_domain_free(&z->ns[z->level][j]); | |
396 | pos = 0; | |
397 | j = 0; | |
398 | while (pos = dns_packet_getname(cached,cachedlen,pos,&t1)) { | |
399 | log_cachedns(d,t1); | |
400 | if (j < QUERY_MAXNS) | |
401 | if (!dns_domain_copy(&z->ns[z->level][j++],t1)) goto DIE; | |
402 | } | |
403 | break; | |
404 | } | |
405 | } | |
406 | ||
407 | if (!*d) goto DIE; | |
408 | j = 1 + (unsigned int) (unsigned char) *d; | |
409 | dlen -= j; | |
410 | d += j; | |
411 | } | |
412 | ||
413 | ||
414 | HAVENS: | |
415 | for (j = 0;j < QUERY_MAXNS;++j) | |
416 | if (z->ns[z->level][j]) { | |
417 | if (z->level + 1 < QUERY_MAXLEVEL) { | |
418 | if (!dns_domain_copy(&z->name[z->level + 1],z->ns[z->level][j])) goto DIE; | |
419 | dns_domain_free(&z->ns[z->level][j]); | |
420 | ++z->level; | |
421 | goto NEWNAME; | |
422 | } | |
423 | dns_domain_free(&z->ns[z->level][j]); | |
424 | } | |
425 | ||
426 | for (j = 0;j < 64;j += 4) | |
427 | if (byte_diff(z->servers[z->level] + j,4,"\0\0\0\0")) | |
428 | break; | |
429 | if (j == 64) goto SERVFAIL; | |
430 | ||
431 | dns_sortip(z->servers[z->level],64); | |
432 | if (z->level) { | |
433 | log_tx(z->name[z->level],DNS_T_A,z->control[z->level],z->servers[z->level],z->level); | |
434 | if (dns_transmit_start(&z->dt,z->servers[z->level],flagforwardonly,z->name[z->level],DNS_T_A,z->localip) == -1) goto DIE; | |
435 | } | |
436 | else { | |
437 | log_tx(z->name[0],z->type,z->control[0],z->servers[0],0); | |
438 | if (dns_transmit_start(&z->dt,z->servers[0],flagforwardonly,z->name[0],z->type,z->localip) == -1) goto DIE; | |
439 | } | |
440 | return 0; | |
441 | ||
442 | ||
443 | LOWERLEVEL: | |
444 | dns_domain_free(&z->name[z->level]); | |
445 | for (j = 0;j < QUERY_MAXNS;++j) | |
446 | dns_domain_free(&z->ns[z->level][j]); | |
447 | --z->level; | |
448 | goto HAVENS; | |
449 | ||
450 | ||
451 | HAVEPACKET: | |
452 | if (++z->loop == 100) goto DIE; | |
453 | buf = z->dt.packet; | |
454 | len = z->dt.packetlen; | |
455 | ||
456 | whichserver = z->dt.servers + 4 * z->dt.curserver; | |
457 | control = z->control[z->level]; | |
458 | d = z->name[z->level]; | |
459 | dtype = z->level ? DNS_T_A : z->type; | |
460 | ||
461 | pos = dns_packet_copy(buf,len,0,header,12); if (!pos) goto DIE; | |
462 | pos = dns_packet_skipname(buf,len,pos); if (!pos) goto DIE; | |
463 | pos += 4; | |
464 | posanswers = pos; | |
465 | ||
466 | uint16_unpack_big(header + 6,&numanswers); | |
467 | uint16_unpack_big(header + 8,&numauthority); | |
468 | uint16_unpack_big(header + 10,&numglue); | |
469 | ||
470 | rcode = header[3] & 15; | |
471 | if (rcode && (rcode != 3)) goto DIE; /* impossible; see irrelevant() */ | |
472 | ||
473 | flagout = 0; | |
474 | flagcname = 0; | |
475 | flagreferral = 0; | |
476 | flagsoa = 0; | |
477 | soattl = 0; | |
478 | cnamettl = 0; | |
479 | for (j = 0;j < numanswers;++j) { | |
480 | pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; | |
481 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
482 | ||
483 | if (dns_domain_equal(t1,d)) | |
484 | if (byte_equal(header + 2,2,DNS_C_IN)) { /* should always be true */ | |
485 | if (typematch(header,dtype)) | |
486 | flagout = 1; | |
487 | else if (typematch(header,DNS_T_CNAME)) { | |
488 | if (!dns_packet_getname(buf,len,pos,&cname)) goto DIE; | |
489 | flagcname = 1; | |
490 | cnamettl = ttlget(header + 4); | |
491 | } | |
492 | } | |
493 | ||
494 | uint16_unpack_big(header + 8,&datalen); | |
495 | pos += datalen; | |
496 | } | |
497 | posauthority = pos; | |
498 | ||
499 | for (j = 0;j < numauthority;++j) { | |
500 | pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; | |
501 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
502 | ||
503 | if (typematch(header,DNS_T_SOA)) { | |
504 | flagsoa = 1; | |
505 | soattl = ttlget(header + 4); | |
506 | if (soattl > 3600) soattl = 3600; | |
507 | } | |
508 | else if (typematch(header,DNS_T_NS)) { | |
509 | flagreferral = 1; | |
510 | if (!dns_domain_copy(&referral,t1)) goto DIE; | |
511 | } | |
512 | ||
513 | uint16_unpack_big(header + 8,&datalen); | |
514 | pos += datalen; | |
515 | } | |
516 | posglue = pos; | |
517 | ||
518 | ||
519 | if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa) | |
520 | if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) { | |
521 | log_lame(whichserver,control,referral); | |
522 | byte_zero(whichserver,4); | |
523 | goto HAVENS; | |
524 | } | |
525 | ||
526 | ||
527 | if (records) { alloc_free(records); records = 0; } | |
528 | ||
529 | k = numanswers + numauthority + numglue; | |
530 | records = (unsigned int *) alloc(k * sizeof(unsigned int)); | |
531 | if (!records) goto DIE; | |
532 | ||
533 | pos = posanswers; | |
534 | for (j = 0;j < k;++j) { | |
535 | records[j] = pos; | |
536 | pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; | |
537 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
538 | uint16_unpack_big(header + 8,&datalen); | |
539 | pos += datalen; | |
540 | } | |
541 | ||
542 | i = j = k; | |
543 | while (j > 1) { | |
544 | if (i > 1) { --i; pos = records[i - 1]; } | |
545 | else { pos = records[j - 1]; records[j - 1] = records[i - 1]; --j; } | |
546 | ||
547 | q = i; | |
548 | while ((p = q * 2) < j) { | |
549 | if (!smaller(buf,len,records[p],records[p - 1])) ++p; | |
550 | records[q - 1] = records[p - 1]; q = p; | |
551 | } | |
552 | if (p == j) { | |
553 | records[q - 1] = records[p - 1]; q = p; | |
554 | } | |
555 | while ((q > i) && smaller(buf,len,records[(p = q/2) - 1],pos)) { | |
556 | records[q - 1] = records[p - 1]; q = p; | |
557 | } | |
558 | records[q - 1] = pos; | |
559 | } | |
560 | ||
561 | i = 0; | |
562 | while (i < k) { | |
563 | char type[2]; | |
564 | ||
565 | pos = dns_packet_getname(buf,len,records[i],&t1); if (!pos) goto DIE; | |
566 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
567 | ttl = ttlget(header + 4); | |
568 | ||
569 | byte_copy(type,2,header); | |
570 | if (byte_diff(header + 2,2,DNS_C_IN)) { ++i; continue; } | |
571 | ||
572 | for (j = i + 1;j < k;++j) { | |
573 | pos = dns_packet_getname(buf,len,records[j],&t2); if (!pos) goto DIE; | |
574 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
575 | if (!dns_domain_equal(t1,t2)) break; | |
576 | if (byte_diff(header,2,type)) break; | |
577 | if (byte_diff(header + 2,2,DNS_C_IN)) break; | |
578 | } | |
579 | ||
580 | if (!dns_domain_suffix(t1,control)) { i = j; continue; } | |
581 | if (!roots_same(t1,control)) { i = j; continue; } | |
582 | ||
583 | if (byte_equal(type,2,DNS_T_ANY)) | |
584 | ; | |
585 | else if (byte_equal(type,2,DNS_T_AXFR)) | |
586 | ; | |
587 | else if (byte_equal(type,2,DNS_T_SOA)) { | |
588 | while (i < j) { | |
589 | pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; | |
590 | pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; | |
591 | pos = dns_packet_getname(buf,len,pos,&t3); if (!pos) goto DIE; | |
592 | pos = dns_packet_copy(buf,len,pos,misc,20); if (!pos) goto DIE; | |
593 | if (records[i] < posauthority) | |
594 | log_rrsoa(whichserver,t1,t2,t3,misc,ttl); | |
595 | ++i; | |
596 | } | |
597 | } | |
598 | else if (byte_equal(type,2,DNS_T_CNAME)) { | |
599 | pos = dns_packet_skipname(buf,len,records[j - 1]); if (!pos) goto DIE; | |
600 | pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; | |
601 | log_rrcname(whichserver,t1,t2,ttl); | |
602 | cachegeneric(DNS_T_CNAME,t1,t2,dns_domain_length(t2),ttl); | |
603 | } | |
604 | else if (byte_equal(type,2,DNS_T_PTR)) { | |
605 | save_start(); | |
606 | while (i < j) { | |
607 | pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; | |
608 | pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; | |
609 | log_rrptr(whichserver,t1,t2,ttl); | |
610 | save_data(t2,dns_domain_length(t2)); | |
611 | ++i; | |
612 | } | |
613 | save_finish(DNS_T_PTR,t1,ttl); | |
614 | } | |
615 | else if (byte_equal(type,2,DNS_T_NS)) { | |
616 | save_start(); | |
617 | while (i < j) { | |
618 | pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; | |
619 | pos = dns_packet_getname(buf,len,pos + 10,&t2); if (!pos) goto DIE; | |
620 | log_rrns(whichserver,t1,t2,ttl); | |
621 | save_data(t2,dns_domain_length(t2)); | |
622 | ++i; | |
623 | } | |
624 | save_finish(DNS_T_NS,t1,ttl); | |
625 | } | |
626 | else if (byte_equal(type,2,DNS_T_MX)) { | |
627 | save_start(); | |
628 | while (i < j) { | |
629 | pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; | |
630 | pos = dns_packet_copy(buf,len,pos + 10,misc,2); if (!pos) goto DIE; | |
631 | pos = dns_packet_getname(buf,len,pos,&t2); if (!pos) goto DIE; | |
632 | log_rrmx(whichserver,t1,t2,misc,ttl); | |
633 | save_data(misc,2); | |
634 | save_data(t2,dns_domain_length(t2)); | |
635 | ++i; | |
636 | } | |
637 | save_finish(DNS_T_MX,t1,ttl); | |
638 | } | |
639 | else if (byte_equal(type,2,DNS_T_A)) { | |
640 | save_start(); | |
641 | while (i < j) { | |
642 | pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; | |
643 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
644 | if (byte_equal(header + 8,2,"\0\4")) { | |
645 | pos = dns_packet_copy(buf,len,pos,header,4); if (!pos) goto DIE; | |
646 | save_data(header,4); | |
647 | log_rr(whichserver,t1,DNS_T_A,header,4,ttl); | |
648 | } | |
649 | ++i; | |
650 | } | |
651 | save_finish(DNS_T_A,t1,ttl); | |
652 | } | |
653 | else { | |
654 | save_start(); | |
655 | while (i < j) { | |
656 | pos = dns_packet_skipname(buf,len,records[i]); if (!pos) goto DIE; | |
657 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
658 | uint16_unpack_big(header + 8,&datalen); | |
659 | if (datalen > len - pos) goto DIE; | |
660 | save_data(header + 8,2); | |
661 | save_data(buf + pos,datalen); | |
662 | log_rr(whichserver,t1,type,buf + pos,datalen,ttl); | |
663 | ++i; | |
664 | } | |
665 | save_finish(type,t1,ttl); | |
666 | } | |
667 | ||
668 | i = j; | |
669 | } | |
670 | ||
671 | alloc_free(records); records = 0; | |
672 | ||
673 | ||
674 | if (flagcname) { | |
675 | ttl = cnamettl; | |
676 | CNAME: | |
677 | if (!z->level) { | |
678 | if (z->alias[QUERY_MAXALIAS - 1]) goto DIE; | |
679 | for (j = QUERY_MAXALIAS - 1;j > 0;--j) | |
680 | z->alias[j] = z->alias[j - 1]; | |
681 | for (j = QUERY_MAXALIAS - 1;j > 0;--j) | |
682 | z->aliasttl[j] = z->aliasttl[j - 1]; | |
683 | z->alias[0] = z->name[0]; | |
684 | z->aliasttl[0] = ttl; | |
685 | z->name[0] = 0; | |
686 | } | |
687 | if (!dns_domain_copy(&z->name[z->level],cname)) goto DIE; | |
688 | goto NEWNAME; | |
689 | } | |
690 | ||
691 | if (rcode == 3) { | |
692 | log_nxdomain(whichserver,d,soattl); | |
693 | cachegeneric(DNS_T_ANY,d,"",0,soattl); | |
694 | ||
695 | NXDOMAIN: | |
696 | if (z->level) goto LOWERLEVEL; | |
697 | if (!rqa(z)) goto DIE; | |
698 | response_nxdomain(); | |
699 | cleanup(z); | |
700 | return 1; | |
701 | } | |
702 | ||
703 | if (!flagout && flagsoa) | |
704 | if (byte_diff(DNS_T_ANY,2,dtype)) | |
705 | if (byte_diff(DNS_T_AXFR,2,dtype)) | |
706 | if (byte_diff(DNS_T_CNAME,2,dtype)) { | |
707 | save_start(); | |
708 | save_finish(dtype,d,soattl); | |
709 | log_nodata(whichserver,d,dtype,soattl); | |
710 | } | |
711 | ||
712 | log_stats(); | |
713 | ||
714 | ||
715 | if (flagout || flagsoa || !flagreferral) { | |
716 | if (z->level) { | |
717 | pos = posanswers; | |
718 | for (j = 0;j < numanswers;++j) { | |
719 | pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; | |
720 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
721 | uint16_unpack_big(header + 8,&datalen); | |
722 | if (dns_domain_equal(t1,d)) | |
723 | if (typematch(header,DNS_T_A)) | |
724 | if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */ | |
725 | if (datalen == 4) | |
726 | for (k = 0;k < 64;k += 4) | |
727 | if (byte_equal(z->servers[z->level - 1] + k,4,"\0\0\0\0")) { | |
728 | if (!dns_packet_copy(buf,len,pos,z->servers[z->level - 1] + k,4)) goto DIE; | |
729 | break; | |
730 | } | |
731 | pos += datalen; | |
732 | } | |
733 | goto LOWERLEVEL; | |
734 | } | |
735 | ||
736 | if (!rqa(z)) goto DIE; | |
737 | ||
738 | pos = posanswers; | |
739 | for (j = 0;j < numanswers;++j) { | |
740 | pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; | |
741 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
742 | ttl = ttlget(header + 4); | |
743 | uint16_unpack_big(header + 8,&datalen); | |
744 | if (dns_domain_equal(t1,d)) | |
745 | if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */ | |
746 | if (typematch(header,dtype)) { | |
747 | if (!response_rstart(t1,header,ttl)) goto DIE; | |
748 | ||
749 | if (typematch(header,DNS_T_NS) || typematch(header,DNS_T_CNAME) || typematch(header,DNS_T_PTR)) { | |
750 | if (!dns_packet_getname(buf,len,pos,&t2)) goto DIE; | |
751 | if (!response_addname(t2)) goto DIE; | |
752 | } | |
753 | else if (typematch(header,DNS_T_MX)) { | |
754 | pos2 = dns_packet_copy(buf,len,pos,misc,2); if (!pos2) goto DIE; | |
755 | if (!response_addbytes(misc,2)) goto DIE; | |
756 | if (!dns_packet_getname(buf,len,pos2,&t2)) goto DIE; | |
757 | if (!response_addname(t2)) goto DIE; | |
758 | } | |
759 | else if (typematch(header,DNS_T_SOA)) { | |
760 | pos2 = dns_packet_getname(buf,len,pos,&t2); if (!pos2) goto DIE; | |
761 | if (!response_addname(t2)) goto DIE; | |
762 | pos2 = dns_packet_getname(buf,len,pos2,&t3); if (!pos2) goto DIE; | |
763 | if (!response_addname(t3)) goto DIE; | |
764 | pos2 = dns_packet_copy(buf,len,pos2,misc,20); if (!pos2) goto DIE; | |
765 | if (!response_addbytes(misc,20)) goto DIE; | |
766 | } | |
767 | else { | |
768 | if (pos + datalen > len) goto DIE; | |
769 | if (!response_addbytes(buf + pos,datalen)) goto DIE; | |
770 | } | |
771 | ||
772 | response_rfinish(RESPONSE_ANSWER); | |
773 | } | |
774 | ||
775 | pos += datalen; | |
776 | } | |
777 | ||
778 | cleanup(z); | |
779 | return 1; | |
780 | } | |
781 | ||
782 | ||
783 | if (!dns_domain_suffix(d,referral)) goto DIE; | |
784 | control = d + dns_domain_suffixpos(d,referral); | |
785 | z->control[z->level] = control; | |
786 | byte_zero(z->servers[z->level],64); | |
787 | for (j = 0;j < QUERY_MAXNS;++j) | |
788 | dns_domain_free(&z->ns[z->level][j]); | |
789 | k = 0; | |
790 | ||
791 | pos = posauthority; | |
792 | for (j = 0;j < numauthority;++j) { | |
793 | pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE; | |
794 | pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE; | |
795 | uint16_unpack_big(header + 8,&datalen); | |
796 | if (dns_domain_equal(referral,t1)) /* should always be true */ | |
797 | if (typematch(header,DNS_T_NS)) /* should always be true */ | |
798 | if (byte_equal(header + 2,2,DNS_C_IN)) /* should always be true */ | |
799 | if (k < QUERY_MAXNS) | |
800 | if (!dns_packet_getname(buf,len,pos,&z->ns[z->level][k++])) goto DIE; | |
801 | pos += datalen; | |
802 | } | |
803 | ||
804 | goto HAVENS; | |
805 | ||
806 | ||
807 | SERVFAIL: | |
808 | if (z->level) goto LOWERLEVEL; | |
809 | if (!rqa(z)) goto DIE; | |
810 | response_servfail(); | |
811 | cleanup(z); | |
812 | return 1; | |
813 | ||
814 | ||
815 | DIE: | |
816 | cleanup(z); | |
817 | if (records) { alloc_free(records); records = 0; } | |
818 | return -1; | |
819 | } | |
820 | ||
821 | int query_start(struct query *z,char *dn,char type[2],char class[2],char localip[4]) | |
822 | { | |
823 | if (byte_equal(type,2,DNS_T_AXFR)) { errno = error_perm; return -1; } | |
824 | ||
825 | cleanup(z); | |
826 | z->level = 0; | |
827 | z->loop = 0; | |
828 | ||
829 | if (!dns_domain_copy(&z->name[0],dn)) return -1; | |
830 | byte_copy(z->type,2,type); | |
831 | byte_copy(z->class,2,class); | |
832 | byte_copy(z->localip,4,localip); | |
833 | ||
834 | return doit(z,0); | |
835 | } | |
836 | ||
837 | int query_get(struct query *z,iopause_fd *x,struct taia *stamp) | |
838 | { | |
839 | switch(dns_transmit_get(&z->dt,x,stamp)) { | |
840 | case 1: | |
841 | return doit(z,1); | |
842 | case -1: | |
843 | return doit(z,-1); | |
844 | } | |
845 | return 0; | |
846 | } | |
847 | ||
848 | void query_io(struct query *z,iopause_fd *x,struct taia *deadline) | |
849 | { | |
850 | dns_transmit_io(&z->dt,x,deadline); | |
851 | } |