summaryrefslogtreecommitdiffstats
path: root/main.c
blob: a7b2ba46387c8547c21eff873319d483bede64ee (plain) (blame)
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
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
#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> /* htons(3) */
#include <netdb.h> /* getaddrinfo(3) */
#include <stdlib.h> /* atoi(3) */
#include <string.h> /* strchr(3) */
#include <time.h> /* clock_gettime(2) */
#include <signal.h> /* signal(2) */
/* #include <sys/prctl.h> */ /* prctl(2) */
/* #include <sys/wait.h> */ /* waitpid(2) */
#include <poll.h> /* poll(2) */
#include "domain2name.c"
#include "host.c"
#define HELP "find recursive DNS resolvers on IPv4 networks\n" \
"%s [-a ip] [-b ip] [-e file [-f]] [h] [-k] [-m] [-n num] [-p port] [-t μs] [-w μs] domain netwo1 [netwo2 ...]\n" \
"	-a Specify the A RR IPv4 address of the domain to be used instead of getaddrinfo(3).\n" \
"	-b Bind on a specific interface, defined by IPv4. Default is to use any interface.\n" \
"	-e Output PCAP to filename. Any existing file is truncated. No IP/UDP checksums. See -f.\n" \
"	-f Exclude sent packets from -e PCAP output They're all the same with different dst IPs.\n" \
"	-h Show this help and exit.\n" \
"	-k Increment IP addresses in reverse bit endianness (000 100 010 110 001 101 011 111).\n" \
"	-m Spiral-search around a single host given instead of networks. Use with -n.\n" \
"	-n Stops scanning after provided number of working servers is found and reported.\n" \
"	-p Set the source port number to use instead of a dynamically asigned one.\n" \
"	-t Number of microseconds to wait between sent packets. (default & min. 1000 - 64 KB/s)\n" \
"	-w Finish after μs after last received packet when done with sending. (default 1000000)\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 be\n" \
"looked up or IP dot-notation addresses. Mask can also be a bit prefix (default /32).\n" \
"It would take a day to scan the entire address space (0.0.0.0/0) with the default timings.\n" \
"If network has host bits set, scanning starts at that address. 10.0.0.100/24 scans 156 hosts.\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	16 bits: first two bits one, then 14 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 quest	1 response
    |	OPCODE   4 bits: type of query			0 query	1 iquer	2 sstat	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
	TTL	32 bits: unsigned intager of seconds. for this period the record is valid.
	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	| //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
	SUBSECS 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 (8 bytes) DATA				https://www.ietf.org/rfc/rfc768.txt
	SRCPORT	16 bits
	DSTPORT	16 bits
	LENGTH	16 bits: size of packet including header in bytes+8
	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
