#include #include #include #include #include #include #include #include #include #include extern const char *__progname; static int flag_mx = 0; static char *name = 0; static struct sockaddr **addr_l; static int addr_a; static int addr_n; static int set_name(char *s) { if (name) { fprintf(stderr,"%s: name already specified, using later value\n",__progname); name = s; return(1); } name = s; return(0); } static void handleargs(int ac, char **av) { __label__ nextarg; int skip; int errs; void WANTARG(void) { if (++skip >= ac) { fprintf(stderr,"%s: %s needs another argument\n",__progname,*av); errs ++; goto nextarg; } } skip = 0; errs = 0; for (ac--,av++;ac;ac--,av++) { if (skip > 0) { skip --; continue; } if (**av != '-') { errs += set_name(*av); continue; } if (!strcmp(*av,"-mx")) { flag_mx = 1; continue; } if (!strcmp(*av,"-host")) { WANTARG(); errs += set_name(av[skip]); continue; } fprintf(stderr,"%s: unrecognized flag %s\n",__progname,*av); errs ++; nextarg:; } if (errs) exit(1); } static int dns_skip(const void *dbuf, int len, int off) { const unsigned char *d; int i; d = dbuf; while (1) { if (off >= len) return(-1); i = d[off]; switch (i & 0xc0) { case 0: if (i) off += i + 1; else return(off+1); break; case 0xc0: if (off+1 >= len) return(-1); return(off+2); break; default: return(-1); break; } } } static void query_it(const char *name, int wanttype, void (*fn)(const char *, const void *, const void *, const void *, int)) { int namelen; int rl; int n; int x; int i; int nl; int rdl; int type; int class; unsigned char resp[8192]; unsigned char compbuf[1024]; unsigned char nbuf[1024]; int exp(int inx) { return(dn_expand((const void *)&resp[0],(const void *)&resp[rl],(const void *)&resp[inx],(void *)&compbuf[0],sizeof(compbuf))); } bzero(&resp[0],12); /* XXX res_query doesn't check response length (!!) */ rl = res_search(name,C_IN,wanttype,(void *)&resp[0],sizeof(resp)); if (rl <= 0) { switch (h_errno) { case HOST_NOT_FOUND: return; break; case NO_DATA: return; break; } } if (rl < 12) { fprintf(stderr,"%s: response is too short (%d)\n",name,rl); return; } if (resp[3] & 2) { fprintf(stderr,"%s: response is truncated\n",name); return; } n = (resp[6] * 256) + resp[7]; if (n == 0) return; x = 12; for (i=(resp[4]*256)+resp[5];i>0;i--) { x = dns_skip(&resp[0],rl,x); if (x < 0) { fprintf(stderr,"%s: response contains invalid compressed name\n",name); return; } x += 4; if (x >= rl) { fprintf(stderr,"%s: response queries overflow packet\n",name); return; } } namelen = strlen(name); for (i=n;i>0;i--) { nl = exp(x); if (nl < 0) { fprintf(stderr,"%s: response includes invalid compressed name\n",name); return; } x += nl + 8 + 2; if (x >= rl) { fprintf(stderr,"%s: response answers overflow packet\n",name); return; } rdl = (resp[x-2] * 256) + resp[x-1]; class = (resp[x-8] * 256) + resp[x-7]; if (class == C_IN) { type = (resp[x-10] * 256) + resp[x-9]; if (type == T_CNAME) { if ( strcasecmp(&compbuf[0],name) && ( strncasecmp(&compbuf[0],name,namelen) || (compbuf[namelen] != '.') ) ) { fprintf(stderr,"%s: name %s, got %s\n",__progname,name,&compbuf[0]); } nl = exp(x); if (nl < 0) { fprintf(stderr,"%s: response includes invalid compressed name\n",name); return; } if (nl != rdl) { fprintf(stderr,"%s: response CNAME data length wrong\n",name); return; } strcpy(&nbuf[0],&compbuf[0]); name = &nbuf[0]; namelen = strlen(name); } else if (type == wanttype) { if ( strcasecmp(&compbuf[0],name) && ( strncasecmp(&compbuf[0],name,namelen) || (compbuf[namelen] != '.') ) ) { fprintf(stderr,"%s: name %s, got %s\n",__progname,name,&compbuf[0]); } (*fn)(name,(const void *)&resp[0],(const void *)&resp[rl],(const void *)&resp[x],rdl); } } x += rdl; } } static void save_addr(const struct sockaddr *sa) { int i; void *b; for (i=addr_n-1;i>=0;i--) { if ( (sa->sa_family == addr_l[i]->sa_family) && (sa->sa_len == addr_l[i]->sa_len) && !bcmp(sa,addr_l[i],sa->sa_len) ) return; } if (addr_n >= addr_a) addr_l = realloc(addr_l,(addr_a=addr_n+8)*sizeof(*addr_l)); b = malloc(sa->sa_len); bcopy(sa,b,sa->sa_len); addr_l[addr_n++] = b; } static void save_addr4(const char *qname, const void *pktbase __attribute__((__unused__)), const void *pktend __attribute__((__unused__)), const void *rrn, int rdl) { struct sockaddr_in sin; if (rdl != 4) { fprintf(stderr,"%s: A data length wrong (%d)\n",qname,rdl); return; } bzero(&sin,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_len = sizeof(sin); sin.sin_port = 0; bcopy(rrn,&sin.sin_addr,4); save_addr((const struct sockaddr *)&sin); } static void save_addr6(const char *qname, const void *pktbase __attribute__((__unused__)), const void *pktend __attribute__((__unused__)), const void *rrn, int rdl) { struct sockaddr_in6 sin6; if (rdl != 16) { fprintf(stderr,"%s: AAAA data length wrong (%d)\n",qname,rdl); return; } bzero(&sin6,sizeof(sin6)); sin6.sin6_family = AF_INET6; sin6.sin6_len = sizeof(sin6); sin6.sin6_port = 0; bcopy(rrn,&sin6.sin6_addr,16); save_addr((const struct sockaddr *)&sin6); } static void got_mx(const char *qname, const void *pktbase, const void *pktend, const void *rrn, int rdl) { unsigned char xbuf[1024]; int xl; if (rdl <= 2) { fprintf(stderr,"%s: MX data length too short (%d)\n",qname,rdl); return; } xl = dn_expand(pktbase,pktend,2+(const char *)rrn,(void *)&xbuf[0],sizeof(xbuf)); if (xl < 0) { fprintf(stderr,"%s: MX data includes invalid compressed name\n",qname); return; } if (rdl != xl+2) { fprintf(stderr,"%s: MX data length wrong\n",qname); return; } query_it(&xbuf[0],T_A,&save_addr4); query_it(&xbuf[0],T_AAAA,&save_addr6); } static void print_addrs(void) { int i; struct sockaddr *sa; char txt[64]; const char *s; for (i=0;isa_family) { case AF_INET: s = inet_ntop(AF_INET,&((struct sockaddr_in *)sa)->sin_addr,&txt[0],sizeof(txt)); break; case AF_INET6: s = inet_ntop(AF_INET6,&((struct sockaddr_in6 *)sa)->sin6_addr,&txt[0],sizeof(txt)); break; default: abort(); break; } if (! s) { fprintf(stderr,"%s: error converting to text: %s\n",__progname,strerror(errno)); exit(1); } printf("%s\n",s); } } int main(int, char **); int main(int ac, char **av) { if (ac == 1) { fprintf(stderr,"Usage: %s [-mx] [-host ]hostname\n",__progname); exit(1); } handleargs(ac,av); if (! name) { fprintf(stderr,"%s: need a name\n",__progname); exit(1); } addr_l = 0; addr_n = 0; addr_a = 0; res_init(); if (flag_mx) { query_it(name,T_MX,&got_mx); } else { query_it(name,T_A,&save_addr4); query_it(name,T_AAAA,&save_addr6); } print_addrs(); exit(0); }