1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
|
#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 <time.h> /* clock_gettime(2) */
#include "domain2name.c"
#include "host.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 XSTR(x) #x
#define STR(x) XSTR(x)
#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. No IP/UDP checksums.\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
*/
/* UDP PACKET: HEADER DATA https://www.ietf.org/rfc/rfc768.txt
SRCPORT 16 bits
DSTPORT 16 bits
LENGTH 16 bits: size of packet including header in bytes
CHCKSUM 16 bits: same algo as IP, data: pseudoheader (srcip dstip 0x0011 LENGTH) header data
*/
#define MICROSECOND 0xA1B2C3D4
#define NANOSECOND 0xA1B23C4D
#define PCAPMAJ 2
#define PCAPMIN 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)); /* measured in 32 bit words */
enum precedence precedence : 3 __attribute__((packed)); /* without optionts it's min 5 */
enum srvtype srvtype : 5 __attribute__((packed));
uint16_t length __attribute__((packed)); /* header + data in 8 bit words */
uint16_t identifier __attribute__((packed));
enum ip_flags flags : 3 __attribute__((packed));
unsigned int foffset : 13 __attribute__((packed)); /* fragment offset - 64 bit words */
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)); /* ----------- 20 bytes */
char options[] /* ignored for char[] */;
} __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 udp {
struct sockaddr_in src;
struct sockaddr_in dst;
size_t len; /* of data only */
char * data;
};
int logudp (int o /* file descriptor */, struct udp u) {
struct timespec t;
if (clock_gettime(CLOCK_REALTIME, &t) == -1) {
perror("clock_gettime(CLOCK_REALTIME, &t)");
return -1;
}
struct pcap_packet p = {
.seconds = t.tv_sec,
.subseconds = t.tv_nsec,
.capture_length = u.len,
.original_length = u.len
};
struct ip i = {
.version = 4,
.headlen = 5,
.precedence = Routine,
.srvtype = 0,
.length = htons(8+u.len),
.identifier = 0x6969,
.flags = 0,
.foffset = 0,
.ttl = 69,
.protocol = Udp,
.checksum = 0, /* wireshark does not validate, at least not on debian 11 */
.src = u.src.sin_addr,
.dst = u.dst.sin_addr
};
#define LOGUDP_L (sizeof p + sizeof i + 4*2 + u.len)
char * c, * b = alloca(LOGUDP_L); /* to do in one write (thread safe) */
char * n = "\0"; /* as per udp rfc when no checksum was calculated (-: */
uint16_t l = htons(u.len);
c = (char *) memcpy(b, &p, sizeof p) + sizeof p;
c = (char *) memcpy(c, &i, sizeof i) + sizeof i;
c = (char *) memcpy(c, &u.src.sin_port, 2) + 2;
c = (char *) memcpy(c, &u.dst.sin_port, 2) + 2;
c = (char *) memcpy(c, &l, 2) + 2;
c = (char *) memcpy(c, &n, 2) + 2;
c = (char *) memcpy(c, u.data, u.len) + u.len;
if (write(o, b, LOGUDP_L) == -1) { /* atomic and thread safe, as per posix */
perror("write(" STR(LOGUDP_L) ")");
return -2;
}
return 0;
}
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;
}
struct pcap_global g = {
.subsecond = NANOSECOND,
.major = PCAPMAJ,
.minor = PCAPMIN,
.reserved[0] = 0,
.reserved[1] = 0,
.snaplen = 65535,
.fcs = 0,
.linktype = Ip
};
if (write(o, &g, sizeof g) == -1) {
perror("write(o, &g, sizeof g)");
r = 2;
goto r;
}
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 = 3;
goto r;
}
n = alloca(l*sizeof(*n));
for (int i = optind; i < argc; i++) {
int w = i-optind;
n[w] = str2net(argv[i]);
}
goto o;
case '?':
fprintf(stderr, "unknown option :: " HELP, argv[0]);
r = 6;
goto r;
case ':':
fprintf(stderr, "missing option argument :: " HELP, argv[0]);
r = 7;
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 = 8;
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 = 10;
goto r;
}
if ((c = fork()) == -1) {
perror("fork()");
r = 11;
goto r;
}
r:
if (s != -1)
if (close(s))
perror("close(s)");
if (o != -1)
if (close(o))
perror("close(o)");
return r;
}
|