release
[hcoop/zz_old/debian/djbdns.git] / dnstrace.c
CommitLineData
dc0d77d7
CE
1#include "uint16.h"
2#include "uint32.h"
3#include "fmt.h"
4#include "str.h"
5#include "byte.h"
6#include "ip4.h"
7#include "gen_alloc.h"
8#include "gen_allocdefs.h"
9#include "exit.h"
10#include "buffer.h"
11#include "stralloc.h"
12#include "error.h"
13#include "strerr.h"
14#include "iopause.h"
15#include "printrecord.h"
16#include "alloc.h"
17#include "parsetype.h"
18#include "dd.h"
19#include "dns.h"
20
21#define FATAL "dnstrace: fatal: "
22
23void nomem(void)
24{
25 strerr_die2x(111,FATAL,"out of memory");
26}
27void usage(void)
28{
29 strerr_die1x(100,"dnstrace: usage: dnstrace type name rootip ...");
30}
31
32static stralloc querystr;
33char ipstr[IP4_FMT];
34static stralloc tmp;
35
36void printdomain(const char *d)
37{
38 if (!stralloc_copys(&tmp,"")) nomem();
39 if (!dns_domain_todot_cat(&tmp,d)) nomem();
40 buffer_put(buffer_1,tmp.s,tmp.len);
41}
42
43static struct dns_transmit tx;
44
45int resolve(char *q,char qtype[2],char ip[4])
46{
47 struct taia start;
48 struct taia stamp;
49 struct taia deadline;
50 char servers[64];
51 iopause_fd x[1];
52 int r;
53
54 taia_now(&start);
55
56 byte_zero(servers,64);
57 byte_copy(servers,4,ip);
58
59 if (dns_transmit_start(&tx,servers,0,q,qtype,"\0\0\0\0") == -1) return -1;
60
61 for (;;) {
62 taia_now(&stamp);
63 taia_uint(&deadline,120);
64 taia_add(&deadline,&deadline,&stamp);
65 dns_transmit_io(&tx,x,&deadline);
66 iopause(x,1,&deadline,&stamp);
67 r = dns_transmit_get(&tx,x,&stamp);
68 if (r == -1) return -1;
69 if (r == 1) break;
70 }
71
72 taia_now(&stamp);
73 taia_sub(&stamp,&stamp,&start);
74 taia_uint(&deadline,1);
75 if (taia_less(&deadline,&stamp)) {
76 buffer_put(buffer_1,querystr.s,querystr.len);
77 buffer_puts(buffer_1,"ALERT:took more than 1 second\n");
78 }
79
80 return 0;
81}
82
83struct address {
84 char *owner;
85 char ip[4];
86} ;
87
88GEN_ALLOC_typedef(address_alloc,struct address,s,len,a)
89GEN_ALLOC_readyplus(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus)
90GEN_ALLOC_append(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus,address_alloc_append)
91
92static address_alloc address;
93
94struct ns {
95 char *owner;
96 char *ns;
97} ;
98
99GEN_ALLOC_typedef(ns_alloc,struct ns,s,len,a)
100GEN_ALLOC_readyplus(ns_alloc,struct ns,s,len,a,i,n,x,30,ns_alloc_readyplus)
101GEN_ALLOC_append(ns_alloc,struct ns,s,len,a,i,n,x,30,ns_alloc_readyplus,ns_alloc_append)
102
103static ns_alloc ns;
104
105struct query {
106 char *owner;
107 char type[2];
108} ;
109
110GEN_ALLOC_typedef(query_alloc,struct query,s,len,a)
111GEN_ALLOC_readyplus(query_alloc,struct query,s,len,a,i,n,x,30,query_alloc_readyplus)
112GEN_ALLOC_append(query_alloc,struct query,s,len,a,i,n,x,30,query_alloc_readyplus,query_alloc_append)
113
114static query_alloc query;
115
116struct qt {
117 char *owner;
118 char type[2];
119 char *control;
120 char ip[4];
121} ;
122
123GEN_ALLOC_typedef(qt_alloc,struct qt,s,len,a)
124GEN_ALLOC_readyplus(qt_alloc,struct qt,s,len,a,i,n,x,30,qt_alloc_readyplus)
125GEN_ALLOC_append(qt_alloc,struct qt,s,len,a,i,n,x,30,qt_alloc_readyplus,qt_alloc_append)
126
127static qt_alloc qt;
128
129void qt_add(const char *q,const char type[2],const char *control,const char ip[4])
130{
131 struct qt x;
132 int i;
133
134 if (!*q) return; /* don't ask the roots about our artificial . host */
135
136 for (i = 0;i < qt.len;++i)
137 if (dns_domain_equal(qt.s[i].owner,q))
138 if (dns_domain_equal(qt.s[i].control,control))
139 if (byte_equal(qt.s[i].type,2,type))
140 if (byte_equal(qt.s[i].ip,4,ip))
141 return;
142
143 byte_zero(&x,sizeof x);
144 if (!dns_domain_copy(&x.owner,q)) nomem();
145 if (!dns_domain_copy(&x.control,control)) nomem();
146 byte_copy(x.type,2,type);
147 byte_copy(x.ip,4,ip);
148 if (!qt_alloc_append(&qt,&x)) nomem();
149}
150
151void query_add(const char *owner,const char type[2])
152{
153 struct query x;
154 int i;
155 int j;
156
157 for (i = 0;i < query.len;++i)
158 if (dns_domain_equal(query.s[i].owner,owner))
159 if (byte_equal(query.s[i].type,2,type))
160 return;
161
162 byte_zero(&x,sizeof x);
163 if (!dns_domain_copy(&x.owner,owner)) nomem();
164 byte_copy(x.type,2,type);
165 if (!query_alloc_append(&query,&x)) nomem();
166
167 for (i = 0;i < ns.len;++i)
168 if (dns_domain_suffix(owner,ns.s[i].owner))
169 for (j = 0;j < address.len;++j)
170 if (dns_domain_equal(ns.s[i].ns,address.s[j].owner))
171 qt_add(owner,type,ns.s[i].owner,address.s[j].ip);
172}
173
174void ns_add(const char *owner,const char *server)
175{
176 struct ns x;
177 int i;
178 int j;
179
180 buffer_put(buffer_1,querystr.s,querystr.len);
181 buffer_puts(buffer_1,"NS:");
182 printdomain(owner);
183 buffer_puts(buffer_1,":");
184 printdomain(server);
185 buffer_puts(buffer_1,"\n");
186
187 for (i = 0;i < ns.len;++i)
188 if (dns_domain_equal(ns.s[i].owner,owner))
189 if (dns_domain_equal(ns.s[i].ns,server))
190 return;
191
192 query_add(server,DNS_T_A);
193
194 byte_zero(&x,sizeof x);
195 if (!dns_domain_copy(&x.owner,owner)) nomem();
196 if (!dns_domain_copy(&x.ns,server)) nomem();
197 if (!ns_alloc_append(&ns,&x)) nomem();
198
199 for (i = 0;i < query.len;++i)
200 if (dns_domain_suffix(query.s[i].owner,owner))
201 for (j = 0;j < address.len;++j)
202 if (dns_domain_equal(server,address.s[j].owner))
203 qt_add(query.s[i].owner,query.s[i].type,owner,address.s[j].ip);
204}
205
206void address_add(const char *owner,const char ip[4])
207{
208 struct address x;
209 int i;
210 int j;
211
212 buffer_put(buffer_1,querystr.s,querystr.len);
213 buffer_puts(buffer_1,"A:");
214 printdomain(owner);
215 buffer_puts(buffer_1,":");
216 buffer_put(buffer_1,ipstr,ip4_fmt(ipstr,ip));
217 buffer_puts(buffer_1,"\n");
218
219 for (i = 0;i < address.len;++i)
220 if (dns_domain_equal(address.s[i].owner,owner))
221 if (byte_equal(address.s[i].ip,4,ip))
222 return;
223
224 byte_zero(&x,sizeof x);
225 if (!dns_domain_copy(&x.owner,owner)) nomem();
226 byte_copy(x.ip,4,ip);
227 if (!address_alloc_append(&address,&x)) nomem();
228
229 for (i = 0;i < ns.len;++i)
230 if (dns_domain_equal(ns.s[i].ns,owner))
231 for (j = 0;j < query.len;++j)
232 if (dns_domain_suffix(query.s[j].owner,ns.s[i].owner))
233 qt_add(query.s[j].owner,query.s[j].type,ns.s[i].owner,ip);
234}
235
236char seed[128];
237
238static char *t1;
239static char *t2;
240static char *referral;
241static char *cname;
242
243static int typematch(const char rtype[2],const char qtype[2])
244{
245 return byte_equal(qtype,2,rtype) || byte_equal(qtype,2,DNS_T_ANY);
246}
247
248void parsepacket(const char *buf,unsigned int len,const char *d,const char dtype[2],const char *control)
249{
250 char misc[20];
251 char header[12];
252 unsigned int pos;
253 uint16 numanswers;
254 unsigned int posanswers;
255 uint16 numauthority;
256 unsigned int posauthority;
257 uint16 numglue;
258 unsigned int posglue;
259 uint16 datalen;
260 unsigned int rcode;
261 int flagout;
262 int flagcname;
263 int flagreferral;
264 int flagsoa;
265 int j;
266 const char *x;
267
268 pos = dns_packet_copy(buf,len,0,header,12); if (!pos) goto DIE;
269 pos = dns_packet_skipname(buf,len,pos); if (!pos) goto DIE;
270 pos += 4;
271
272 uint16_unpack_big(header + 6,&numanswers);
273 uint16_unpack_big(header + 8,&numauthority);
274 uint16_unpack_big(header + 10,&numglue);
275
276 rcode = header[3] & 15;
277 if (rcode && (rcode != 3)) { errno = error_proto; goto DIE; } /* impossible */
278
279 flagout = 0;
280 flagcname = 0;
281 flagreferral = 0;
282 flagsoa = 0;
283 posanswers = pos;
284 for (j = 0;j < numanswers;++j) {
285 pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
286 pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
287 if (dns_domain_equal(t1,d))
288 if (byte_equal(header + 2,2,DNS_C_IN))
289 if (typematch(header,dtype))
290 flagout = 1;
291 else if (typematch(header,DNS_T_CNAME)) {
292 if (!dns_packet_getname(buf,len,pos,&cname)) goto DIE;
293 flagcname = 1;
294 }
295 uint16_unpack_big(header + 8,&datalen);
296 pos += datalen;
297 }
298 posauthority = pos;
299 for (j = 0;j < numauthority;++j) {
300 pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
301 pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
302 if (typematch(header,DNS_T_SOA))
303 flagsoa = 1;
304 else if (typematch(header,DNS_T_NS)) {
305 flagreferral = 1;
306 if (!dns_domain_copy(&referral,t1)) goto DIE;
307 }
308 uint16_unpack_big(header + 8,&datalen);
309 pos += datalen;
310 }
311 posglue = pos;
312
313 if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa)
314 if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) {
315 buffer_put(buffer_1,querystr.s,querystr.len);
316 buffer_puts(buffer_1,"ALERT:lame server; refers to ");
317 printdomain(referral);
318 buffer_puts(buffer_1,"\n");
319 return;
320 }
321
322 pos = posanswers;
323 for (j = 0;j < numanswers + numauthority + numglue;++j) {
324 pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
325 pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
326 uint16_unpack_big(header + 8,&datalen);
327 if (dns_domain_suffix(t1,control))
328 if (byte_equal(header + 2,2,DNS_C_IN)) {
329 if (typematch(header,DNS_T_NS)) {
330 if (!dns_packet_getname(buf,len,pos,&t2)) goto DIE;
331 ns_add(t1,t2);
332 }
333 else if (typematch(header,DNS_T_A) && datalen == 4) {
334 if (!dns_packet_copy(buf,len,pos,misc,4)) goto DIE;
335 address_add(t1,misc);
336 }
337 }
338 pos += datalen;
339 }
340
341
342 if (flagcname) {
343 query_add(cname,dtype);
344 buffer_put(buffer_1,querystr.s,querystr.len);
345 buffer_puts(buffer_1,"CNAME:");
346 printdomain(cname);
347 buffer_puts(buffer_1,"\n");
348 return;
349 }
350 if (rcode == 3) {
351 buffer_put(buffer_1,querystr.s,querystr.len);
352 buffer_puts(buffer_1,"NXDOMAIN\n");
353 return;
354 }
355 if (flagout || flagsoa || !flagreferral) {
356 if (!flagout) {
357 buffer_put(buffer_1,querystr.s,querystr.len);
358 buffer_puts(buffer_1,"NODATA\n");
359 return;
360 }
361 pos = posanswers;
362 for (j = 0;j < numanswers + numauthority + numglue;++j) {
363 pos = printrecord(&tmp,buf,len,pos,d,dtype);
364 if (!pos) goto DIE;
365 if (tmp.len) {
366 buffer_put(buffer_1,querystr.s,querystr.len);
367 buffer_puts(buffer_1,"answer:");
368 buffer_put(buffer_1,tmp.s,tmp.len); /* includes \n */
369 }
370 }
371 return;
372 }
373
374 if (!dns_domain_suffix(d,referral)) goto DIE;
375 buffer_put(buffer_1,querystr.s,querystr.len);
376 buffer_puts(buffer_1,"see:");
377 printdomain(referral);
378 buffer_puts(buffer_1,"\n");
379 return;
380
381 DIE:
382 x = error_str(errno);
383 buffer_put(buffer_1,querystr.s,querystr.len);
384 buffer_puts(buffer_1,"ALERT:unable to parse response packet; ");
385 buffer_puts(buffer_1,x);
386 buffer_puts(buffer_1,"\n");
387}
388
389int main(int argc,char **argv)
390{
391 static stralloc out;
392 static stralloc fqdn;
393 static stralloc udn;
394 static char *q;
395 char *control;
396 char type[2];
397 char ip[64];
398 int i;
399 uint16 u16;
400
401 dns_random_init(seed);
402
403 if (!stralloc_copys(&querystr,"0:.:.:start:")) nomem();
404
405 if (!address_alloc_readyplus(&address,1)) nomem();
406 if (!query_alloc_readyplus(&query,1)) nomem();
407 if (!ns_alloc_readyplus(&ns,1)) nomem();
408 if (!qt_alloc_readyplus(&qt,1)) nomem();
409
410 if (!*argv) usage();
411 if (!*++argv) usage();
412 if (!parsetype(*argv,type)) usage();
413
414 if (!*++argv) usage();
415 if (!dns_domain_fromdot(&q,*argv,str_len(*argv))) nomem();
416
417 query_add(q,type);
418 ns_add("","");
419
420 while (*++argv) {
421 if (!stralloc_copys(&udn,*argv)) nomem();
422 if (dns_ip4_qualify(&out,&fqdn,&udn) == -1) nomem(); /* XXX */
423 for (i = 0;i + 4 <= out.len;i += 4)
424 address_add("",out.s + i);
425 }
426
427 for (i = 0;i < qt.len;++i) {
428 if (!dns_domain_copy(&q,qt.s[i].owner)) nomem();
429 control = qt.s[i].control;
430 if (!dns_domain_suffix(q,control)) continue;
431 byte_copy(type,2,qt.s[i].type);
432 byte_copy(ip,4,qt.s[i].ip);
433
434 if (!stralloc_copys(&querystr,"")) nomem();
435 uint16_unpack_big(type,&u16);
436 if (!stralloc_catulong0(&querystr,u16,0)) nomem();
437 if (!stralloc_cats(&querystr,":")) nomem();
438 if (!dns_domain_todot_cat(&querystr,q)) nomem();
439 if (!stralloc_cats(&querystr,":")) nomem();
440 if (!dns_domain_todot_cat(&querystr,control)) nomem();
441 if (!stralloc_cats(&querystr,":")) nomem();
442 if (!stralloc_catb(&querystr,ipstr,ip4_fmt(ipstr,ip))) nomem();
443 if (!stralloc_cats(&querystr,":")) nomem();
444
445 buffer_put(buffer_1,querystr.s,querystr.len);
446 buffer_puts(buffer_1,"tx\n");
447 buffer_flush(buffer_1);
448
449 if (resolve(q,type,ip) == -1) {
450 const char *x = error_str(errno);
451 buffer_put(buffer_1,querystr.s,querystr.len);
452 buffer_puts(buffer_1,"ALERT:query failed; ");
453 buffer_puts(buffer_1,x);
454 buffer_puts(buffer_1,"\n");
455 }
456 else
457 parsepacket(tx.packet,tx.packetlen,q,type,control);
458
459 if (dns_domain_equal(q,"\011localhost\0")) {
460 buffer_put(buffer_1,querystr.s,querystr.len);
461 buffer_puts(buffer_1,"ALERT:some caches do not handle localhost internally\n");
462 address_add(q,"\177\0\0\1");
463 }
464 if (dd(q,"",ip) == 4) {
465 buffer_put(buffer_1,querystr.s,querystr.len);
466 buffer_puts(buffer_1,"ALERT:some caches do not handle IP addresses internally\n");
467 address_add(q,ip);
468 }
469
470 buffer_flush(buffer_1);
471 }
472
473 _exit(0);
474}