1690 lines
48 KiB
C
1690 lines
48 KiB
C
//
|
|
// Copyright (c) 2016 by Carsten Larsen
|
|
// 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.
|
|
//
|
|
|
|
//
|
|
// Original file version:
|
|
// $Id: dnstracer.c, v 1.48 2004/07/08 11:15:17 mavetju Exp $
|
|
//
|
|
|
|
#include <unistd.h>
|
|
#include <sys/time.h>
|
|
#include <sys/param.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <arpa/nameser.h>
|
|
#include <netdb.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
|
|
#include "dnstracer.h"
|
|
#include "dnstracer_broken.h"
|
|
#include "getaddrinfo.h"
|
|
#include "mem.h"
|
|
|
|
#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;
|
|
char * global_source_address = NULL;
|
|
|
|
/*****************************************************************************/
|
|
|
|
struct arecord {
|
|
char * server_name;
|
|
char * server_ip;
|
|
int 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 * answer; // First Answer RR received
|
|
struct dnsrr * authority; // First Authority RR received
|
|
struct dnsrr * 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 {
|
|
unsigned short identification;
|
|
union {
|
|
unsigned short flags;
|
|
struct bit {
|
|
#ifdef WORDS_BIGENDIAN
|
|
unsigned char qr:1;
|
|
unsigned char opcode:4;
|
|
unsigned char aa:1;
|
|
unsigned char tc:1;
|
|
unsigned char rd:1;
|
|
unsigned char ra:1;
|
|
unsigned char zero:3;
|
|
unsigned char rcode:4;
|
|
#else
|
|
unsigned char rcode:4;
|
|
unsigned char zero:3;
|
|
unsigned char ra:1;
|
|
unsigned char rd:1;
|
|
unsigned char tc:1;
|
|
unsigned char aa:1;
|
|
unsigned char opcode:4;
|
|
unsigned char qr:1;
|
|
#endif
|
|
} bit;
|
|
} flags;
|
|
unsigned short nquestions;
|
|
unsigned short nanswerRR;
|
|
unsigned short nauthorityRR;
|
|
unsigned short nadditionalRR;
|
|
};
|
|
|
|
struct dnsquestion {
|
|
unsigned int querylength;
|
|
unsigned char * query;
|
|
unsigned short type;
|
|
unsigned short class;
|
|
};
|
|
|
|
struct dnsrr {
|
|
unsigned char * domainname;
|
|
unsigned char * domainname_string;
|
|
unsigned short type;
|
|
unsigned short class;
|
|
unsigned int ttl;
|
|
unsigned short datalength;
|
|
unsigned char * data;
|
|
unsigned char * data_string; // parsed version of data if possible
|
|
struct dnsrr * next;
|
|
};
|
|
|
|
char *rr_types[256] = {
|
|
"#0x00", "a", "ns", "#0x03", "#0x04", "cname", "soa", "#0x07",
|
|
"#0x08", "#0x09", "#0x0a", "#0x0b", "#ptr", "#hinfo", "#0x0e", "#mx",
|
|
"#txt", "#0x11", "#0x12", "#0x13", "#0x14", "#0x15", "#0x16", "#0x17",
|
|
"#0x18", "#0x19", "#0x1a", "#0x1b", "#0x1c", "#0x1d", "#0x1e", "#0x1f",
|
|
"#0x20", "#0x21", "#0x22", "#0x23", "#0x24", "#0x25", "#0x26", "#0x27",
|
|
"#0x28", "#0x29", "#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", "#0xff"
|
|
};
|
|
|
|
/*****************************************************************************/
|
|
|
|
char *get_resource(int type, struct dnssession *session, char *buffer, int dots);
|
|
char *printablename(char *name, int withdots);
|
|
|
|
unsigned int debugging = 0;
|
|
|
|
/*****************************************************************************/
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
unsigned int
|
|
getlong(char *s)
|
|
{
|
|
unsigned char *t = (unsigned char*)s;
|
|
return 256*256*256*t[0] + 256*256*t[1] + 256*t[2] + t[3];
|
|
}
|
|
|
|
unsigned short
|
|
getshort(char *s)
|
|
{
|
|
unsigned char *t = (unsigned char*)s;
|
|
return 256*t[0] + t[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) {
|
|
unsigned int offset;
|
|
char oldp;
|
|
|
|
oldp = p[0];
|
|
p[0] &= 0x3f;
|
|
offset = getshort(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 = (unsigned char*)strdup(domainname);
|
|
RR->domainname_string = (unsigned char*)strdup(printablename(domainname, 1));
|
|
RR->type = getshort(thisrr);
|
|
RR->class = getshort(thisrr + 2);
|
|
RR->ttl = getlong(thisrr + 4);
|
|
RR->datalength = getshort(thisrr + 8);
|
|
RR->data = (unsigned char *)calloc(1, RR->datalength);
|
|
memcpy(RR->data, thisrr + 10, RR->datalength);
|
|
|
|
p = thisrr + 10;
|
|
RR->data_string = (unsigned char*)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 * pquestion;
|
|
char * panswer;
|
|
char * pauthority;
|
|
char * padditional;
|
|
|
|
char *pbuffer;
|
|
int i;
|
|
|
|
pbuffer = session->recv_pkt;
|
|
|
|
//
|
|
// Extract the header.
|
|
//
|
|
session->recv_pkt_header = 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 = (unsigned char*)strdup(getname(session, &pquestion));
|
|
question->type = getshort(pquestion);
|
|
question->class = getshort(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->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(int 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(int 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";
|
|
default:
|
|
return "unknown";
|
|
}
|
|
}
|
|
|
|
char *
|
|
get_ttl(int 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(int 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",
|
|
(unsigned char)buffer[0],
|
|
(unsigned char)buffer[1],
|
|
(unsigned char)buffer[2],
|
|
(unsigned 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",
|
|
(unsigned char)buffer[ 0], (unsigned char)buffer[ 1],
|
|
(unsigned char)buffer[ 2], (unsigned char)buffer[ 3],
|
|
(unsigned char)buffer[ 4], (unsigned char)buffer[ 5],
|
|
(unsigned char)buffer[ 6], (unsigned char)buffer[ 7],
|
|
(unsigned char)buffer[ 8], (unsigned char)buffer[ 9],
|
|
(unsigned char)buffer[10], (unsigned char)buffer[11],
|
|
(unsigned char)buffer[12], (unsigned char)buffer[13],
|
|
(unsigned char)buffer[14], (unsigned char)buffer[15]
|
|
);
|
|
return retval;
|
|
|
|
case ns_t_cname:
|
|
strcpy(retval, printablename(getname(session, &buffer), dots));
|
|
return retval;
|
|
|
|
case ns_t_txt:
|
|
strcpy(retval, printablename(getname(session, &buffer), dots));
|
|
return retval;
|
|
|
|
case ns_t_mx:
|
|
{
|
|
unsigned short us;
|
|
|
|
us = getshort(buffer);
|
|
buffer += 2;
|
|
sprintf(retval, "%hu %s", us, printablename(getname(session, &buffer), dots));
|
|
return retval;
|
|
}
|
|
|
|
case ns_t_hinfo:
|
|
strcpy(retval, printablename(getname(session, &buffer), dots));
|
|
return retval;
|
|
|
|
case ns_t_ns:
|
|
strcpy(retval, printablename(getname(session, &buffer), dots));
|
|
return retval;
|
|
|
|
case ns_t_ptr:
|
|
strcpy(retval, 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];
|
|
unsigned long ul;
|
|
|
|
strcpy(mname, printablename(getname(session, &buffer), dots));
|
|
strcpy(rname, printablename(getname(session, &buffer), dots));
|
|
|
|
ul = getlong(buffer);
|
|
sprintf(retval, "serial: %ld mname: %s rname: %s", ul, mname, rname);
|
|
return retval;
|
|
}
|
|
|
|
default:
|
|
return "unknown";
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
dump_question(struct dnsquestion *question)
|
|
{
|
|
printf("- Queryname: %s\n", printablename((char*)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)\n");
|
|
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((char*)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, (char*)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->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->additional;
|
|
while (additionalrr != NULL) {
|
|
printf("ADDITIONAL RR\n");
|
|
dump_rr(additionalrr, session);
|
|
additionalrr = additionalrr->next;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
//
|
|
// Packet creation routines
|
|
//
|
|
|
|
unsigned char *
|
|
create_packet(struct dnssession *session)
|
|
{
|
|
unsigned char * pkt;
|
|
struct dnsheader nheader; // networked header
|
|
struct dnsquestion nquestion; // networked question
|
|
int len;
|
|
|
|
//
|
|
// 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;
|
|
pkt = (unsigned 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));
|
|
memcpy(pkt + sizeof(struct dnsheader), nquestion.query, nquestion.querylength);
|
|
memcpy(pkt + sizeof(struct dnsheader) + nquestion.querylength, &(nquestion.type), 4);
|
|
|
|
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;
|
|
|
|
#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 = (char*)create_packet(session);
|
|
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];
|
|
int 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 (WaitSelect(session->socket + 1, &in_set, NULL, NULL, &timeout, NULL) < 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 (getshort(buffer) != session->send_header->identification) {
|
|
fprintf(stderr, "Expected id: %hx, received id: %hx\n",
|
|
session->send_header->identification, getshort(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;
|
|
}
|
|
|
|
void
|
|
create_question(struct dnssession *session, char *name)
|
|
{
|
|
unsigned char *p, *q;
|
|
|
|
session->send_question =
|
|
(struct dnsquestion *)calloc(1, sizeof(struct dnsquestion));
|
|
|
|
session->send_question->querylength = strlen(name) + 2;
|
|
session->send_question->query =
|
|
(unsigned char*)calloc(1, session->send_question->querylength + 2);
|
|
strcpy((char*)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;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
//
|
|
// 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((char*)rr->domainname, 1));
|
|
|
|
if (rr->data_string == NULL)
|
|
arecord->rr_data = NULL;
|
|
else
|
|
arecord->rr_data = strdup((char*)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);
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
CloseSocket(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,
|
|
(char*)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((char*)rrauth->domainname_string, ".") == 0) {
|
|
rrauth = rrauth->next;
|
|
if (!refersbackwards++)
|
|
printf("Refers backwards ");
|
|
continue;
|
|
}
|
|
|
|
if (server_authfor != NULL && strcmp(server_authfor, ".") != 0 &&
|
|
strnrcasecmp((char*)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->additional;
|
|
while (rradd != NULL) {
|
|
if (strcmp(printablename((char*)rradd->domainname, 1),
|
|
(char*)rrauth->data_string) == 0)
|
|
found++;
|
|
rradd = rradd->next;
|
|
}
|
|
|
|
rradd = session->additional;
|
|
do {
|
|
//
|
|
// Find the first IP address
|
|
//
|
|
while (rradd != NULL) {
|
|
if ((char*)strcmp(printablename((char*)rradd->domainname, 1),
|
|
(char*)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((char*)rradd->domainname, 1));
|
|
strcpy(nextserver_ip, (char*)rradd->data_string);
|
|
retval += create_session(host,
|
|
nextserver_ip, (rradd->type == ns_t_aaaa) ? 1 : 0,
|
|
nextserver_name, (char*)rrauth->domainname_string,
|
|
depth + 1, s,
|
|
(rrauth->next == NULL && found <= 1) ? 1 : 0);
|
|
} else {
|
|
int ip, ipfound = 0;
|
|
|
|
strcpy(nextserver_name, (char*)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) {
|
|
unsigned char *s = (unsigned char*)addr_list[i];
|
|
sprintf(nextserver_ip,
|
|
"%hu.%hu.%hu.%hu", s[0], s[1], s[2], s[3]);
|
|
ipv6 = 0;
|
|
} else {
|
|
unsigned char *s = (unsigned char*)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, (char*)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, (char*)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 and usage
|
|
//
|
|
|
|
void
|
|
usage(void)
|
|
{
|
|
fprintf(stderr,
|
|
#ifdef ANSI_CONSOLE
|
|
"\33[33mDNSTRACER version " PACKAGE_VERSION DEBUG_TEXT " (c) Edwin Groothuis - http://www.mavetju.org\33[31m\n"
|
|
#else
|
|
"DNSTRACER version " PACKAGE_VERSION DEBUG_TEXT " (c) Edwin Groothuis - http://www.mavetju.org\n"
|
|
#endif
|
|
"Usage: dnstracer [options] [host]\n"
|
|
"\t-c: disable local caching, default enabled\n"
|
|
"\t-C: enable negative caching, default disabled\n"
|
|
"\t-o: enable overview of received answers, default disabled\n"
|
|
"\t-q <querytype>: query-type to use for the DNS requests, default A\n"
|
|
"\t-r <retries>: amount of retries for DNS requests, default 3\n"
|
|
"\t-s <server>: use this server for the initial request.\n"
|
|
"\t Default is A.ROOT-SERVERS.NET.\n"
|
|
"\t-t <maximum timeout>: Limit time to wait per try\n"
|
|
"\t-v: verbose\n"
|
|
"\t-S <ip address>: use this source address.\n"
|
|
);
|
|
#ifndef NOIPV6
|
|
fprintf(stderr,
|
|
"\t-4: don't query IPv6 servers\n"
|
|
);
|
|
#endif
|
|
exit(1);
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int ch;
|
|
// localhost as default does not make sense in AmigaOS
|
|
//char * server_name = "127.0.0.1";
|
|
char * server_name = "A.ROOT-SERVERS.NET";
|
|
char ipaddress[NS_MAXDNAME];
|
|
char argv0[NS_MAXDNAME];
|
|
int server_root = 0;
|
|
int ipv6 = 0;
|
|
int init;
|
|
|
|
prog = argv[0];
|
|
|
|
init = open_libs();
|
|
if (init != 0)
|
|
return init;
|
|
|
|
while ((ch = getopt(argc, argv, "4cCoqz: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 'o':
|
|
global_overview = 1;
|
|
break;
|
|
|
|
case 'q':
|
|
if ((global_querytype = atoi(optarg)) < 1) {
|
|
#define compare(s, v) \
|
|
if (strcmp((s), optarg) == 0) global_querytype = (v)
|
|
|
|
compare("a", ns_t_a );
|
|
compare("aaaa", ns_t_aaaa );
|
|
compare("a6", ns_t_aaaa );
|
|
compare("ptr", ns_t_ptr );
|
|
compare("cname",ns_t_cname );
|
|
compare("hinfo",ns_t_hinfo );
|
|
compare("mx", ns_t_mx );
|
|
compare("ns", ns_t_ns );
|
|
compare("txt", ns_t_txt );
|
|
compare("soa", ns_t_soa );
|
|
|
|
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;
|
|
#if defined( DEBUG ) || defined( _DEBUG )
|
|
case 'z':
|
|
if( ! ( debugging = ( unsigned int )atoi( optarg ) ) )
|
|
usage();
|
|
break;
|
|
#endif
|
|
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);
|
|
|
|
{
|
|
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) {
|
|
unsigned char *s = (unsigned char*)h->h_addr_list[0];
|
|
sprintf(ipaddress, "%hu.%hu.%hu.%hu", s[0], s[1], s[2], s[3]);
|
|
ipv6 = 0;
|
|
} else {
|
|
unsigned char *s = (unsigned char*)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;
|
|
}
|