#include <sys/socket.h> /* udp(7) */
#include <netinet/in.h>
#include <netinet/udp.h>
#include <poll.h> /* poll(2) */
#include <sys/types.h> /* socket(2) */
#include <sys/socket.h>
#include <unistd.h> /* close(2) */
#include <stdio.h> /* perror(3) */
#include <sys/stat.h> /* open(2) */
#include <fcntl.h>
#include <errno.h> /* errno(3) */
#include <arpa/inet.h> /* byteorder(3) */
#include <netdb.h> /* getaddrinfo(3) */
#include <stdlib.h> /* atoi(3) */
#include <string.h> /* strchr(3) */
#include "domain2name.c"
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MAXDOMAIN 255
#define QUERYDOMAIN "http://sijanec.eu/link?r=.sijanec.eu."
#define EXPECTEDA 93.103.235.126
#define HELP "find recursive DNS resolvers on IPv4 networks\n" \
"%s [-a ipv4] [-b ipv4] [-d domain] [-h] [-o filename] [-p port] network1 [network2 ...]\n" \
" -a Specify the IPv4 of the -d domain to be used instead of getaddrinfo(3).\n" \
" -b Bind on a specific interface, defined by IPv4. Default is any interface.\n" \
" -d Specify the domain name to be used in queries that has a single A record.\n" \
" -h Show this help and exit.\n" \
" -o Output PCAP to filename. Any existing file is truncated.\n" \
" -p Set the source port number to use instead of a dynamically asigned one\n" \
"Network addresses are optionally followed by slash and netmask, otherwise networks are\n" \
"understood as single host addresses. Both network names and netmasks can be domains to\n" \
"be looked up or IP dot-notation addresses. Mask can also be a bit prefix (/32 default).\n" \
"When scanning the Internet please make sure that you specify your own domain instead\n" \
"of the default one (dnsfind.sijanec.eu).\n"
/* DNS PACKET: HEADER QUESTION ANSWER AUTHORITY ADDITIONAL datatracker.ietf.org/doc/html/rfc1035
DEFINITIONS: (those appear somewhere in the packet, packet does not start with definitions!)
LABLEN 8 bits: first two bits zero, then 6 bits length of label
POINTER 8 bits: first two bits one, then 6 bits as offset from first byte of packet
STRING a single byte for defining length (0-256 - all eight bits) and then string of chars
DOMAIN i.) one or more LABLEN followed by ASCII label (no dots) end with either LABLEN 0
or a POINTER that points to some LABLEN somewhere else in the packet -OR-:
ii.) a POINTER that points to some LABLEN somewhere else in the packet
HEADER: 12 bytes
XID 16 bits: random string to be matched in response to prevent cache poisoning
/ QR 1 bit : what type is this packet? 0 query 1 response
| OPCODE 4 bits: type of query 0 std 1 invrs 2 srvst 3-15 reserved
1 byte AA 1 bit : is response authoritative? 0 no 1 yes
| TC 1 bit : was response truncated? 0 no 1 yes
\ RD 1 bit : does query desire recursion? 0 no 1 yes
/ RA 1 bit : does response server recurse? 0 no 1 yes
1 byte Z 3 bits: reserved for future 0 only option
\ RCODE 4 bits: error condition 0 ok 1 fmter 2 srvfa 3 nxdom 4 N/I 5 forbidden
QDCOUNT 16 bits: number of questions
ANCOUNT 16 bits: number of answers
NSCOUNT 16 bits: authority section (where to ask for actual response - NS RECORDS)
ARCOUNT 16 bits: additional section (glue records)
QUESTION:
QNAME DOMAIN
QTYPE 16 bits: 1 A 2 NS 5 CNAME 6 SOA 10 NULL 12 PTR 13 HINFO 15 MX 16 TXT ...
QCLASS 16 bits: 1 INTERNET 2 CSNET (obsolete) 3 CHAOS 4 HESIOD 255 ANY ...
ANSWER:
NAME DOMAIN
TYPE same as QTYPE
CLASS same description as QCLASS, except class 255 ANY is not allowed here
RDLEN 16 bits: length of RDATA field - this is a number 0-65536, no two zero bits
RDATA A: 4 bytes IP address NS: DOMAIN CNAME: DOMAIN
SOA: NAME-ns1 NAME-email 32b-serial 32b-refresh 32b-retry 32b-expire 32b-nxdomainttl
NULL: any data up to RDLEN PTR: DOMAIN HINFO: STRING-CPU, STRING-OS
MX: 16 bit preference, NAME-like domain TXT: one or more STRING
*/
/* PCAP file format: GLOBALHEADER PACKETHEADER PACKETDATA PACKETHEADER2 PACKETDATA2 ...
GLOBAL HEADER: 24 bytes
MAGIC 32 bits: 0xA1B2C3D4 timestamp is s and micros 0xA1B23C4D timestamp is s and nanos
MAJOR 16 bits: version 2 https://tools.ietf.org/id/draft-gharris-opsawg-pcap-00.html
MINOR 16 bits: version 4 _/ /-------------------------------------------------\
RESERV1 32 bits: unused and set to 0 < http://en.wikipedia.org/wiki/Frame_check_sequence|
RESERV2 32 bits: unused and set to 0 \--------------------------------------------\ ^ |
SNAPLEN 32 bits: larger or equal to size of largest capture of a single packet |/ \|
FCS 4 bits: if last bit is 1, first 3 tell nr of bytes of FCS appended to every packet/
LINKTYP 28 bits: pkt type //tcpdump.org/linktypes.html 101 IPv4/v6 1 ether | |
PACKET HEADER: 16 bytes
SECONDS 32 bits: UNIX timestamp
NANOSEC 32 bits: nanoseconds elapsed since the second, can also be microseconds - see MAGIC
CAPTURE 32 bits: number of bytes captured from the packet following the header
ORIGLEN 32 bits: number of bytes of the original packet size (can be more than CAPTURE)
*/
/* IPv4 PACKET: HEADER DATA https://datatracker.ietf.org/doc/html/rfc791
HEADER:
/-- VERSION 4 bits: 4 IPv4 6 IPv6
| T HEADLEN 4 bits: >= 5. Header size 32 bit words (header is padded), so it points to data.
|w SRVTYPE 8 bits: 3bPrecedence 1bLowDelay 1bHighThroughput 1bHighReliability 2bReserved
|e Prec.: 0routine 1prio 2immediate 3flash 4flashoverride 5critic 110inetctrl 111netctrl
|n LENGTH 16 bits: length including header and data. every host must accept at least 576.
|t IDENTIF 16 bits: not so unique ID per src-dest persisted across fragmentations for reassembly
|y FLAGS 3 bits: bit 0 (1. bit): evil bit, bit 1: don't fragment, bit 2: more fragments
| B FOFFSET 13 bits: where in complete datagram this fragment belongs in 64 bit words-first has 0
|y TTL 8 bits: every router decreases by one, when zero, packet is destroyed
|t PROTO 8 bits: datatracker.ietf.org/doc/html/rfc790#page-6 1 ICMP 6 TCP 17 UDP
|e CHCKSUM 16 bits: 16 bit one's complement of the one's complement sum of 16b words in header
|s SRCADDR 32 bits:
\-- DSTADDR 32 bits:
OPTIONS variable: depending on type, it may be single byte-type or byte-type, byte-len, data.
Option type byte: 1b-copy to fragmented headers 2b-option class 5b-option number
Option classes: 0 control 1 reserved 2 debugging 3 reserved
Option byte zero denotes an end of options, NOOP is option number 1
PADDING variable: zero bytes ensuring header is aligned into 32 bit words
*/
#define MICROSECOND 0xA1B2C3D4
#define NANOSECOND 0xA1B23C4D
#define PCAPVERMAJ 2
#define PCAPVERMIN 4
enum linktype {
Ethernet = 1,
Ip = 101
};
struct pcap_global {
uint32_t subsecond __attribute__((packed));
uint16_t major __attribute__((packed));
uint16_t minor __attribute__((packed));
uint32_t reserved[2] __attribute__((packed));
uint32_t snaplen __attribute__((packed));
unsigned int fcs : 4 __attribute__((packed));
enum linktype linktype : 28 __attribute__((packed));
} __attribute__((packed));
#define microseconds subseconds
#define nanoseconds subseconds
struct pcap_packet {
uint32_t seconds __attribute__((packed));
uint32_t subseconds __attribute__((packed));
uint32_t capture_length __attribute__((packed));
uint32_t original_length __attribute__((packed));
} __attribute__((packed));
enum precedence {
Routine,
Priority,
Immediate,
Flash,
Flash_override,
Critical,
Inetctrl,
Netctrl
};
enum srvtype {
Low_delay = 1 << 0,
High_throughput = 1 << 1,
High_reliability = 1 << 2,
Reserved_0 = 1 << 3,
Reserved_1 = 1 << 4
};
enum ip_flags {
Evil = 1 << 0,
Df = 1 << 1,
Mf = 1 << 2
};
enum protocol {
ICMP = 1,
TCP = 6,
UDP = 17
};
struct ip {
unsigned int version : 4 __attribute__((packed));
unsigned int headlen : 4 __attribute__((packed));
enum precedence precedence : 3 __attribute__((packed));
enum srvtype srvtype : 5 __attribute__((packed));
uint16_t length __attribute__((packed));
uint16_t identifier __attribute__((packed));
enum ip_flags flags : 3 __attribute__((packed));
unsigned int foffset : 13 __attribute__((packed));
uint8_t ttl /* ignored for uint8_t */;
enum protocol protocol : 8 __attribute__((packed));
uint16_t checksum __attribute__((packed));
struct in_addr src __attribute__((packed));
struct in_addr dst __attribute__((packed));
char options[] /* ignored for char[] */; /* options, padding and data */
} __attribute__((packed));
enum qr {
Question,
Response
};
enum opcode {
Query,
Iquery,
Stauts
};
enum rcode {
Success,
Format_error,
Servfail,
Nxdomain,
Ni,
Forbidden
};
enum type {
A = 1,
Ns,
Md,
Cname = 5, /* we skip the quite obsolete Mf record, luckily Mf (more fragments) is also 4 */
Soa,
Mb,
Mg,
Mr,
Null,
Wks,
Ptr,
Hinfo,
Minfo,
Mx,
Txt
};
enum class {
In = 1,
Cs,
Ch,
He,
Any = 255
};
enum dns_flags {
Aa = 1 << 0,
Tc = 1 << 1,
Rd = 1 << 2,
Ra = 1 << 3,
Z0 = 1 << 4,
Z1 = 1 << 5,
Z2 = 1 << 6
};
struct header {
uint16_t xid __attribute__((packed));
enum qr qr : 1 __attribute__((packed));
enum opcode opcode : 4 __attribute__((packed));
enum dns_flags flags : 7 __attribute__((packed));
enum rcode rcode : 4 __attribute__((packed));
uint16_t qdcount __attribute__((packed));
uint16_t ancount __attribute__((packed));
uint16_t nscount __attribute__((packed));
uint16_t arcount __attribute__((packed));
char data[] /* ignored for char[] */;
} __attribute__((packed));
struct question {
char * qname __attribute__((packed));
enum type qtype : 16 __attribute__((packed));
enum class qclass : 16 __attribute__((packed));
} __attribute__((packed));
struct answer {
char * name __attribute__((packed));
enum class class : 16 __attribute__((packed));
uint16_t rdlen __attribute__((packed));
char * rdata __attribute__((packed));
};
struct in_net {
struct in_addr addr;
struct in_addr mask;
};
int resolve (const char * d, uint32_t * r) {
struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_DGRAM,
.ai_flags = 0,
.ai_protocol = 0,
.ai_canonname = NULL,
.ai_addr = NULL,
.ai_next = NULL
};
struct addrinfo * result;
int ret = getaddrinfo(d, NULL, &hints, &result);
*r = ((struct sockaddr_in *) result->ai_addr)->sin_addr.s_addr; /* ah yes, C */
freeaddrinfo(result);
return ret;
}
int main (int argc, char ** argv) {
int r = 0;
struct in_addr a = {
.s_addr = 0
};
struct sockaddr_in b = {
.sin_family = AF_INET,
.sin_port = 0,
.sin_addr = {
.s_addr = INADDR_ANY
}
};
char * d = "dnsfind.sijanec.eu";
int s = -1; /* socket */
int o = -1; /* output file */
char buf[512]; /* max dns packet */
int c; /* child process */
struct in_net * n; /* networks */
int l; /* count of networks */
while (1) {
switch (getopt(argc, argv, ":a:b:d:h")) {
case 'a':
inet_aton(optarg, &a);
break;
case 'b':
inet_aton(optarg, &b.sin_addr);
break;
case 'd':
d = optarg;
break;
case 'h':
printf(HELP, argv[0]);
r = 0;
goto r;
case 'o':
if ((o = open(optarg, O_CREAT | O_TRUNC | O_WRONLY)) == -1) {
perror("open(optarg, O_CREAT | O_TRUNC | O_WRONLY)");
r = 1;
goto r;
}
write(o, "", 1);
break;
case 'p':
b.sin_port = htons(atoi(optarg));
break;
case -1:
if (!(l = argc-optind)) {
fprintf(stderr, "specify targets to scan :: " HELP, argv[0]);
r = 2;
goto r;
}
n = alloca(l*sizeof(*n));
for (int i = optind; i < argc; i++) {
int w = i-optind;
char * m = strchr(argv[i], '/');
int e;
if (m)
*m++ = '\0';
else
m = "32";
fprintf(stderr, "network %d: resolving %s ... ", w, argv[i]);
if ((e = resolve(argv[i], &n[w].addr.s_addr))) {
fprintf(stderr, "failed: %s\n", gai_strerror(e));
r = 3;
goto r;
}
fprintf(stderr, " %s mask %s ...", inet_ntoa(n[w].addr), m);
char * p;
int x = strtoll(m, &p, 10);
n[w].mask.s_addr = 0;
for (int j = 0; j < x && j < 32; j++)
n[w].mask.s_addr = n[w].mask.s_addr >> 1 | 1 << 31;
n[w].mask.s_addr = htonl(n[w].mask.s_addr);
if (*p)
if ((e = resolve(m, &n[w].mask.s_addr))) {
fprintf(stderr, "no: %s\n", gai_strerror(e));
r = 4;
goto r;
}
fprintf(stderr, " %s\n", inet_ntoa(n[w].mask));
}
goto o;
case '?':
fprintf(stderr, "unknown option :: " HELP, argv[0]);
r = 5;
goto r;
case ':':
fprintf(stderr, "missing option argument :: " HELP, argv[0]);
r = 6;
goto r;
}
}
o:
if (!a.s_addr) {
int e;
fprintf(stderr, "resolving %s ... ", d);
if ((e = resolve(d, &a.s_addr))) {
fprintf(stderr, "failed: %s\n", gai_strerror(e));
r = 7;
goto r;
}
fprintf(stderr, " %s\n", inet_ntoa(a));
}
if ((s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
perror("socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)");
r = 8;
goto r;
}
if (bind(s, (struct sockaddr *) &b, sizeof(struct sockaddr))) {
perror("bind(s, (struct sokaddr *) &b, sizeof(struct sockaddr))");
r = 9;
goto r;
}
if ((c = fork()) == -1) {
perror("fork()");
r = 10;
goto r;
}
r:
if (s != -1)
if (close(s))
perror("close(s)");
if (o != -1)
if (close(o))
perror("close(o)");
return r;
}