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