#define ETHERNET = 1,
#define 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));
	uint32_t linktype		__attribute__((packed)); /* FCS in included here for order */
} __attribute__((packed));
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));
#define LOW_DELAY (1 << 4)
#define HIGH_THROUGHPUT (1 << 3)
#define HIGH_RELIABILITY (1 << 2)
#define ROUTINE (0 << 5)
#define PRIORITY (1 << 5)
#define IMMEDIATE (1 << 6)
#define FLASH (PRIORITY | IMMEDIATE)
#define FLASH_OVERRIDE (1 << 7)
#define CRITICAL (FLASH_OVERRIDE | PRIORITY)
#define INETCTRL (FLASH_OVERRIDE | IMMEDIATE)
#define NETCTRL (FLASH_OVERRIDE | FLASH)
#define HEADLENOR (1 << 6) /* always bitwiseOR the headlen with this to apply the version number */
#define EVIL (1 << 15)
#define DF (1 << 14)
#define MF (1 << 13)
#define ICMP 1
#define TCP 6
#define UDP 17
struct ip {
	uint8_t headlen; /* length of header in 32 bit words (min 5, max 15 = 60B). see HEADLENOR. */
	uint8_t srvtype; /* OR here: LOW_DELAY, HIGH_THROUGHPUT, HIGH_RELIABILITY, ROUTINE, ... */
	uint16_t length			__attribute__((packed)); /* header + data in 8 bit words */
	uint16_t identifier		__attribute__((packed));
	uint16_t foffset /* 64b wrds */ __attribute__((packed)); /* or any flags: EVIL, DF, MF */
	uint8_t ttl;			/* ignored for uint8_t */
	uint8_t protocol;		/* ignored for uint8_t */
	uint16_t checksum;		/* ignoref for uint8_t */
	struct in_addr src		__attribute__((packed));
	struct in_addr dst		__attribute__((packed)); /* ----------- 20 bytes */
	char options[];			/* ignored for char[] */
} __attribute__((packed));
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
};
#define RESPONSE (1 << 15)								/* :FLAGS */
#define QUESTION (0 << 15)
#define QUERY (0 << 11)
#define IQUERY (1 << 11)
#define STATUS (1 << 12)
#define AA (1 << 10)
#define TC (1 << 9)
#define RD (1 << 8)
#define RA (1 << 7)
#define SUCCESS (0 << 0)
#define FORMAT_ERROR (1 << 0)
#define SERVFAIL (1 << 1)
#define NXDOMAIN (FORMAT_ERROR | SERVFAIL)
#define NI (1 << 2)
#define FORBIDDEN (NXDOMAIN | FORMAT_ERROR)
struct header {
	uint16_t xid			__attribute__((packed));
	uint16_t flags			__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 rr { /* name is omitted, first byte of struct is first byte of type */
	uint16_t type			__attribute__((packed));
	uint16_t class			__attribute__((packed));
	uint32_t ttl			__attribute__((packed));
	uint16_t len			__attribute__((packed));
	char data[];			/* ignored for char[] */
} __attribute__((packed));
struct question {
	uint16_t type			__attribute__((packed));
	uint16_t class			__attribute__((packed));
} __attribute__((packed));
int logudp (int o /* fd */, struct sockaddr_in s, struct sockaddr_in d, char * u, size_t l /* d */) {
	struct timespec t;
	if (clock_gettime(CLOCK_REALTIME, &t) == -1) {
		perror("clock_gettime(CLOCK_REALTIME, &t)");
		return -2;
	}
	struct pcap_packet p = {
		.seconds = t.tv_sec,
		.subseconds = t.tv_nsec,
		.capture_length = sizeof(struct ip) + l + 4*2,
		.original_length = sizeof(struct ip) + l + 4*2
	};
	struct ip i = {
		.headlen = 5 | HEADLENOR,
		.srvtype = ROUTINE,
		.length = htons(8+l+sizeof i),
		.identifier = 0x6969,
		.foffset = 0,
		.ttl = 69,
		.protocol = UDP,
		.checksum = 0, /* wireshark does not validate, at least not on debian 11 */
		.src = s.sin_addr,
		.dst = d.sin_addr
	};
#define LOGUDP_L (sizeof p + sizeof i + 4*2 + l)
	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 v = htons(l+8);
	c = (char *) memcpy(b, &p, sizeof p) + sizeof p;
	c = (char *) memcpy(c, &i, sizeof i) + sizeof i;
	c = (char *) memcpy(c, &s.sin_port, 2) + 2;
	c = (char *) memcpy(c, &d.sin_port, 2) + 2;
	c = (char *) memcpy(c, &v, 2) + 2;
	c = (char *) memcpy(c, &n, 2) + 2;
	c = (char *) memcpy(c, u, l) + l;
	c++; c--; /* to make scan-build happy that c is never read */
	if (write(o, b, LOGUDP_L) == -1) { /* atomic and thread safe, as per posix */
		perror("write(o, b, LOGUDP_L)");
		return -3;
	}
	return LOGUDP_L;
}
struct in_addr parse_a (const char * u, int s /* buffer size of u */, const char * d, int l, int n) {
	struct in_addr r = {
		.s_addr = 0	/* returns 0.0.0.0 in case of error or if no more results for n. */
	};
	int y = normalizedomain_len(d, l);
	if (y < 0)
		return r;
	char * ž = alloca(y);
	if (normalizedomain(ž, d, l) < 0)
		return r;
	const struct header * h = (const struct header *) u;
	int q = ntohs(h->qdcount);
	int a = ntohs(h->ancount+h->nscount+h->arcount);
	char * o = NULL;
	char * fuckc = NULL;
	const char * c = u+sizeof(struct header);
	while (q--) {
		int č = name2domain_len(u, s, c);
		if (č < 0)
			goto r;
		if (!(fuckc = realloc(o, č)))
			goto r;
		o = fuckc;
		if (!(c = name2domain(o, u, s, c)))
			goto r;
		struct question * ć = (struct question *) (c+1); /* scan-build ACK: ć is not read */
		ć++; ć--; /* to make scan-build happy that ć is never read */
		if ((c += sizeof(*ć)+1) >= u+512)
			goto r;
	} /* we skip over any questions */
	while (a--) {
		int č = name2domain_len(u, s, c);
		if (č < 0)
			goto r;
		if (!(fuckc = realloc(o, č)))
			goto r;
		o = fuckc;
		if (!(c = name2domain(o, u, s, c)))
			goto r;
		struct rr * ć = (struct rr *) (c+1);
		if (c+sizeof(*ć) >= u+512)
			goto r;
		if ((c += ntohs(ć->len)+sizeof(*ć)+1) >= u+512)
			goto r;
		if (y != č)
			continue;
		if (memcmp(o, ž, y))
			continue;
		if (ntohs(ć->type) != A)
			continue;
		if (ntohs(ć->class) != In)
			continue;
		if (ntohs(ć->len) != 4)			/* this is actually a malformed packet, */
			continue;			/* A resource record must be four bytes */
		if (n--)
			continue;
		r.s_addr = *(in_addr_t *) (ć->data);
		goto r;
	} /* we scroll over answers now and treat all three types the same, waiting for our A */
r:
	free(o);
	return r;
} /* returns nth IP of A record for domain d of length l in response packet u or 0.0.0.0 for err */
int finish = 0;
void handler () {
	if (++finish >= 3)
		exit(1);
}
int main (int argc, char ** argv) {
	int r = 1;
	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 = NULL;
	int s = -1; /* socket */
	int o = -1; /* output file */
	struct in_net * n; /* networks */
	int l; /* count of networks */
	int i = 0; /* network index */
	long long int j = 0; /* host in network index */
	int k = 0; /* little bitendian IP address inc: 10.0.0.0, 10.128.0.0, 10.64.0.0, 10.192.0.0 */
	int targetnum = 0;
	int workingnum = 0;
	unsigned int spiralsearch = 0;
	unsigned int spiralsearch_up = 0;
	unsigned int spiralsearch_down = 0;
	int t = 1000;
	int w = 1000000;
	int e = 0; /* whether to exclude sent packets in PCAP - they're all the same */
	struct in_net h; /* host to scan is .addr, h as struct in_net is returned from host() */
	int notfirst = 0;
	signal(SIGINT, handler);
	signal(SIGTERM, handler);
	while (1) {
		switch (getopt(argc, argv, ":a:b:e:fhkmn:p:t:w:")) {
			case 'a':
				inet_aton(optarg, &a);
				break;
			case 'b':
				inet_aton(optarg, &b.sin_addr);
				break;
			case 'e':
				if ((o = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 00664)) == -1) {
					perror("open(optarg, O_CREAT | O_TRUNC | O_WRONLY)");
					r = 2;
					goto r;
				}
				struct pcap_global g = {
					.subsecond = NANOSECOND,
					.major = PCAPMAJ,
					.minor = PCAPMIN,
					.reserved[0] = 0,
					.reserved[1] = 0,
					.snaplen = 65535,
					.linktype = IP
				};
				if (write(o, &g, sizeof g) == -1) {
					perror("write(o, &g, sizeof g)");
					r = 3;
					goto r;
				}
				break;
			case 'f':
				e++;
				break;
			case 'h':
				printf(HELP, argv[0]);
				r = 0;
				goto r;
			case 'k':
				k++;
				break;
			case 'm':
				spiralsearch++;
				break;
			case 'n':
				targetnum = atoi(optarg);
				break;
			case 'p':
				b.sin_port = htons(atoi(optarg));
				break;
			case 't':
				t = atoi(optarg);
				break;
			case 'w':
				w = atoi(optarg);
				break;
			case -1:
				if (!(argc-optind)) {
					fprintf(stderr, "specify domain name :: " HELP, argv[0]);
					r = 4;
					goto r;
				}
				d = argv[optind];
				int e = optind+1;
				if (!(l = argc-e)) {
					fprintf(stderr, "specify targets to scan :: " HELP, argv[0]);
					r = 5;
					goto r;
				}
				if (spiralsearch && l != 1) {
					fprintf(stderr, "-m option is set, max one host :: " HELP, argv[0]);
					r = 6;
					goto r;
				}
				n = alloca(l*sizeof *n);
				for (int i = e; i < argc; i++) {
					int w = i-e;
					n[w] = str2net(argv[i]);
					if (spiralsearch) {
						n[w].mask.s_addr = INADDR_BROADCAST;
						h = n[w];
					}
				}
				goto o;
			case '?':
				fprintf(stderr, "unknown option :: " HELP, argv[0]);
				r = 7;
				goto r;
			case ':':
				fprintf(stderr, "missing option argument :: " HELP, argv[0]);
				r = 8;
				goto r;
			default:
				r = 9;
				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 = 10;
			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 = 11;
		goto r;
	}
	int ž = 1;
	if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &ž, sizeof(ž)) == -1) {
		perror("setsockopt(s, SOL_SOCKET, SO_BROADCAST, &ž, sizeof(ž))");
		r = 12;
		goto r;
	} /* setting this so that sending packets to a broadcast address does not fail with noperm */
	if (bind(s, (struct sockaddr *) &b, sizeof(struct sockaddr))) {
		perror("bind(s, (struct sokaddr *) &b, sizeof(struct sockaddr))");
		r = 13;
		goto r;
	}
	struct timespec lp = { /* last packet */
		.tv_sec = 0
	};
	fprintf(stderr, "starting at host number %lld\n", (j = localnumber(n[i])));
	long int scanuntilhost = -1; /* no limit */
	while (!finish) {
		if (notfirst) {
			if (k) {
				if (!(j = ri(j, 32-popcnt32(n[i].mask.s_addr))))
					goto k;
			} else
				j++;
		} else
			notfirst++;
		if (getenv("DF_DEBUG"))
			fprintf(stderr, "j = %lld, scanuntilhost = %ld\n", j, scanuntilhost);
		if (spiralsearch || (h = host(n[i], j)).mask.s_addr != INADDR_BROADCAST) {
k:
			if (spiralsearch) {
				if (spiralsearch < 10) /* this indicates we haven't yet */
					spiralsearch = 10; /* scanned given ip itself */
				else {
					if (spiralsearch_down == UINT32_MAX
							&& spiralsearch_up == UINT32_MAX)
						goto finished_sending;
					if ((spiralsearch_up <= spiralsearch_down
							&& spiralsearch_up != UINT32_MAX)
							|| spiralsearch_down == UINT32_MAX) {
						h.addr.s_addr = htonl(ntohl(n[i].addr.s_addr)
								+ ++spiralsearch_up);
						if (h.addr.s_addr == INADDR_BROADCAST)
							spiralsearch_up = UINT32_MAX;
					} else {
						h.addr.s_addr = htonl(ntohl(n[i].addr.s_addr)
								- ++spiralsearch_down);
						if (!h.addr.s_addr)
							spiralsearch_down = UINT32_MAX;
					}
				}
			} else if (++i >= l) {
finished_sending:
				fprintf(stderr, "finished sending, waiting for last replies\n");
				if (clock_gettime(CLOCK_MONOTONIC, &lp) == -1) {
					perror("clock_gettime(CLOCK_MONOTONIC, &z)");
					r = 14;
					goto r;
				}
				goto i;
			} else {
				j = localnumber(n[i]);
				h = host(n[i], j);
			}
		}
		struct sockaddr_in m = {	/* I don't know much about scopes in C and I'm */
			.sin_family = AF_INET,	/* intentionally excercising them for the cost of */
			.sin_port = htons(53),	/* code unreadability. in this scope I defined h */
			.sin_addr = h.addr	/* as struct header, in scope above it was in_net, */
		};				/* and I used h as in_net in this scope as well, */
		struct header h = {		/* but h as header is declared after that use (; */
			.xid = 0x6969, /* oh no, cache poisoning, whatever'll I do */
			.flags = htons(QUESTION | QUERY | RD),
			.qdcount = htons(1),
			.ancount = 0,
			.nscount = 0,
			.arcount = 0
		};
		int v = domain2name_len(d, strlen(d));
#define L (sizeof h + v + 2*2)
		if (v < 0) {
			r = 15;
			goto r;
		}
		if (L > 65535) { /* pebkac, there'll be no error message here */
			r = 16;
			goto r;
		}
		char u[65535]; /* max udp packet, alloca in a loop would be bad (not scope based) */
		char * c;
		uint16_t y = htons(A);
		uint16_t k = htons(In);
		c = (char *) memcpy(u, &h, sizeof h) + sizeof h;
		c += domain2name(c, d, strlen(d));
		c = (char *) memcpy(c, &y, 2) + 2;
		c = (char *) memcpy(c, &k, 2) + 2;
		c++; c--; /* to make scan-build happy that c is never read */
		int ž;
		if (!e && o != -1 && (ž = logudp(o, b, m, u, L)) < -1) {
			fprintf(stderr, "logudp(o, b, m, u, L) == %d\n", ž);
			r = 17;
			goto r;
		}
		if (sendto(s, u, L, 0, (struct sockaddr *) &m, sizeof(struct sockaddr_in)) == -1) {
			perror("sendto(s, u, L, 0, (struct sockaddr *) &m, sizeof(struct sockad...");
			r = 18;
			goto r;
		} /* https://stevecao.wordpress.com/2018/09/28/diagnosing-invalid-argument-error */
		struct timespec z;
i:
		if (clock_gettime(CLOCK_MONOTONIC, &z) == -1) {
			perror("clock_gettime(CLOCK_MONOTONIC, &z)");
			r = 19;
			goto r;
		}
		if ((z.tv_sec*1000000 + z.tv_nsec/1000) - (lp.tv_sec*1000000 + lp.tv_nsec/1000) > w
				&& lp.tv_sec) {
			fprintf(stderr, "no more packets were received for -w μs. done.\n");
			r = 0;
			goto r;
		}
		struct pollfd q = {
			.fd = s,
			.events = POLLIN
		};
		int p;
		if ((p = poll(&q, 1, t/1000 == 0 ? 1 : t/1000)) == -1) {
			perror("poll(&q, 1, t/1000 == 0 ? 1 : t/1000)");
			r = 20;
			goto r;
		}
		if (!p) {
			if (lp.tv_sec)
				goto i;
			else
				continue;
		}
		if (q.revents & POLLERR || q.revents & POLLHUP || q.revents & POLLNVAL) {
			r = 21;
			goto r;
		}
		struct sockaddr_in f;
		socklen_t č = sizeof f;
		while (1) {
			int š;
			if ((š = recvfrom(s, u, 65535, MSG_DONTWAIT, (struct sockaddr *) &f, &č))
				       == -1) {
				if (errno != EWOULDBLOCK) {
					perror("recvfrom(s, u, 65535, MSG_DONTWAIT, (struct soc...");
					r = 22;
					goto r;
				}
				break;
			}
			if (lp.tv_sec)
				lp = z; /* this loop ends nearly in an instant */
			if (o != -1 && (ž = logudp(o, f, b, u, š)) < -1) {
				fprintf(stderr, "logudp(o, f, b, u, š) == %d\n", ž);
				r = 23;
				goto r;
			}
			fprintf(stderr, "RESPONSE\t%s", inet_ntoa(f.sin_addr));
			ž = 0;
			struct in_addr i = parse_a(u, 65535, d, strlen(d), ž++);
			while (parse_a(u, 65535, d, strlen(d), ž++).s_addr);
			if (i.s_addr == a.s_addr) { /* if multithread, change printf to write. */
				printf("\tWORKING");
				if (ž-1 == 1 && i.s_addr)
					if (++workingnum >= targetnum && targetnum) {
						printf("\n");
						fprintf(stderr, "discovered %d working server%s.\n",
								workingnum, workingnum==1 ? "": "s");
						r = 0; /* r is 1 by default */
						goto r;
					}
			}
			if (i.s_addr && i.s_addr != a.s_addr)
				printf("\tLYINGWITH\t%s", inet_ntoa(i));
			if (--ž > 1)
				printf("\tMORETHANONE\t%d", ž);
			if (!i.s_addr)
				printf("\tNOA");
			printf("\n");

		}
		if (z.tv_sec)
			goto i;
	}
r:
	if (!r && notfirst) { /* TODO: tell EXACT packets that were sent before termination. */
		char * x = alloca(l*31+strlen("SCANNED  \n0")+strlen("WORKINGNUM aaaaaaaaaaaaaaaa"));
		if (spiralsearch) {
			strcpy(x, "SPIRALSEARCH ");
			strcat(x, inet_ntoa(n[0].addr));
			strcat(x, " ");
		} else {
			strcpy(x, "SCANNED ");
			for (int m = 0; m < (finish ? i : l); m++) {
				strcat(x, inet_ntoa(n[m].addr));
				strcat(x, "/");
				strcat(x, inet_ntoa(n[m].mask));
				strcat(x, " ");
			}
		}
		sprintf(x+strlen(x), "\nWORKINGNUM %d\n", workingnum);
		write(STDIN_FILENO, x, strlen(x));
	}
	if (s != -1)
		if (close(s))
			perror("close(s)");
	if (o != -1)
		if (close(o))
			perror("close(o)");
	return r;
}