#include "uint16.h"
#include "uint32.h"
#include "fmt.h"
#include "str.h"
#include "byte.h"
#include "ip4.h"
#include "gen_alloc.h"
#include "gen_allocdefs.h"
#include "exit.h"
#include "buffer.h"
#include "stralloc.h"
#include "error.h"
#include "strerr.h"
#include "iopause.h"
#include "printrecord.h"
#include "alloc.h"
#include "parsetype.h"
#include "dd.h"
#include "dns.h"
#define FATAL "dnstrace: fatal: "
void nomem(void)
{
strerr_die2x(111,FATAL,"out of memory");
}
void usage(void)
{
strerr_die1x(100,"dnstrace: usage: dnstrace type name rootip ...");
}
static stralloc querystr;
char ipstr[IP4_FMT];
static stralloc tmp;
void printdomain(const char *d)
{
if (!stralloc_copys(&tmp,"")) nomem();
if (!dns_domain_todot_cat(&tmp,d)) nomem();
buffer_put(buffer_1,tmp.s,tmp.len);
}
static struct dns_transmit tx;
int resolve(char *q,char qtype[2],char ip[4])
{
struct taia start;
struct taia stamp;
struct taia deadline;
char servers[64];
iopause_fd x[1];
int r;
taia_now(&start);
byte_zero(servers,64);
byte_copy(servers,4,ip);
if (dns_transmit_start(&tx,servers,0,q,qtype,"\0\0\0\0") == -1) return -1;
for (;;) {
taia_now(&stamp);
taia_uint(&deadline,120);
taia_add(&deadline,&deadline,&stamp);
dns_transmit_io(&tx,x,&deadline);
iopause(x,1,&deadline,&stamp);
r = dns_transmit_get(&tx,x,&stamp);
if (r == -1) return -1;
if (r == 1) break;
}
taia_now(&stamp);
taia_sub(&stamp,&stamp,&start);
taia_uint(&deadline,1);
if (taia_less(&deadline,&stamp)) {
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"ALERT:took more than 1 second\n");
}
return 0;
}
struct address {
char *owner;
char ip[4];
} ;
GEN_ALLOC_typedef(address_alloc,struct address,s,len,a)
GEN_ALLOC_readyplus(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus)
GEN_ALLOC_append(address_alloc,struct address,s,len,a,i,n,x,30,address_alloc_readyplus,address_alloc_append)
static address_alloc address;
struct ns {
char *owner;
char *ns;
} ;
GEN_ALLOC_typedef(ns_alloc,struct ns,s,len,a)
GEN_ALLOC_readyplus(ns_alloc,struct ns,s,len,a,i,n,x,30,ns_alloc_readyplus)
GEN_ALLOC_append(ns_alloc,struct ns,s,len,a,i,n,x,30,ns_alloc_readyplus,ns_alloc_append)
static ns_alloc ns;
struct query {
char *owner;
char type[2];
} ;
GEN_ALLOC_typedef(query_alloc,struct query,s,len,a)
GEN_ALLOC_readyplus(query_alloc,struct query,s,len,a,i,n,x,30,query_alloc_readyplus)
GEN_ALLOC_append(query_alloc,struct query,s,len,a,i,n,x,30,query_alloc_readyplus,query_alloc_append)
static query_alloc query;
struct qt {
char *owner;
char type[2];
char *control;
char ip[4];
} ;
GEN_ALLOC_typedef(qt_alloc,struct qt,s,len,a)
GEN_ALLOC_readyplus(qt_alloc,struct qt,s,len,a,i,n,x,30,qt_alloc_readyplus)
GEN_ALLOC_append(qt_alloc,struct qt,s,len,a,i,n,x,30,qt_alloc_readyplus,qt_alloc_append)
static qt_alloc qt;
void qt_add(const char *q,const char type[2],const char *control,const char ip[4])
{
struct qt x;
int i;
if (!*q) return; /* don't ask the roots about our artificial . host */
for (i = 0;i < qt.len;++i)
if (dns_domain_equal(qt.s[i].owner,q))
if (dns_domain_equal(qt.s[i].control,control))
if (byte_equal(qt.s[i].type,2,type))
if (byte_equal(qt.s[i].ip,4,ip))
return;
byte_zero(&x,sizeof x);
if (!dns_domain_copy(&x.owner,q)) nomem();
if (!dns_domain_copy(&x.control,control)) nomem();
byte_copy(x.type,2,type);
byte_copy(x.ip,4,ip);
if (!qt_alloc_append(&qt,&x)) nomem();
}
void query_add(const char *owner,const char type[2])
{
struct query x;
int i;
int j;
for (i = 0;i < query.len;++i)
if (dns_domain_equal(query.s[i].owner,owner))
if (byte_equal(query.s[i].type,2,type))
return;
byte_zero(&x,sizeof x);
if (!dns_domain_copy(&x.owner,owner)) nomem();
byte_copy(x.type,2,type);
if (!query_alloc_append(&query,&x)) nomem();
for (i = 0;i < ns.len;++i)
if (dns_domain_suffix(owner,ns.s[i].owner))
for (j = 0;j < address.len;++j)
if (dns_domain_equal(ns.s[i].ns,address.s[j].owner))
qt_add(owner,type,ns.s[i].owner,address.s[j].ip);
}
void ns_add(const char *owner,const char *server)
{
struct ns x;
int i;
int j;
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"NS:");
printdomain(owner);
buffer_puts(buffer_1,":");
printdomain(server);
buffer_puts(buffer_1,"\n");
for (i = 0;i < ns.len;++i)
if (dns_domain_equal(ns.s[i].owner,owner))
if (dns_domain_equal(ns.s[i].ns,server))
return;
query_add(server,DNS_T_A);
byte_zero(&x,sizeof x);
if (!dns_domain_copy(&x.owner,owner)) nomem();
if (!dns_domain_copy(&x.ns,server)) nomem();
if (!ns_alloc_append(&ns,&x)) nomem();
for (i = 0;i < query.len;++i)
if (dns_domain_suffix(query.s[i].owner,owner))
for (j = 0;j < address.len;++j)
if (dns_domain_equal(server,address.s[j].owner))
qt_add(query.s[i].owner,query.s[i].type,owner,address.s[j].ip);
}
void address_add(const char *owner,const char ip[4])
{
struct address x;
int i;
int j;
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"A:");
printdomain(owner);
buffer_puts(buffer_1,":");
buffer_put(buffer_1,ipstr,ip4_fmt(ipstr,ip));
buffer_puts(buffer_1,"\n");
for (i = 0;i < address.len;++i)
if (dns_domain_equal(address.s[i].owner,owner))
if (byte_equal(address.s[i].ip,4,ip))
return;
byte_zero(&x,sizeof x);
if (!dns_domain_copy(&x.owner,owner)) nomem();
byte_copy(x.ip,4,ip);
if (!address_alloc_append(&address,&x)) nomem();
for (i = 0;i < ns.len;++i)
if (dns_domain_equal(ns.s[i].ns,owner))
for (j = 0;j < query.len;++j)
if (dns_domain_suffix(query.s[j].owner,ns.s[i].owner))
qt_add(query.s[j].owner,query.s[j].type,ns.s[i].owner,ip);
}
char seed[128];
static char *t1;
static char *t2;
static char *referral;
static char *cname;
static int typematch(const char rtype[2],const char qtype[2])
{
return byte_equal(qtype,2,rtype) || byte_equal(qtype,2,DNS_T_ANY);
}
void parsepacket(const char *buf,unsigned int len,const char *d,const char dtype[2],const char *control)
{
char misc[20];
char header[12];
unsigned int pos;
uint16 numanswers;
unsigned int posanswers;
uint16 numauthority;
unsigned int posauthority;
uint16 numglue;
unsigned int posglue;
uint16 datalen;
unsigned int rcode;
int flagout;
int flagcname;
int flagreferral;
int flagsoa;
int j;
const char *x;
pos = dns_packet_copy(buf,len,0,header,12); if (!pos) goto DIE;
pos = dns_packet_skipname(buf,len,pos); if (!pos) goto DIE;
pos += 4;
uint16_unpack_big(header + 6,&numanswers);
uint16_unpack_big(header + 8,&numauthority);
uint16_unpack_big(header + 10,&numglue);
rcode = header[3] & 15;
if (rcode && (rcode != 3)) { errno = error_proto; goto DIE; } /* impossible */
flagout = 0;
flagcname = 0;
flagreferral = 0;
flagsoa = 0;
posanswers = pos;
for (j = 0;j < numanswers;++j) {
pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
if (dns_domain_equal(t1,d))
if (byte_equal(header + 2,2,DNS_C_IN))
if (typematch(header,dtype))
flagout = 1;
else if (typematch(header,DNS_T_CNAME)) {
if (!dns_packet_getname(buf,len,pos,&cname)) goto DIE;
flagcname = 1;
}
uint16_unpack_big(header + 8,&datalen);
pos += datalen;
}
posauthority = pos;
for (j = 0;j < numauthority;++j) {
pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
if (typematch(header,DNS_T_SOA))
flagsoa = 1;
else if (typematch(header,DNS_T_NS)) {
flagreferral = 1;
if (!dns_domain_copy(&referral,t1)) goto DIE;
}
uint16_unpack_big(header + 8,&datalen);
pos += datalen;
}
posglue = pos;
if (!flagcname && !rcode && !flagout && flagreferral && !flagsoa)
if (dns_domain_equal(referral,control) || !dns_domain_suffix(referral,control)) {
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"ALERT:lame server; refers to ");
printdomain(referral);
buffer_puts(buffer_1,"\n");
return;
}
pos = posanswers;
for (j = 0;j < numanswers + numauthority + numglue;++j) {
pos = dns_packet_getname(buf,len,pos,&t1); if (!pos) goto DIE;
pos = dns_packet_copy(buf,len,pos,header,10); if (!pos) goto DIE;
uint16_unpack_big(header + 8,&datalen);
if (dns_domain_suffix(t1,control))
if (byte_equal(header + 2,2,DNS_C_IN)) {
if (typematch(header,DNS_T_NS)) {
if (!dns_packet_getname(buf,len,pos,&t2)) goto DIE;
ns_add(t1,t2);
}
else if (typematch(header,DNS_T_A) && datalen == 4) {
if (!dns_packet_copy(buf,len,pos,misc,4)) goto DIE;
address_add(t1,misc);
}
}
pos += datalen;
}
if (flagcname) {
query_add(cname,dtype);
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"CNAME:");
printdomain(cname);
buffer_puts(buffer_1,"\n");
return;
}
if (rcode == 3) {
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"NXDOMAIN\n");
return;
}
if (flagout || flagsoa || !flagreferral) {
if (!flagout) {
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"NODATA\n");
return;
}
pos = posanswers;
for (j = 0;j < numanswers + numauthority + numglue;++j) {
pos = printrecord(&tmp,buf,len,pos,d,dtype);
if (!pos) goto DIE;
if (tmp.len) {
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"answer:");
buffer_put(buffer_1,tmp.s,tmp.len); /* includes \n */
}
}
return;
}
if (!dns_domain_suffix(d,referral)) goto DIE;
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"see:");
printdomain(referral);
buffer_puts(buffer_1,"\n");
return;
DIE:
x = error_str(errno);
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"ALERT:unable to parse response packet; ");
buffer_puts(buffer_1,x);
buffer_puts(buffer_1,"\n");
}
int main(int argc,char **argv)
{
static stralloc out;
static stralloc fqdn;
static stralloc udn;
static char *q;
char *control;
char type[2];
char ip[64];
int i;
uint16 u16;
dns_random_init(seed);
if (!stralloc_copys(&querystr,"0:.:.:start:")) nomem();
if (!address_alloc_readyplus(&address,1)) nomem();
if (!query_alloc_readyplus(&query,1)) nomem();
if (!ns_alloc_readyplus(&ns,1)) nomem();
if (!qt_alloc_readyplus(&qt,1)) nomem();
if (!*argv) usage();
if (!*++argv) usage();
if (!parsetype(*argv,type)) usage();
if (!*++argv) usage();
if (!dns_domain_fromdot(&q,*argv,str_len(*argv))) nomem();
query_add(q,type);
ns_add("","");
while (*++argv) {
if (!stralloc_copys(&udn,*argv)) nomem();
if (dns_ip4_qualify(&out,&fqdn,&udn) == -1) nomem(); /* XXX */
for (i = 0;i + 4 <= out.len;i += 4)
address_add("",out.s + i);
}
for (i = 0;i < qt.len;++i) {
if (!dns_domain_copy(&q,qt.s[i].owner)) nomem();
control = qt.s[i].control;
if (!dns_domain_suffix(q,control)) continue;
byte_copy(type,2,qt.s[i].type);
byte_copy(ip,4,qt.s[i].ip);
if (!stralloc_copys(&querystr,"")) nomem();
uint16_unpack_big(type,&u16);
if (!stralloc_catulong0(&querystr,u16,0)) nomem();
if (!stralloc_cats(&querystr,":")) nomem();
if (!dns_domain_todot_cat(&querystr,q)) nomem();
if (!stralloc_cats(&querystr,":")) nomem();
if (!dns_domain_todot_cat(&querystr,control)) nomem();
if (!stralloc_cats(&querystr,":")) nomem();
if (!stralloc_catb(&querystr,ipstr,ip4_fmt(ipstr,ip))) nomem();
if (!stralloc_cats(&querystr,":")) nomem();
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"tx\n");
buffer_flush(buffer_1);
if (resolve(q,type,ip) == -1) {
const char *x = error_str(errno);
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"ALERT:query failed; ");
buffer_puts(buffer_1,x);
buffer_puts(buffer_1,"\n");
}
else
parsepacket(tx.packet,tx.packetlen,q,type,control);
if (dns_domain_equal(q,"\011localhost\0")) {
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"ALERT:some caches do not handle localhost internally\n");
address_add(q,"\177\0\0\1");
}
if (dd(q,"",ip) == 4) {
buffer_put(buffer_1,querystr.s,querystr.len);
buffer_puts(buffer_1,"ALERT:some caches do not handle IP addresses internally\n");
address_add(q,ip);
}
buffer_flush(buffer_1);
}
_exit(0);
}
syntax highlighted by Code2HTML, v. 0.9.1