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
nomem(void)23 void nomem(void)
24 {
25 strerr_die2x(111,FATAL,"out of memory");
26 }
usage(void)27 void usage(void)
28 {
29 strerr_die1x(100,"dnstrace: usage: dnstrace type name rootip ...");
30 }
31
32 static stralloc querystr;
33 char ipstr[IP4_FMT];
34 static stralloc tmp;
35
printdomain(const char * d)36 void 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
43 static struct dns_transmit tx;
44
resolve(char * q,char qtype[2],char ip[4])45 int 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
83 struct address {
84 char *owner;
85 char ip[4];
86 } ;
87
88 GEN_ALLOC_typedef(address_alloc,struct address,s,len,a)
89 GEN_ALLOC_readyplus(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus)
90 GEN_ALLOC_append(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus,address_alloc_append)
91
92 static address_alloc address;
93
94 struct ns {
95 char *owner;
96 char *ns;
97 } ;
98
99 GEN_ALLOC_typedef(ns_alloc,struct ns,s,len,a)
100 GEN_ALLOC_readyplus(ns_alloc,struct ns,s,len,a,i,n,x,30,ns_alloc_readyplus)
101 GEN_ALLOC_append(ns_alloc,struct ns,s,len,a,i,n,x,30,ns_alloc_readyplus,ns_alloc_append)
102
103 static ns_alloc ns;
104
105 struct query {
106 char *owner;
107 char type[2];
108 } ;
109
110 GEN_ALLOC_typedef(query_alloc,struct query,s,len,a)
111 GEN_ALLOC_readyplus(query_alloc,struct query,s,len,a,i,n,x,30,query_alloc_readyplus)
112 GEN_ALLOC_append(query_alloc,struct query,s,len,a,i,n,x,30,query_alloc_readyplus,query_alloc_append)
113
114 static query_alloc query;
115
116 struct qt {
117 char *owner;
118 char type[2];
119 char *control;
120 char ip[4];
121 } ;
122
123 GEN_ALLOC_typedef(qt_alloc,struct qt,s,len,a)
124 GEN_ALLOC_readyplus(qt_alloc,struct qt,s,len,a,i,n,x,30,qt_alloc_readyplus)
125 GEN_ALLOC_append(qt_alloc,struct qt,s,len,a,i,n,x,30,qt_alloc_readyplus,qt_alloc_append)
126
127 static qt_alloc qt;
128
qt_add(const char * q,const char type[2],const char * control,const char ip[4])129 void 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
query_add(const char * owner,const char type[2])151 void 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
ns_add(const char * owner,const char * server)174 void 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
address_add(const char * owner,const char ip[4])206 void 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
236 char seed[128];
237
238 static char *t1;
239 static char *t2;
240 static char *referral;
241 static char *cname;
242
typematch(const char rtype[2],const char qtype[2])243 static 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
parsepacket(const char * buf,unsigned int len,const char * d,const char dtype[2],const char * control)248 void 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
main(int argc,char ** argv)389 int 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 }
475