#include #include #include #include #include #include #include #include #include #include "ndbhf.h" static void nstrcpy(char*, char*, int); static void mkptrname(char*, char*, int); static Ndbtuple *doquery(char*, char*); /* * Run a DNS lookup for val/type on net. */ Ndbtuple* dnsquery(char *net, char *val, char *type) { static int init; char rip[128]; Ndbtuple *t; USED(net); if(!init){ init = 1; fmtinstall('I', eipfmt); } /* give up early on stupid questions - vwhois */ if(strcmp(val, "::") == 0 || strcmp(val, "0.0.0.0") == 0) return nil; /* zero out the error string */ werrstr(""); /* if this is a reverse lookup, first look up the domain name */ if(strcmp(type, "ptr") == 0){ mkptrname(val, rip, sizeof rip); t = doquery(rip, "ptr"); }else t = doquery(val, type); return t; } /* * convert address into a reverse lookup address */ static void mkptrname(char *ip, char *rip, int rlen) { char buf[128]; char *p, *np; int len; if(strstr(ip, "in-addr.arpa") || strstr(ip, "IN-ADDR.ARPA")){ nstrcpy(rip, ip, rlen); return; } nstrcpy(buf, ip, sizeof buf); for(p = buf; *p; p++) ; *p = '.'; np = rip; len = 0; while(p >= buf){ len++; p--; if(*p == '.'){ memmove(np, p+1, len); np += len; len = 0; } } memmove(np, p+1, len); np += len; strcpy(np, "in-addr.arpa"); } static void nstrcpy(char *to, char *from, int len) { strncpy(to, from, len); to[len-1] = 0; } /* * Disgusting, ugly interface to libresolv, * which everyone seems to have. */ enum { MAXRR = 100, MAXDNS = 4096, }; static int name2type(char*); static uchar *skipquestion(uchar*, uchar*, uchar*, int); static uchar *unpack(uchar*, uchar*, uchar*, Ndbtuple**, int); static uchar *rrnext(uchar*, uchar*, uchar*, Ndbtuple**); static Ndbtuple *rrunpack(uchar*, uchar*, uchar**, char*, ...); static Ndbtuple* doquery(char *name, char *type) { int n, nstype; uchar *buf, *p; HEADER *h; Ndbtuple *t; if((nstype = name2type(type)) < 0){ werrstr("unknown dns type %s", type); return nil; } buf = malloc(MAXDNS); if(buf == nil) return nil; if((n = res_search(name, ns_c_in, nstype, buf, MAXDNS)) < 0){ free(buf); return nil; } if(n >= MAXDNS){ free(buf); werrstr("too much dns information"); return nil; } h = (HEADER*)buf; h->qdcount = ntohs(h->qdcount); h->ancount = ntohs(h->ancount); h->nscount = ntohs(h->nscount); h->arcount = ntohs(h->arcount); p = buf+sizeof(HEADER); p = skipquestion(buf, buf+n, p, h->qdcount); p = unpack(buf, buf+n, p, &t, h->ancount); USED(p); return t; } static struct { char *s; int t; } dnsnames[] = { "ip", ns_t_a, "ns", ns_t_ns, "md", ns_t_md, "mf", ns_t_mf, "cname", ns_t_cname, "soa", ns_t_soa, "mb", ns_t_mb, "mg", ns_t_mg, "mr", ns_t_mr, "null", ns_t_null, "ptr", ns_t_ptr, "hinfo", ns_t_hinfo, "minfo", ns_t_minfo, "mx", ns_t_mx, "txt", ns_t_txt, "rp", ns_t_rp, "key", ns_t_key, "cert", ns_t_cert, "sig", ns_t_sig, "aaaa", ns_t_aaaa, "ixfr", ns_t_ixfr, "axfr", ns_t_axfr, "all", ns_t_any, }; static char* type2name(int t) { int i; for(i=0; ientry = t; else first = t; for(last=t; last->entry; last=last->entry) last->line = last->entry; last->line = t; } *tt = first; return p; } #define G2(p) nhgets(p) #define G4(p) nhgetl(p) static uchar* rrnext(uchar *buf, uchar *ebuf, uchar *p, Ndbtuple **tt) { char tmp[Ndbvlen]; char b[MAXRR]; uchar ip[IPaddrlen]; int len; Ndbtuple *first, *t; int rrtype; int rrlen; first = nil; t = nil; *tt = nil; if(p == nil) return nil; if((len = dn_expand(buf, ebuf, p, b, sizeof b)) < 0){ corrupt: werrstr("corrupt dns packet"); if(first) ndbfree(first); return nil; } p += len; rrtype = G2(p); rrlen = G2(p+8); p += 10; if(rrtype == ns_t_ptr) first = ndbnew("ptr", b); else first = ndbnew("dom", b); switch(rrtype){ default: goto end; case ns_t_hinfo: t = rrunpack(buf, ebuf, &p, "YY", "cpu", "os"); break; case ns_t_minfo: t = rrunpack(buf, ebuf, &p, "NN", "mbox", "mbox"); break; case ns_t_mx: t = rrunpack(buf, ebuf, &p, "SN", "pref", "mx"); break; case ns_t_cname: case ns_t_md: case ns_t_mf: case ns_t_mg: case ns_t_mr: case ns_t_mb: case ns_t_ns: case ns_t_ptr: case ns_t_rp: t = rrunpack(buf, ebuf, &p, "N", type2name(rrtype)); break; case ns_t_a: if(rrlen != IPv4addrlen) goto corrupt; memmove(ip, v4prefix, IPaddrlen); memmove(ip+IPv4off, p, IPv4addrlen); snprint(tmp, sizeof tmp, "%I", ip); t = ndbnew("ip", tmp); p += rrlen; break; case ns_t_aaaa: if(rrlen != IPaddrlen) goto corrupt; snprint(tmp, sizeof tmp, "%I", ip); t = ndbnew("ip", tmp); p += rrlen; break; case ns_t_null: snprint(tmp, sizeof tmp, "%.*H", rrlen, p); t = ndbnew("null", tmp); p += rrlen; break; case ns_t_txt: t = rrunpack(buf, ebuf, &p, "Y", "txt"); break; case ns_t_soa: t = rrunpack(buf, ebuf, &p, "NNLLLLL", "ns", "mbox", "serial", "refresh", "retry", "expire", "ttl"); break; case ns_t_key: t = rrunpack(buf, ebuf, &p, "SCCY", "flags", "proto", "alg", "key"); break; case ns_t_sig: t = rrunpack(buf, ebuf, &p, "SCCLLLSNY", "type", "alg", "labels", "ttl", "exp", "incep", "tag", "signer", "sig"); break; case ns_t_cert: t = rrunpack(buf, ebuf, &p, "SSCY", "type", "tag", "alg", "cert"); break; } if(t == nil) goto corrupt; end: first->entry = t; *tt = first; return p; } static Ndbtuple* rrunpack(uchar *buf, uchar *ebuf, uchar **pp, char *fmt, ...) { char *name; int len, n; uchar *p; va_list arg; Ndbtuple *t, *first, *last; char tmp[Ndbvlen]; p = *pp; va_start(arg, fmt); first = nil; last = nil; for(; *fmt; fmt++){ name = va_arg(arg, char*); switch(*fmt){ default: return nil; case 'C': snprint(tmp, sizeof tmp, "%d", *p++); break; case 'S': snprint(tmp, sizeof tmp, "%d", G2(p)); p += 2; break; case 'L': snprint(tmp, sizeof tmp, "%d", G4(p)); p += 4; break; case 'N': if((len = dn_expand(buf, ebuf, p, tmp, sizeof tmp)) < 0) return nil; p += len; break; case 'Y': len = *p++; n = len; if(n >= sizeof tmp) n = sizeof tmp-1; memmove(tmp, p, n); p += len; tmp[n] = 0; break; } t = ndbnew(name, tmp); if(last) last->entry = t; else first = t; last = t; } *pp = p; return first; }