dnstracer/CHANGES000644 001751 001751 00000006620 11023741350 014316 0ustar00edwinedwin000000 000000 $Id: CHANGES,v 1.26 2004/07/08 08:19:58 mavetju Exp $ Version 1.10: - Add support for EDNS0 (extra big packets) - Add support for querying of ANY records. - Add represention of SRV, RP records. Version 1.9: - With the rollout of IPv6 for a.root-servers.net, it always took the IPv6 address even if you used the -4 option. - Fix false lame server issues with domains which have the higher domain in it (command.com for example). Version 1.8.1: - Put back timeout command line parameter, it will be the maximum time to wait instead of an increasing one. Version 1.8: - Better handling of timeout (instead of a static value, start with five seconds and double it each retry) - Warnings are printed if you receive a different RR type than the one you asked for (for example when you ask for an A record and receive a CNAME) - Fixed problems when records received in the authority field are shorter than the domain the nameserver is authoritative for. - When compiled without IPv6 support on an IPv6 capable machine the machine tried to query the IPv6 translated IPv5 address anyway. - -C caches hosts which haven't answered too. Version 1.7.1: - Fix display of SOA serial number, by Janne Snabb - Non-IPv6 builds should not use gethostbyname2() Version 1.7: - Removed a debug-line while doing PTR requests. - Now IPv6 servers are traced too. Use -4 to disable it. - Added -S option for specifying source IP address. version 1.6: - Added SOA record tracing - Fixed a debug duplicate variable printing. version 1.5: - Sometimes it happened while tracing a name without specifying a server it would return immediatly without printing any information. - names with a trailing dot are now handled correctly. - warning from Kris Kennaway regarding sprintf()'s without format-strings (yes, I feel ashamed). - On request of Anthony DeRobertis , via Thomas Seyrat , support for PTR records. version 1.4: - Added -Wall to Makefile.am so at least I would get warnings from my C compiler thank you. Removed some stupid unused variables. - Fixed big/little endian problem in the structs so that the flags are looking allright now. - Answers are displayed as authoritative or normal. - Digging goes on until we have hit an authorative server. - Info about which zone is being queried now. version 1.3: - added win32 support by Mike Black - fixed bug where the IP addresses in the resource records wouldn't be used. - when an authority NS record wouldn't have an additional A record it can get multiple IP addresses when it resolves the hostname in the gethostbyname(). This is now handled correctly. version 1.2: - renamed into dnstracer due to clash with DJBDNS's dnstracer. - dnstracer would segfault if nameserver name couldn't be resolved (by Vilhelm Bergman ) - support for /etc/resolv.conf via a patch from Moritz Barsnick - -s . will take A.ROOT-SERVERS.NET - autoconf/automake support... *shudder* - support for different query-classes. - Gerd v. Egidy gave it RPM capabilities and checked the automake/autoconf construction. version 1.1: - fixed problems for big-endian little-endian machines. - fixed linking problem on Solaris. Use Makefile.sun for them. version 1.0: - initial release dnstracer/CONTACT000644 001751 001751 00000000630 10751710122 014334 0ustar00edwinedwin000000 000000 $Id: CONTACT,v 1.1 2002/01/21 00:40:23 mavetju Exp $ HOW TO CONTACT Via email: edwin@mavetju.org Via snail-mail: Edwin Groothuis 7 Islington Crescent Greenacre NSW2190 AUSTRALIA I have two mailing-lists: announce@lists.mavetju.org <- low traffic announcements only questions@lists.mavetju.org <- general questions See http://www.mavetju.org/contacts.php on how to subscribe to them. dnstracer/FILES000644 001751 001751 00000002266 10751710122 014112 0ustar00edwinedwin000000 000000 $Id: FILES,v 1.1 2002/01/25 01:40:09 mavetju Exp $ General ------- CHANGES - Change log CONTACT - How to contact me FILES - This file LICENSE - License under which this software is distributed README - Small manual Auto-configuration ------------------ Makefile.am - My Makefile-template configure.in - My configure-template Makefile.in - (Created by automake) aclocal.m4 - (Needed by autoconf) config.guess - (Needed by configure) config.sub - (Needed by configure) configure - Configure-file depcomp - (Needed by configure) install-sh - (Needed by configure) missing - (Needed by configure) mkinstalldirs - (Needed by configure) Source-file ----------- dnstracer.8 - Man-page, obtained from dnstracer.pod dnstracer.c - The source-file dnstracer.pod - The original man-file dnstracer.spec - Needed for RPM creation dnstracer_broken.h - Contains values for enumerations in case they are not defined in arpa/nameser.h WIN32 compatibility ------------------- MSVC.BAT - Microsoft Visual C++ mini-makefile getopt.c - Visual C++ doesn't provide these routines getopt.h - Visual C++ doesn't provide these routines (between brackets: not important, part of autoconfigure) dnstracer/LICENSE000644 001751 001751 00000002441 10751710122 014325 0ustar00edwinedwin000000 000000 Copyright 2002, 2003, 2004 by Edwin Groothuis, edwin@mavetju.org All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. dnstracer/MSVC.BAT000644 001751 001751 00000000333 10751710122 014416 0ustar00edwinedwin000000 000000 rem rem $Id: MSVC.BAT,v 1.1 2002/01/25 00:38:18 mavetju Exp $ rem rem mini-Makefile for dnstracer with Microsoft Visual C++ rem Supplied by Mike Black rem cl /DWIN32 dnstracer.c getopt.c wsock32.lib dnstracer/Makefile000644 001751 001751 00000001074 11023745127 014767 0ustar00edwinedwin000000 000000 MANDATE= June 10, 2008 MANSEC= 8 MANTITLE= Additional Software VERSION= 1.11 PREFIX= /usr/local/ BINPREFIX= ${PREFIX}/bin/ MANPREFIX= ${PREFIX}/man/man${MANSEC}/ all: dnstracer dnstracer.8 install: install dnstracer ${BINPREFIX} install dnstracer.8 ${MANPREFIX} clean: -rm dnstracer.o dnstracer dnstracer.8 dnstracer.core dnstracer: dnstracer.c dnstracer-missing.h $(CC) -Wall -o dnstracer -DVERSION="${VERSION}" dnstracer.c dnstracer.8: dnstracer.pod pod2man --release="${MANDATE}" --date="${MANDATE}" \ --center="${MANTITLE}" --section=${MANSEC} $? > $@ dnstracer/README000644 001751 001751 00000001752 10751710121 014203 0ustar00edwinedwin000000 000000 $Id: README,v 1.8 2003/01/06 22:40:31 mavetju Exp $ WHAT IS DNSTRACER? Dnstracer determines where a given Domain Name Server (DNS) gets its information from, and follows the chain of DNS servers back to the servers which know the data. Its behaviour is similair to ntptrace(8), which does it for the NTP protocol. HOW TO INSTALL? In theory, run 'configure', 'make' and 'make install' should be enough to install the binary and the man-page. I've developed it under FreeBSD 4.4 and tested it on Debian, SunOS and Solaris. Due to the nature of autoconf and friends, you need GNU Make. If you don't want to have IPv6 support in it, run configure with the --disable-ipv6 option. Windows users with Microsoft Visual C++ please use MSVB.BAT (supplied by Mike Black ) LICENSE BSD license. See the file LICENSE for more information. CONTACT My email address is edwin@mavetju.org My website is http://www.mavetju.org Mailing-lists are available: http://www.mavetju.org/contacts.php dnstracer/dnstracer-missing.h000644 001751 001751 00000002103 11023742501 017117 0ustar00edwinedwin000000 000000 // // $Id: dnstracer_broken.h,v 1.2 2002/01/22 01:54:46 mavetju Exp $ // // // Just when I thought life was good and everything was sweet, I found // out that the /usr/include/arpa/nameserv.h on my FreeBSD box was a // little bit different than that file on a Debian box. // // Let's assume that this is enough to convince everybody that these // variables do exist. // #ifndef ns_t_a #define ns_t_a 1 #endif #ifndef ns_t_ns #define ns_t_ns 2 #endif #ifndef ns_t_cname #define ns_t_cname 5 #endif #ifndef ns_t_soa #define ns_t_soa 6 #endif #ifndef ns_t_ptr #define ns_t_ptr 12 #endif #ifndef ns_t_hinfo #define ns_t_hinfo 13 #endif #ifndef ns_t_mx #define ns_t_mx 15 #endif #ifndef ns_t_txt #define ns_t_txt 16 #endif #ifndef ns_t_aaaa #define ns_t_aaaa 28 #endif #ifndef ns_t_srv #define ns_t_srv 33 #endif #ifndef ns_c_in #define ns_c_in 1 #endif #ifndef ns_c_chaos #define ns_c_chaos 3 #endif #ifndef ns_c_hs #define ns_c_hs 4 #endif #ifndef ns_c_none #define ns_c_none 254 #endif #ifndef ns_c_any #define ns_c_any 255 #endif #ifndef NS_MAXDNAME #define NS_MAXDNAME 1024 #endif dnstracer/dnstracer.c000644 001751 001751 00000130262 11023745055 015462 0ustar00edwinedwin000000 000000 // // $Id: dnstracer.c, v 1.48 2004/07/08 11:15:17 mavetju Exp $ // // // Copyright (c) 2002 by Edwin Groothuis, edwin@mavetju.org. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY FATAL DIMENSIONS AND CONTRIBUTORS ``AS // IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // STAFF OF FATAL DIMENSIONS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. // #ifdef WIN32 #include #include #include "getopt.h" // To reduce the amount of #ifdef's, these two are defined. // rand()/srand() is ISO C89 naming but it's obsoleted according // to the man-page. #define random rand #define srandom srand #else #include #include #include #include #include #include #include #include #include #endif #include #include #include #include #include #include "dnstracer-missing.h" #define DEFAULT_RETRIES 3 #define DEFAULT_CACHING 1 #define DEFAULT_NEGATIVE_CACHING 0 #define DEFAULT_OVERVIEW 0 #define DEFAULT_QUERYTYPE ns_t_a #define DEFAULT_NOIPV6 0 #define DEFAULT_EDNS0SIZE 1500 #ifdef NOIPV6 #define gethostbyname2(a, b) gethostbyname(a) #endif int verbose = 0; int global_overview = DEFAULT_OVERVIEW; int global_retries = DEFAULT_RETRIES; int global_caching = DEFAULT_CACHING; int global_negative_caching = DEFAULT_NEGATIVE_CACHING; int global_querytype = DEFAULT_QUERYTYPE; int global_noipv6 = DEFAULT_NOIPV6; int global_timeout = 0; int global_edns0 = DEFAULT_EDNS0SIZE; char * global_source_address = NULL; /*****************************************************************************/ struct arecord { char * server_name; char * server_ip; u_int16_t type; char * rr_name; char * rr_data; struct arecord * next; }; struct busy { char * server; struct busy * next; }; struct answer { char * server; struct answer * next; }; struct dnssession { struct dnsheader * send_header; // The DNS header being sent struct dnsquestion * send_question; // The DNS question being sent struct dnsheader * recv_header; // The DNS header received struct dnsquestion * recv_question; // The DNS question received struct dnsrr * send_additional;// First Additional RR send struct dnsrr * answer; // First Answer RR received struct dnsrr * authority; // First Authority RR received struct dnsrr * recv_additional;// First Additional RR received struct dnssession * next; char * server; // First server being queried char * host; // Hostname being queried int recv_len; // Raw packet with char * recv_pkt; // with pointers to... char * recv_pkt_header; // - header char * recv_pkt_question; // - question char * recv_pkt_answer; // - first answer RR char * recv_pkt_authority; // - first authority RR char * recv_pkt_additional; // - first additional RR int socket; int ipv6; }; struct dnsheader { u_int16_t identification; union { u_int16_t flags; struct bit { #ifdef WORDS_BIGENDIAN u_char qr:1; u_char opcode:4; u_char aa:1; u_char tc:1; u_char rd:1; u_char ra:1; u_char zero:3; u_char rcode:4; #else u_char rcode:4; u_char zero:3; u_char ra:1; u_char rd:1; u_char tc:1; u_char aa:1; u_char opcode:4; u_char qr:1; #endif } bit; } flags; u_int16_t nquestions; u_int16_t nanswerRR; u_int16_t nauthorityRR; u_int16_t nadditionalRR; }; struct dnsquestion { u_int querylength; u_char * query; u_int16_t type; u_int16_t class; }; struct dnsrr { u_char * domainname; u_int16_t type; u_int16_t class; u_int32_t ttl; u_int16_t datalength; u_char * data; u_char * domainname_string; // uncompressed version of DN u_char * data_string; // parsed version of data if possible struct dnsrr * next; }; #define MAX_RR_TYPES 256 char *rr_types[MAX_RR_TYPES] = { "#0x00", "a", "ns", "#0x03", "#0x04", "cname", "soa", "#0x07", "#0x08", "#0x09", "#0x0a", "#0x0b", "ptr", "hinfo", "#0x0e", "mx", "txt", "rp", "#0x12", "#0x13", "#0x14", "#0x15", "#0x16", "#0x17", "#0x18", "#0x19", "#0x1a", "#0x1b", "aaaa", "#0x1d", "#0x1e", "#0x1f", "#0x20", "srv", "0x22", "#0x23", "#0x24", "#0x25", "a6", "#0x27", "#0x28", "opt", "#0x2a", "#0x2b", "#0x2c", "#0x2d", "#0x2e", "#0x2f", "#0x30", "#0x31", "#0x32", "#0x33", "#0x34", "#0x35", "#0x36", "#0x37", "#0x38", "#0x39", "#0x3a", "#0x3b", "#0x3c", "#0x3d", "#0x3e", "#0x3f", "#0x40", "#0x41", "#0x42", "#0x43", "#0x44", "#0x45", "#0x46", "#0x47", "#0x48", "#0x49", "#0x4a", "#0x4b", "#0x4c", "#0x4d", "#0x4e", "#0x4f", "#0x50", "#0x51", "#0x52", "#0x53", "#0x54", "#0x55", "#0x56", "#0x57", "#0x58", "#0x59", "#0x5a", "#0x5b", "#0x5c", "#0x5d", "#0x5e", "#0x5f", "#0x60", "#0x61", "#0x62", "#0x63", "#0x64", "#0x65", "#0x66", "#0x67", "#0x68", "#0x69", "#0x6a", "#0x6b", "#0x6c", "#0x6d", "#0x6e", "#0x6f", "#0x70", "#0x71", "#0x72", "#0x73", "#0x74", "#0x75", "#0x76", "#0x77", "#0x78", "#0x79", "#0x7a", "#0x7b", "#0x7c", "#0x7d", "#0x7e", "#0x7f", "#0x80", "#0x81", "#0x82", "#0x83", "#0x84", "#0x85", "#0x86", "#0x87", "#0x88", "#0x89", "#0x8a", "#0x8b", "#0x8c", "#0x8d", "#0x8e", "#0x8f", "#0x90", "#0x91", "#0x92", "#0x93", "#0x94", "#0x95", "#0x96", "#0x97", "#0x98", "#0x99", "#0x9a", "#0x9b", "#0x9c", "#0x9d", "#0x9e", "#0x9f", "#0xa0", "#0xa1", "#0xa2", "#0xa3", "#0xa4", "#0xa5", "#0xa6", "#0xa7", "#0xa8", "#0xa9", "#0xaa", "#0xab", "#0xac", "#0xad", "#0xae", "#0xaf", "#0xb0", "#0xb1", "#0xb2", "#0xb3", "#0xb4", "#0xb5", "#0xb6", "#0xb7", "#0xb8", "#0xb9", "#0xba", "#0xbb", "#0xbc", "#0xbd", "#0xbe", "#0xbf", "#0xc0", "#0xc1", "#0xc2", "#0xc3", "#0xc4", "#0xc5", "#0xc6", "#0xc7", "#0xc8", "#0xc9", "#0xca", "#0xcb", "#0xcc", "#0xcd", "#0xce", "#0xcf", "#0xd0", "#0xd1", "#0xd2", "#0xd3", "#0xd4", "#0xd5", "#0xd6", "#0xd7", "#0xd8", "#0xd9", "#0xda", "#0xdb", "#0xdc", "#0xdd", "#0xde", "#0xdf", "#0xe0", "#0xe1", "#0xe2", "#0xe3", "#0xe4", "#0xe5", "#0xe6", "#0xe7", "#0xe8", "#0xe9", "#0xea", "#0xeb", "#0xec", "#0xed", "#0xee", "#0xef", "#0xf0", "#0xf1", "#0xf2", "#0xf3", "#0xf4", "#0xf5", "#0xf6", "#0xf7", "#0xf8", "#0xf9", "#0xfa", "#0xfb", "#0xfc", "#0xfd", "#0xfe", "any" }; /*****************************************************************************/ char *get_resource(u_int16_t type, struct dnssession *session, char *buffer, int dots); char *printablename(char *name, int withdots); /*****************************************************************************/ // // Extract information from received packets. // int strnrcasecmp(const char *big, const char *little, size_t len) { char *p; size_t lenl, lenb; lenl = strlen(little); lenb = strlen(big); if (lenl > lenb) return -1; if (len > lenl) return -1; p = (char *)big + lenb - len; return strncasecmp(p, little, len); } u_int32_t get32bit(u_char *s) { return 256*256*256*s[0] + 256*256*s[1] + 256*s[2] + s[3]; } u_int16_t get16bit(u_char *s) { return 256*s[0] + s[1]; } char * getname(struct dnssession *session, char **thisname) { int compressing = 0; char * p; static char hostname[NS_MAXDNAME]; // // Copy the name out of a received packet. It can be compressed. // // // If the name is empty, just return a dot. // if (*thisname[0] == 0) { strcpy(hostname, "\1."); (*thisname)++; return hostname; } p = *thisname; memset(hostname, 0, sizeof(hostname)); while (p[0] != 0) { // // If a name is being compressed, set the pointer to the start of // the packet plus the offset. If this is the first compression, // move the end-of-this-name pointer two places further and stop // changing it from now. // if ((p[0]&0xc0) != 0) { u_int16_t offset; char oldp; oldp = p[0]; p[0] &= 0x3f; offset = get16bit(p); p[0] = oldp; p = session->recv_pkt + offset; if (compressing == 0) *thisname += 2; compressing = 1; continue; } // // Just copy p[0]+1 bytes (+1 to save the length-byte). If // compression is not going on, move the end-of-this-name // pointer. The check for the next character being 0 is a // hack to make sure the end-of-name marked is skipped over // before returning the packet. // // memcpy(hostname + strlen(hostname), p, p[0] + 1); if (compressing == 0) { *thisname += p[0] + 1; if (*thisname[0] == 0) *thisname += 1; } p += p[0] + 1; } return hostname; } char * extract_rr(struct dnssession *session, char *thisrr, struct dnsrr **rr) { struct dnsrr * RR; char * domainname; char * p; RR = (struct dnsrr *)calloc(1, sizeof(struct dnsrr)); RR->next = *rr; // // Extract just one resource-record. // If the record is a ns_t_ns or ns_t_cname, cache the name // into data_string. // domainname = getname(session, &thisrr); RR->domainname = strdup(domainname); RR->domainname_string = strdup(printablename(domainname, 1)); RR->type = get16bit(thisrr); RR->class = get16bit(thisrr + 2); RR->ttl = get32bit(thisrr + 4); RR->datalength = get16bit(thisrr + 8); RR->data = (u_char *)calloc(1, RR->datalength); memcpy(RR->data, thisrr + 10, RR->datalength); p = thisrr + 10; RR->data_string = strdup(get_resource(RR->type, session, p, 1)); thisrr += 10 + RR->datalength; *rr = RR; return thisrr; } void extract_data(struct dnssession *session) { struct dnsheader * header = NULL; struct dnsquestion *question = NULL; struct dnsrr * answer = NULL; struct dnsrr * authority = NULL; struct dnsrr * additional = NULL; char * pheader; char * pquestion; char * panswer; char * pauthority; char * padditional; char *pbuffer; int i; pbuffer = session->recv_pkt; // // Extract the header. // session->recv_pkt_header = pbuffer; pheader = pbuffer; header = (struct dnsheader *)calloc(1, sizeof(struct dnsheader)); memcpy(header, session->recv_pkt, sizeof(struct dnsheader)); header->identification = ntohs(header->identification); header->flags.flags = ntohs(header->flags.flags); header->nquestions = ntohs(header->nquestions); header->nanswerRR = ntohs(header->nanswerRR); header->nauthorityRR = ntohs(header->nauthorityRR); header->nadditionalRR = ntohs(header->nadditionalRR); pbuffer += sizeof(struct dnsheader); // // Extract the questions RR. // session->recv_pkt_question = pbuffer; pquestion = pbuffer; question = (struct dnsquestion *)calloc(1, sizeof(struct dnsquestion)); question->query = strdup(getname(session, &pquestion)); question->type = get16bit(pquestion); question->class = get16bit(pquestion + 2); pbuffer = pquestion + 4; // // Extract the answer RR // session->recv_pkt_answer = pbuffer; for (i = 0; i < header->nanswerRR; i++) { panswer = pbuffer; pbuffer = extract_rr(session, panswer, &answer); } // // Extract the authority RR // session->recv_pkt_authority = pbuffer; for (i = 0; i < header->nauthorityRR; i++) { pauthority = pbuffer; pbuffer = extract_rr(session, pauthority, &authority); } // // Extract the additional RR // session->recv_pkt_additional = pbuffer; for (i = 0; i < header->nadditionalRR; i++) { padditional = pbuffer; pbuffer = extract_rr(session, padditional, &additional); } session->recv_header = header; session->recv_question = question; session->answer = answer; session->recv_additional = additional; session->authority = authority; } /*****************************************************************************/ // // Dump verbose data to the screen // char * printablename(char *name, int withdots) { static char hostname[NS_MAXDNAME]; char *p, q; // // Convert the name from label-format into 'human' readable format, // either with dots or the size of the label as seperators. // if (name == NULL || name[0] == 0) { if (withdots == 0) strcpy(hostname, "(0)root"); else strcpy(hostname, "."); return hostname; } hostname[0] = 0; p = name; while (p[0] != 0) { if (withdots == 0) sprintf(hostname + strlen(hostname), "(%d)", p[0]); else strcat(hostname, "."); q = p[p[0] + 1]; p[p[0] + 1] = 0; sprintf(hostname + strlen(hostname), "%s", p + 1); p = p + p[0] + 1; p[0] = q; } if (withdots == 0) return hostname; else return hostname + 1; // ignore the dot at the beginning of the string } char * get_class(u_int16_t class) { switch (class) { case ns_c_in: return "Internet"; case ns_c_chaos: return "MIT Chaos-net"; case ns_c_hs: return "MIT Hesiod"; case ns_c_none: return "Pre-req in update"; case ns_c_any: return "Wildcard"; default: return "unknown"; } } char * get_type(u_int16_t type) { switch (type) { case ns_t_a: return "A"; case ns_t_ns: return "NS"; case ns_t_cname: return "CNAME"; case ns_t_soa: return "SOA"; case ns_t_ptr: return "PTR"; case ns_t_any: return "ANY"; default: return "unknown"; } } char * get_ttl(u_int32_t ttl) { static char retval[NS_MAXDNAME]; // // Return the TTL as a weeks/days/hours/minuts/seconds value // retval[0] = 0; if (ttl > 7*24*60*60) { sprintf(retval, "%dw", ttl/(7*24*60*60)); ttl %= 7*24*60*60; } if (ttl > 24*60*60) { sprintf(retval + strlen(retval), "%dd", ttl/(24*60*60)); ttl %= 24*60*60; } if (ttl > 60*60) { sprintf(retval + strlen(retval), "%dh", ttl/(60*60)); ttl %= 60*60; } if (ttl > 60) { sprintf(retval + strlen(retval), "%dm", ttl/(60)); ttl %= 60; } if (ttl > 0) { sprintf(retval + strlen(retval), "%ds", ttl); } return retval; } char * get_resource(u_int16_t type, struct dnssession *session, char *buffer, int dots) { static char retval[NS_MAXDNAME]; // // Returns a parsed resource-data string. Only needed for // A, NS and CNAME records. // switch (type) { case ns_t_a: sprintf(retval, "%hu.%hu.%hu.%hu", (u_char)buffer[0], (u_char)buffer[1], (u_char)buffer[2], (u_char)buffer[3]); return retval; case ns_t_aaaa: sprintf(retval, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:" "%02x%02x:%02x%02x:%02x%02x:%02x%02x", (u_char)buffer[ 0], (u_char)buffer[ 1], (u_char)buffer[ 2], (u_char)buffer[ 3], (u_char)buffer[ 4], (u_char)buffer[ 5], (u_char)buffer[ 6], (u_char)buffer[ 7], (u_char)buffer[ 8], (u_char)buffer[ 9], (u_char)buffer[10], (u_char)buffer[11], (u_char)buffer[12], (u_char)buffer[13], (u_char)buffer[14], (u_char)buffer[15] ); return retval; case ns_t_hinfo: case ns_t_ns: case ns_t_ptr: case ns_t_cname: case ns_t_txt: case ns_t_rp: strcpy(retval, printablename(getname(session, &buffer), dots)); return retval; case ns_t_srv: { u_int16_t prio, weight, port; prio = get16bit(buffer); buffer += 2; weight = get16bit(buffer); buffer += 2; port = get16bit(buffer); buffer += 2; sprintf(retval, "%hu %hu %hu %s", prio, weight, port, printablename(getname(session, &buffer), dots)); return retval; } case ns_t_mx: { u_int16_t us; us = get16bit(buffer); buffer += 2; sprintf(retval, "%hu %s", us, printablename(getname(session, &buffer), dots)); return retval; } case ns_t_soa: { static char retval[3*NS_MAXDNAME]; char mname[NS_MAXDNAME]; char rname[NS_MAXDNAME]; u_int32_t ul; strcpy(mname, printablename(getname(session, &buffer), dots)); strcpy(rname, printablename(getname(session, &buffer), dots)); ul = get32bit(buffer); sprintf(retval, "serial: %ud mname: %s rname: %s", ul, mname, rname); return retval; } } sprintf(retval, "unknown type, see get_resource(0x%X)", type); return retval; } void dump_question(struct dnsquestion *question) { printf("- Queryname: %s\n", printablename(question->query, 0)); printf("- Type: %hu (%s)\n", question->type, get_type(question->type)); printf("- Class: %hu (%s)\n", question->class, get_class(question->class)); } void dump_header(struct dnsheader *header) { printf("- Identifier: 0x%04hX\n", header->identification); printf("- Flags: 0x%02hX (", header->flags.flags); if (header->flags.bit.qr) printf("R "); else printf("Q "); if (header->flags.bit.aa) printf("AA "); if (header->flags.bit.tc) printf("TC "); if (header->flags.bit.rd) printf("RD "); if (header->flags.bit.ra) printf("RA "); printf(")\n"); printf("- Opcode: %hu ", header->flags.bit.opcode); switch (header->flags.bit.opcode) { case 0: printf("(Standard query)\n");break; case 1: printf("(Inverse query)\n");break; case 2: printf("(Server status request)\n");break; case 4: printf("(Notify)\n");break; case 5: printf("(Update)\n");break; case 14: printf("(Zone init)\n");break; case 15: printf("(Zone Ref)\n");break; default: printf("(unknown)\n"); } printf("- Return code: %hu ", header->flags.bit.rcode); switch (header->flags.bit.rcode) { case 0: printf("(No error)\n");break; case 1: printf("(Format error)\n");break; case 2: printf("(Server failure)\n");break; case 3: printf("(Name error)\n");break; case 4: printf("(Not implemented)\n");break; case 5: printf("(Refused)\n");break; case 6: printf("(Name exists)\n");break; case 7: printf("(RRset exists)\n");break; case 8: printf("(RRset does not exist)\n");break; case 9: printf("(Not authoritive)\n");break; case 10: printf("(Zone of record different from zone section)\n");break; default: printf("(unknown:%d)\n", header->flags.bit.rcode);break; } printf("- Number questions: %hu\n", header->nquestions); printf("- Number answer RR: %hu\n", header->nanswerRR); printf("- Number authority RR: %hu\n", header->nauthorityRR); printf("- Number additional RR: %hu\n", header->nadditionalRR); } void dump_rr(struct dnsrr *rr, struct dnssession *session) { printf("- Domainname: %s\n", printablename(rr->domainname, 0)); printf("- Type: %hu (%s)\n", rr->type, get_type(rr->type)); printf("- Class: %hu (%s)\n", rr->class, get_class(rr->class)); printf("- TTL: %u (%s)\n", rr->ttl, get_ttl(rr->ttl)); printf("- Resource length: %hu\n", rr->datalength); printf("- Resource data: %s\n", get_resource(rr->type, session, rr->data, 0)); } void dump_data(struct sockaddr_in *dest4, struct sockaddr_in6 *dest6, struct dnssession *session) { struct dnsrr *answerrr; struct dnsrr *authorityrr; struct dnsrr *additionalrr; if (verbose == 0) return; // // Dumps the output of session on the screen. // if (dest4 != NULL) { printf("IP HEADER\n"); printf("- Destination address: %s\n", inet_ntoa(dest4->sin_addr)); } if (dest6 != NULL) { printf("IP HEADER\n"); printf("- Destination address: %s\n", "XXX"); } if (session->send_header != NULL && session->recv_header == NULL) { printf("DNS HEADER (send)\n"); dump_header(session->send_header); } if (session->recv_header != NULL) { printf("DNS HEADER (recv)\n"); dump_header(session->recv_header); } if (session->send_question != NULL && session->recv_question == NULL) { printf("QUESTIONS (send)\n"); dump_question(session->send_question); } if (session->send_additional != NULL && session->recv_question == NULL) { printf("ADDITIONAL (send)\n"); dump_rr(session->send_additional, session); } if (session->recv_question != NULL) { printf("QUESTIONS (recv)\n"); dump_question(session->recv_question); } answerrr = session->answer; while (answerrr != NULL) { printf("ANSWER RR\n"); dump_rr(answerrr, session); answerrr = answerrr->next; } authorityrr = session->authority; while (authorityrr != NULL) { printf("AUTHORITY RR\n"); dump_rr(authorityrr, session); authorityrr = authorityrr->next; } additionalrr = session->recv_additional; while (additionalrr != NULL) { printf("ADDITIONAL RR\n"); dump_rr(additionalrr, session); additionalrr = additionalrr->next; } } /*****************************************************************************/ // // Packet creation routines // u_char * create_packet(struct dnssession *session, int len) { u_char * pkt; struct dnsheader nheader; // networked header struct dnsquestion nquestion; // networked question u_int32_t offset, l; u_int16_t s; // // Transform a "host" packet into a "network" packet. // In other words, copy everything and byte-swap some field. // // len = sizeof(struct dnsheader) + session->send_question->querylength + 4; // if (global_edns0) len += 11; pkt = (u_char *)calloc(1, len); memcpy(&nheader, session->send_header, sizeof(struct dnsheader)); memcpy(&nquestion, session->send_question, sizeof(struct dnsquestion)); nheader.identification = htons(session->send_header->identification); nheader.flags.flags = htons(session->send_header->flags.flags); nheader.nquestions = htons(session->send_header->nquestions); nheader.nanswerRR = htons(session->send_header->nanswerRR); nheader.nauthorityRR = htons(session->send_header->nauthorityRR); nheader.nadditionalRR = htons(session->send_header->nadditionalRR); nquestion.type = htons(session->send_question->type); nquestion.class = htons(session->send_question->class); memcpy(pkt, &nheader, sizeof(struct dnsheader)); offset = sizeof(struct dnsheader);; memcpy(pkt + offset, nquestion.query, nquestion.querylength); offset += nquestion.querylength; memcpy(pkt + offset, &(nquestion.type), 4); offset += 4; if (global_edns0) { *(pkt + offset++) = '\0'; s = htons(session->send_additional->type); memcpy(pkt + offset, &s, 2); offset += 2; s = htons(session->send_additional->class); memcpy(pkt + offset, &s, 2); offset += 2; l = htons(session->send_additional->ttl); memcpy(pkt + offset, &l, 4); offset += 4; s = htons(session->send_additional->datalength); memcpy(pkt + offset, &s, 2); offset += 2; } return pkt; } /*****************************************************************************/ // // Network routines // int create_socket(int PF) { int s; if ((s = socket(PF, SOCK_DGRAM, 0)) < 0) { perror("create_socket/socket"); printf("If this is an IPv6 problem, run configure with --disable-ipv6\n"); exit(1); } if (global_source_address != NULL) { struct addrinfo hints, *src_res; int error; // thanks to the src/usr.bin/telnet/commands.c of FreeBSD 4.7! memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_NUMERICHOST; hints.ai_family = PF_INET; hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo(global_source_address, 0, &hints, &src_res); if (error == EAI_NONAME) { hints.ai_flags = 0; error = getaddrinfo(global_source_address, 0, &hints, &src_res); } if (error != 0) { perror(global_source_address); if (error == EAI_SYSTEM) perror(global_source_address); exit(1); } if (bind(s, src_res->ai_addr, src_res->ai_addrlen) < 0) { perror("create_socket/bind"); exit(1); } } return s; } int send_data(char *server, struct dnssession *session) { int cc; char * pkt; int len; struct sockaddr_in dest4; #ifndef NOIPV6 struct sockaddr_in6 dest6; #endif len = sizeof(struct dnsheader) + session->send_question->querylength + 4; if (global_edns0) len += 11; #ifndef NOIPV6 if (session->ipv6) { memset(&dest6, 0, sizeof(struct sockaddr_in6)); dest6.sin6_family = AF_INET6; dest6.sin6_port = htons(53); inet_pton(AF_INET6, server, &dest6.sin6_addr); dump_data(NULL, &dest6, session); } else #endif { memset(&dest4, 0, sizeof(struct sockaddr_in)); dest4.sin_family = AF_INET; dest4.sin_port = htons(53); dest4.sin_addr.s_addr = inet_addr(server); dump_data(&dest4, NULL, session); } pkt = create_packet(session, len); if ((cc = sendto(session->socket, pkt, len, 0, #ifndef NOIPV6 session->ipv6 ? (struct sockaddr *)&dest6 : (struct sockaddr *)&dest4, session->ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in) #else (struct sockaddr *)&dest4, sizeof(struct sockaddr_in) #endif )) == -1) { perror("send_data/sendto"); } free(pkt); return cc; } int receive_data(struct dnssession *session, int retry) { char buffer[2048]; ssize_t len; fd_set in_set; struct timeval timeout; timeout.tv_sec = 5 * (1 << retry); timeout.tv_usec = 0; if (global_timeout && timeout.tv_sec > global_timeout) timeout.tv_sec = global_timeout; FD_ZERO(&in_set); FD_SET(session->socket, &in_set); if (select(session->socket + 1, &in_set, NULL, NULL, &timeout) < 0) return 2; if (!FD_ISSET(session->socket, &in_set)) return 3; if ((len = recv(session->socket, buffer, sizeof(buffer), 0)) == -1) return 1; if (get16bit(buffer) != session->send_header->identification) { fprintf(stderr, "Expected id: %hx, received id: %hx\n", session->send_header->identification, get16bit(buffer)); return 4; } session->recv_len = len; session->recv_pkt = (char *)calloc(1, len); memcpy(session->recv_pkt, buffer, len); extract_data(session); dump_data(NULL, NULL, session); return 0; } /*****************************************************************************/ // // Record creation routines // void create_header(struct dnssession *session) { session->send_header = (struct dnsheader *)calloc(1, sizeof(struct dnsheader)); // // Create a random identifier between 0 and 32675. It could be up to // 65535, but the high bit sometimes screws things up when comparing // the value received. Looks like it has something to do with // one-complement and two-complement, but don't know how to solve it. // session->send_header->identification = random() & 0x7F7F; session->send_header->nquestions = 1; if (global_edns0) session->send_header->nadditionalRR = 1; } void create_question(struct dnssession *session, char *name) { char *p, *q; session->send_question = (struct dnsquestion *)calloc(1, sizeof(struct dnsquestion)); session->send_question->querylength = strlen(name) + 2; session->send_question->query = (u_char *)calloc(1, session->send_question->querylength + 2); strcpy(session->send_question->query + 1, name); p = session->send_question->query + 1; q = session->send_question->query; while (p[0] != 0) { if (p[0] == '.') { q[0] = p - q - 1; q = p; } p++; } q[0] = p - q - 1; session->send_question->type = global_querytype; session->send_question->class = ns_c_in; } void create_edns0(struct dnssession *session) { if (!global_edns0) return; session->send_additional = (struct dnsrr *)calloc(1, sizeof(struct dnsrr)); session->send_additional->domainname = NULL; session->send_additional->type = ns_t_opt; session->send_additional->class = global_edns0; session->send_additional->ttl = 0; session->send_additional->datalength = 0; session->send_additional->data = NULL; session->send_additional->next = NULL; } /*****************************************************************************/ // // A record caching routines. // It's just a linked list which hold a bunch of IP address from which // we have gotten answers. // struct arecord *arecords = NULL; void add_arecord(struct dnssession *session, struct dnsrr *rr, char *server_name, char *server_ip) { struct arecord * arecord; arecord = (struct arecord *)calloc(1, sizeof(struct arecord)); arecord->server_name = strdup(server_name); arecord->server_ip = strdup(server_ip); arecord->rr_name = strdup(printablename(rr->domainname, 1)); if (rr->data_string == NULL) arecord->rr_data = NULL; else arecord->rr_data = strdup(rr->data_string); arecord->next = arecords; arecords = arecord; } void display_arecords(void) { struct arecord * arecord; int i; char s[10]; arecord = arecords; while (arecord != NULL) { printf("%s (%s)%n", arecord->server_name, arecord->server_ip, &i); if (40 - i < 1) printf(" "); else { sprintf(s, "%%%ds", 40 - i); printf(s, " "); } printf("%s -> %s\n", arecord->rr_name, arecord->rr_data); arecord = arecord->next; } } /*****************************************************************************/ // // Answer caching routines. // It's just a linked list which hold a bunch of IP address from which // we have gotten answers. // struct answer *answers = NULL; void add_answer(char *server) { struct answer *answer; answer = (struct answer *)calloc(1, sizeof(struct answer)); answer->server = strdup(server); answer->next = answers; answers = answer; } int has_answer(char *server) { struct answer *answer; answer = answers; while (answer != NULL) { if (strcmp(answer->server, server) == 0) return 1; answer = answer->next; } return 0; } /*****************************************************************************/ // // Busy signal routines. // It's just a linked list which hold a bunch of IP address of servers // we are currently querying. Just to prevent that we query until we're // out of memory. // struct busy *busies = NULL; void add_busy(char *server) { struct busy *busy; busy = (struct busy *)calloc(1, sizeof(struct busy)); busy->server = strdup(server); busy->next = busies; busies = busy; } void remove_busy(char *server) { struct busy *busy, *prev; if (strcmp(busies->server, server) == 0) { busy = busies; busies = busies->next; free(busy); return; } prev = busies; busy = prev->next; while (busy != NULL) { if (strcmp(busy->server, server) == 0) { prev->next = busy->next; free(busy); return; } } } int is_busy(char *server) { struct busy *busy; busy = busies; while (busy != NULL) { if (strcmp(busy->server, server) == 0) return 1; busy = busy->next; } return 0; } /*****************************************************************************/ // // The core of this program // int create_session(char *host, char *server_ip, int ipv6, char *server_name, char *server_authfor, int depth, char *prefix, int last) { struct dnssession * session; struct dnsrr * rrauth; struct dnsrr * rradd; int i, retval, errorcode, refersbackwards = 0; char s[NS_MAXDNAME]; // // Print the graphs in front of the servernames // if (depth != 0) { printf("%s%c", prefix, last == 1 ? ' ' : '|'); printf("\\___ "); } printf("%s ", server_name); if (server_authfor != NULL) printf("[%s] ", server_authfor); if (server_ip[0] == 0) { printf("(No IP address)"); return 1; } if (global_noipv6 && ipv6) { printf("(%s) Not queried", server_ip); return 1; } #ifdef NOIPV6 if (ipv6) { printf("(%s) Not queried", server_ip); return 1; } #endif printf("(%s) ", server_ip); fflush(stdout); // // If this address is already being queried, ignore it to prevent // recursion. Do not add it as being cached, because one or more // records in it might be unreachable. // if (is_busy(server_ip) == 1) { printf("Lame server "); fflush(stdout); return 0; } // // Ignore things we have already displayed // if (global_caching && has_answer(server_ip)) { printf("(cached)"); return 0; } // // To prevent infinite recursion, mark this server as being probed // add_busy(server_ip); // // Create a nice DNS packet. Each session has its own port, so we // don't have to worry about packets received from previous sessions. // session = (struct dnssession *)calloc(1, sizeof(struct dnssession)); session->socket = create_socket(ipv6 ? AF_INET6 : AF_INET); session->ipv6 = ipv6; session->server = strdup(server_ip); session->host = strdup(host); create_header(session); create_question(session, host); create_edns0(session); // // Send the packet and wait for an answer. // errorcode = 0; for (i = 0; i < global_retries; i++) { send_data(server_ip, session); if ((errorcode = receive_data(session, i)) == 0) break; printf("* "); fflush(stdout); } close(session->socket); // // Timeouts and other weird stuff. Make sure we remove the busy-flag. // if (errorcode != 0) { remove_busy(server_ip); if (global_negative_caching) add_answer(server_ip); return 1; } // // We have an answerRR from this server, print a message. // Also cache it for later. // if (session->recv_header->nanswerRR != 0) { struct dnsrr *answer; if (session->recv_header->flags.bit.aa) printf("Got authoritative answer "); else printf("Got answer "); answer = session->answer; while (answer != NULL) { if (answer->type != global_querytype) printf("[received type is %s] ", rr_types[answer->type]); add_arecord(session, answer, server_name, server_ip); answer = answer->next; } if (global_caching) add_answer(server_ip); } // // If the domainname in the authority section is the same as // the one from this server, mark it as lame. // if (session->authority != NULL && server_authfor != NULL) { if (session->recv_header->flags.bit.aa == 0 && strcasecmp(server_authfor, session->authority->domainname_string) == 0) { printf("Lame server "); remove_busy(server_ip); return 0; } } // // If the current server has an authoritative answer, don't go // further. // if (session->recv_header->flags.bit.aa) { remove_busy(server_ip); return 0; } // // When no answers were received, go through the list of authorities // and ask them for it. Maybe the IP address of the authority was // in the additional RRs, maybe there are two IP address in // the list of additional RRs, maybe there are none and a // gethostbyname() has to be done. // retval = 0; rrauth = session->authority; while (rrauth != NULL) { int found = 0; char nextserver_ip[NS_MAXDNAME]; char nextserver_name[NS_MAXDNAME]; // // Only serve NS records // if (rrauth->type != ns_t_ns) { rrauth = rrauth->next; continue; } // // If the domainname in the authority section is not a postfix // of what we have, don't go there. This might happen if we are // looking through cnames from different domains. // if (strcmp(rrauth->domainname_string, ".") == 0) { rrauth = rrauth->next; if (!refersbackwards++) printf("Refers backwards "); continue; } if (server_authfor != NULL && strcmp(server_authfor, ".") != 0 && strnrcasecmp(rrauth->domainname_string, server_authfor, strlen(server_authfor)) != 0) { if (!refersbackwards++) printf("Refers backwards "); rrauth = rrauth->next; continue; } // // Count the number of IP addresses in the additionalRR // for this authorityRR // rradd = session->recv_additional; while (rradd != NULL) { if (strcmp(printablename(rradd->domainname, 1), rrauth->data_string) == 0) found++; rradd = rradd->next; } rradd = session->recv_additional; do { // // Find the first IP address // while (rradd != NULL) { if (strcmp(printablename(rradd->domainname, 1), rrauth->data_string) == 0) break; rradd = rradd->next; } // // Prepare the graphs in front of the servers name // sprintf(s, "%s%c%s", prefix, last == 1 ? ' ' : '|', depth == 0 ? "" : " "); // // Recurse into this server... // if (rradd != NULL) { // This is easy, we got the IP address in the additional // section. Don't worry about additional records with the // same name, they're done automaticly after this one. printf("\n"); strcpy(nextserver_name, printablename(rradd->domainname, 1)); strcpy(nextserver_ip, rradd->data_string); retval += create_session(host, nextserver_ip, (rradd->type == ns_t_aaaa) ? 1 : 0, nextserver_name, rrauth->domainname_string, depth + 1, s, (rrauth->next == NULL && found <= 1) ? 1 : 0); } else { int ip, ipfound = 0; strcpy(nextserver_name, rrauth->data_string); #ifdef NOIPV6 for (ip = 0; ip < 1; ip++) { #else for (ip = 0; ip < 2; ip++) { #endif int count, i; struct hostent *h; char **addr_list = NULL; h = gethostbyname2(nextserver_name, ip == 0 ? AF_INET : AF_INET6); if (h == NULL) continue; // // One or more IP address were found. Go through all // of them (make sure they're saved before calling // gethostbyname() again). // count = 0; while (h->h_addr_list[count] != NULL) count++; addr_list = (char **)calloc(count, sizeof(char *)); for (i = 0; i < count; i++) { addr_list[i] = (char *)calloc(1, h->h_length); memcpy(addr_list[i], h->h_addr_list[i], h->h_length); } for (i = 0; i < count; i++) { if (ip == 0) { u_char *s = addr_list[i]; sprintf(nextserver_ip, "%hu.%hu.%hu.%hu", s[0], s[1], s[2], s[3]); ipv6 = 0; } else { u_char *s = addr_list[i]; sprintf(nextserver_ip, "%02hx%02hx:%02hx%02hx:%02hx%02hx:%02hx%02hx:" "%02hx%02hx:%02hx%02hx:%02hx%02hx:%02hx%02hx", s[ 0], s[ 1], s[ 2], s[ 3], s[ 4], s[ 5], s[ 6], s[ 7], s[ 8], s[ 9], s[10], s[11], s[12], s[13], s[14], s[15]); ipv6 = 1; } printf("\n"); retval += create_session(host, nextserver_ip, ip == 1, nextserver_name, rrauth->domainname_string, depth + 1, s, (rrauth->next == NULL && found <= 1) ? 1 : 0); ipfound++; } } if (ipfound == 0) { // // No IP address was found for this hostname. // Just call the function and let them print // an error. // printf("\n"); nextserver_ip[0] = 0; retval += create_session(host, nextserver_ip, 0, nextserver_name, rrauth->domainname_string, depth + 1, s, (rrauth->next == NULL && found <= 1) ? 1 : 0); } } // // If there are no more IP addresses, then do the next // authorityRR. // if (--found <= 0) break; if (rradd != NULL) rradd = rradd->next; } while (rradd != NULL); rrauth = rrauth->next; } // // Cache it for later if there were no servers which went wrong. // Also remove the busy flag. // if (global_caching && retval == 0) add_answer(server_ip); remove_busy(server_ip); return retval; } /*****************************************************************************/ // // Startup, usage and win32-initialization // Win32-initialization by Mike Black // void usage(void) { char *version = "1.10"; fprintf(stderr, "DNSTRACER version %s - (c) Edwin Groothuis - http://www.mavetju.org\n" "Usage: dnstracer [options] [host]\n" "\t-c: disable local caching, default enabled\n" "\t-C: enable negative caching, default disabled\n" "\t-e: disable EDNS0, default enabled\n" "\t-E : set EDNS0 size, default 1500\n" "\t-o: enable overview of received answers, default disabled\n" "\t-q : query-type to use for the DNS requests, default A\n" "\t-r : amount of retries for DNS requests, default 3\n" "\t-s : use this server for the initial request, default localhost\n" "\t If . is specified, A.ROOT-SERVERS.NET will be used.\n" "\t-t : Limit time to wait per try\n" "\t-v: verbose\n" "\t-S : use this source address.\n", version ); #ifndef NOIPV6 fprintf(stderr, "\t-4: don't query IPv6 servers\n" ); #endif exit(1); } #ifdef WIN32 int wsockinit(void) { WSADATA wsaData; /* Structure for WinSock setup communication */ if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { /* Load Winsock 2.0 DLL */ fprintf(stderr, "WSAStartup() failed"); exit(1); } return 1; } #endif int main(int argc, char **argv) { int ch; char * server_name = "127.0.0.1"; char * server_ip = "0000:0000:0000:0000:0000:0000:0000:0000"; char ipaddress[NS_MAXDNAME]; char argv0[NS_MAXDNAME]; int server_root = 0; int ipv6 = 0; #ifndef WIN32 // // Get the first nameserver from /etc/resolv.conf // // This piece of code was donated by Moritz Barsnick. // if (!(_res.options & RES_INIT)) res_init(); if (_res.nscount > 0) { server_ip = strdup(inet_ntoa(_res.nsaddr_list[0].sin_addr)); server_name = strdup(server_ip); } #endif #ifdef WIN32 wsockinit(); #endif while ((ch = getopt(argc, argv, "4cCeE:oq:r:S:s:t:v")) != -1) { switch (ch) { case '4': #ifndef NOIPV6 global_noipv6 = 1; #else printf("Option -4 ignored\n"); #endif break; case 'c': global_caching = 0; break; case 'C': global_negative_caching = 1; break; case 'e': global_edns0 = 0; break; case 'E': global_edns0 = atoi(optarg); break; case 'o': global_overview = 1; break; case 'q': if ((global_querytype = atoi(optarg)) < 1) { int i; for (i = 0; i < MAX_RR_TYPES; i++) { if (strcmp(rr_types[i], optarg) == 0) { global_querytype = i; break; } } if (global_querytype < 1) { fprintf(stderr, "Strange querytype, setting to default\n"); global_querytype = DEFAULT_QUERYTYPE; } } break; case 'r': if ((global_retries = atoi(optarg)) < 1) { fprintf(stderr, "Strange amount of retries, setting to default\n"); global_retries = DEFAULT_RETRIES; } break; case 'S': global_source_address = optarg; break; case 's': server_name = optarg; if (strcmp(server_name, ".") == 0) { server_name = strdup("A.ROOT-SERVERS.NET"); server_root = 1; } break; case 't': global_timeout = atoi(optarg); break; case 'v': verbose = 1; break; default: usage(); } } argc -= optind; argv += optind; if (argv[0] == NULL) usage(); // check for a trailing dot strcpy(argv0, argv[0]); if (argv0[strlen(argv[0]) - 1] == '.') argv0[strlen(argv[0]) - 1] = 0; printf("Tracing to %s[%s] via %s, maximum of %d retries\n", argv0, rr_types[global_querytype], server_name, global_retries); srandom(time(NULL)); { struct hostent *h = NULL; #ifndef NOIPV6 h = gethostbyname2(server_name, AF_INET6); #endif if (h == NULL || global_noipv6) h = gethostbyname2(server_name, AF_INET); if (h == NULL) { fprintf(stderr, "Cannot find IP address for %s\n", server_name); return 1; } if (h->h_addrtype == AF_INET) { u_char *s = h->h_addr_list[0]; sprintf(ipaddress, "%hu.%hu.%hu.%hu", s[0], s[1], s[2], s[3]); ipv6 = 0; } else { u_char *s = h->h_addr_list[0]; sprintf(ipaddress, "%02hx%02hx:%02hx%02hx:%02hx%02hx:%02hx%02hx:" "%02hx%02hx:%02hx%02hx:%02hx%02hx:%02hx%02hx", s[ 0], s[ 1], s[ 2], s[ 3], s[ 4], s[ 5], s[ 6], s[ 7], s[ 8], s[ 9], s[10], s[11], s[12], s[13], s[14], s[15]); ipv6 = 1; } } create_session(argv0, ipaddress, ipv6, server_name, server_root == 0 ? NULL : ".", 0, "", 1); printf("\n"); if (global_overview != 0) { printf("\n"); display_arecords(); } return 0; } dnstracer/dnstracer.pod000644 001751 001751 00000005547 11023742547 016034 0ustar00edwinedwin000000 000000 # # $Id: dnstracer.pod,v 1.19 2004/07/08 11:15:42 mavetju Exp $ # =head1 NAME B - trace a chain of DNS servers to the source =head1 SYNOPSIS B [B] I =head1 DESCRIPTION B determines where a given Domain Name Server (DNS) gets its information from, and follows the chain of DNS servers back to the servers which know the data. Options are: =over 8 =item B<-c> Disable local caching. =item B<-C> Enable negative caching. =item B<-e> Disable EDNS0 options. =item B<-E I> Set EDNS0 size, default is 1500 bytes. =item B<-o> Enable overview of received answers at the end. =item B<-q I> Change the query-class, default is A. You can either specify a number of the type (if you're brave) or one of the following strings: a, aaaa, a6, soa, cname, hinfo, mx, ns, txt and ptr. =item B<-r I> Number of retries for DNS requests, default 3. =item B<-s I> DNS server to use for the initial request, default is aquired from the system. If a dot is specified (.), A.ROOT-SERVERS.NET will be used. =item B<-v> Be verbose on what sent or received. =item B<-4> Use only IPv4 servers, don't query IPv6 servers (only available when IPv6 support hasn't been disabled) =item B<-S I> Use this as source-address for the outgoing packets. =back =head1 HOW IT WORKS It sends the specified name-server a non-recursive request for the name. Non-recursive means: if the name-server knows it, it will return the data requested. If the name-server doesn't know it, it will return pointers to name-servers that are authoritive for the domain part in the name or it will return the addresses of the root name-servers. If the name server does returns an authoritative answer for the name, the next server is queried. If it returns an non-authoritative answer for the name, the name servers in the authority records will be queried. The program stops if all name-servers are queried. Make sure the server you're querying doesn't do forwarding towards other servers, as dnstracer is not able to detect this for you. It detects so called lame servers, which are name-servers which has been told to have information about a certain domain, but don't have this information. =head1 EXAMPLES Search for the A record of www.mavetju.org on your local nameserver: dnstracer www.mavetju.org Search for the MX record of mavetju.org on the root-nameservers: dnstracer C<-s> . C<-q> mx mavetju.org Search for the PTR record (hostname) of 212.204.230.141: dnstracer C<-q> ptr 141.230.204.212.in-addr.arpa And for IPv6 addresses: dnstracer C<-q> ptr C<-s> . C<-o> 2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.6.4.0.2.0.0.0.0.8.b.0.e.f.f.3.ip6.arpa =head1 SEE ALSO ntptrace(8), traceroute(8), dig(1) =head1 AUTHOR Edwin Groothuis, edwin@mavetju.org (http://www.mavetju.org) See http://www.mavetju.org/contacts.php for mailing-lists. dnstracer/getopt.c000644 001751 001751 00000012726 10751710121 014774 0ustar00edwinedwin000000 000000 // // $Id: getopt.c,v 1.1 2002/01/25 00:38:18 mavetju Exp $ // // Only used in the win32-version of dnstracer. // Supplied by Mike Black // /* * Copyright (C) 1994 Arno Schaefer * * AU: Auswertung der Kommandozeile, der POSIX-Version von getopt () * nachempfunden. * * PO: ANSI C */ /*----------------------------------------------------------------- | | FILE: getopt.c | | PURPOSE: standard getopt() for platforms that don't have it. | | | ROUTINES: | getopt() parse args from command string | | | AUTHOR: Arno Schaefer | | DATE: 1994 | | MODIFICATIONS: | Revision 1.2 2001/11/05 20:56:04 dan | Update to establish common header format | | | +---------------------------------------------------------------*/ #include #include #include "getopt.h" /* Globale Variablen */ char *optarg; int optind = 1; int opterr = 1; int optopt; static char *nextarg = NULL; /* Funktion */ int getopt (int argc, char *argv[], char *optstring) /* * AU: Auswertung der Kommandozeile * * VB: argc und argv sind die Parameter, die an main () uebergeben werden. * optstring ist ein String, der die Zeichen enthaelt, die als * Optionen erkannt werden. Wenn ein Zeichen von einem Doppelpunkt * gefolgt wird, hat die Option ein Argument, das direkt auf das Zeichen * folgt oder durch Space davon getrennt ist. Gueltige Optionszeichen * sind alle druckbaren Zeichen ausser '?', ' ' und ':'. * * optind ist der Index auf das naechste Element von argv[], das * bearbeitet wird. * * opterr ist ein Flag, das festlegt, ob bei Fehlern Fehlermeldungen * ausgegeben werden. * * optarg ist ein Zeiger auf das Argument, wenn eine Option ein * Argument hat. * * optopt enthaelt bei Fehlern das Optionszeichen, das den Fehler aus- * geloest hat. * * NB: Rueckgabewert ist das jeweils naechste Optionszeichen, oder -1 am * Ende der Optionsliste. * * Die Optionsliste ist zu Ende, wenn argv[optind] NULL ist, oder * argv[optind] nicht mit '-' (oder '/') beginnt, oder argv[optind] * ein einzelnes "-" ist. In diesem Fall wird optind nicht erhoeht. * Das Ende der Optionsliste kann mit "--" erzwungen werden, dann ist * argv[optind] das erste Argument nach "--". * * FB: Ein '?' wird zurueckgegeben, wenn ein Optionszeichen nicht in * optstring enthalten war oder ein ungueltiges Optionszeichen * uebergeben wurde ('?' oder ':'). Ausserdem bei einem fehlenden * Argument, wenn das erste Zeichen von optstring kein ':' ist. * * Ein ':' wird zurueckgegeben bei einem fehlenden Argument, wenn * das erste Zeichen von optstring ein ':' ist. */ { char *search; optarg = NULL; if (nextarg == NULL) { nextarg = argv[optind]; if (nextarg == NULL) { return (-1); } #ifdef __MSDOS__ if (*nextarg != '-' && *nextarg != '/') #else if (*nextarg != '-') #endif { return (-1); } nextarg++; } /* if */ optopt = *nextarg++; if (optopt == 0) { return (-1); } optind++; if (optopt == '-' && *nextarg == 0) { return (-1); } if (optopt == ':' || optopt == '?') { if (opterr) { fprintf ( stderr, "%s: illegal option -- %c\n", argv[0], optopt ); } return ('?'); } /* if */ search = strchr (optstring, optopt); if (search == NULL) { if (opterr) { fprintf ( stderr, "%s: illegal option -- %c\n", argv[0], optopt ); } return ('?'); } /* if */ if (*nextarg == 0) { nextarg = NULL; } if (search[1] != ':') { if (nextarg != NULL) { optind--; } return (optopt); } if (nextarg != NULL) { optarg = nextarg; nextarg = NULL; return (optopt); } optarg = argv[optind]; if (optind == argc) { if (opterr) { fprintf ( stderr, "%s: option requires an argument -- %c\n", argv[0], optopt ); } /* if */ if (optstring[0] == ':') { return (':'); } else { return ('?'); } } /* if */ else { optind++; } return (optopt); } /* end getopt() */ dnstracer/getopt.h000644 001751 001751 00000001017 10751710121 014770 0ustar00edwinedwin000000 000000 // // $Id: getopt.h,v 1.1 2002/01/25 00:38:19 mavetju Exp $ // // Only used in the win32-version of dnstracer. // Supplied by Mike Black // /* * Copyright (C) 1994 Arno Schaefer * * AU: Prototypen und externe Variablen fuer getopt () * * PO: ANSI C */ #ifndef GETOPT_H #define GETOPT_H #ifdef __cplusplus extern "C" { #endif extern char *optarg; extern int optind; extern int opterr; extern int optopt; int getopt (int argc, char *argv[], char *optstring); #ifdef __cplusplus } #endif #endif