summaryrefslogtreecommitdiffstats
path: root/main.c
blob: 41b18dc776e55e371d0d94a627168610fefbe619 (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
#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 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 ip] [-b ip] [-d domain] [-h] [-o file] [-p port] [-t μs] [-w μs] net1 [net2 ...]\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" \
	"	-t Number of microseconds to wait between sent packets. (default 1000 - 64 KB/s)\n" \
	"	-w Finish after μs after recv'd last packet when done 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\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" \
	"It would take a day to scan the entire address space with the default timings.\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 */) {
	if (o == -1)
		return -1;
	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;
	if (write(o, b, LOGUDP_L) == -1) { /* atomic and thread safe, as per posix */
		perror("write(" STR(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 = {	/* this uses heap and does not fix heap if realloc failed. */
		.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;
	const char * c = u+sizeof(struct header);
	while (q--) {
		int č = name2domain_len(u, s, c);
		if (č < 0)
			goto r;
		if (!(o = realloc(o, č)))
			goto r;
		if (!(c = name2domain(o, u, s, c)))
			goto r;
		struct question * ć = (struct question *) (c+1);
		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 (!(o = realloc(o, č)))
			goto r;
		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 = 2;
	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 */
	struct in_net * n; /* networks */
	int l; /* count of networks */
	int i = 0; /* network index */
	int j = -1; /* host in network index */
	int t = 1000;
	int w = 1000000;
	struct in_addr h; /* host to scan */
	signal(SIGINT, handler);
	signal(SIGTERM, handler);
	while (1) {
		switch (getopt(argc, argv, ":a:b:d:ho:p:t:w:")) {
			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, 00664)) == -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,
					.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 't':
				t = atoi(optarg);
				break;
			case 'w':
				w = 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 = 4;
				goto r;
			case ':':
				fprintf(stderr, "missing option argument :: " HELP, argv[0]);
				r = 5;
				goto r;
			default:
				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;
	}
	struct timespec lp = { /* last packet */
		.tv_sec = 0
	};
	while (!finish) {
		if (!(h = host(n[i], ++j)).s_addr) {
			if (++i >= l) {
				fprintf(stderr, "finished sending, waiting for last replies\n");
				if (clock_gettime(CLOCK_MONOTONIC, &lp) == -1) {
					perror("clock_gettime(CLOCK_MONOTONIC, &z)");
					r = 10;
					goto r;
				}
				goto i;
			}
			else
				h = host(n[i], (j = 0));
		}
		struct sockaddr_in e = {
			.sin_family = AF_INET,
			.sin_port = htons(53),
			.sin_addr = h
		};
		struct header h = {
			.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 = 11;
			goto r;
		}
		if (L > 65535) { /* pebkac, there'll be no error message here */
			r = 12;
			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;
		int ž;
		if ((ž = logudp(o, b, e, u, L)) < -1) {
			fprintf(stderr, "logudp(o, b, e, u, L) == %d\n", ž);
			r = 13;
			goto r;
		}
		if (sendto(s, u, L, 0, (struct sockaddr *) &e, sizeof(struct sockaddr)) == -1) {
			perror("sendto(s, u, L, 0, (struct sockaddr *) &e, sizeof(struct sockaddr))");
			r = 14;
			goto r;
		}
		struct timespec z;
i:
		if (clock_gettime(CLOCK_MONOTONIC, &z) == -1) {
			perror("clock_gettime(CLOCK_MONOTONIC, &z)");
			r = 15;
			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 microseconds. 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 = 16;
			goto r;
		}
		if (!p) {
			if (lp.tv_sec)
				goto i;
			else
				continue;
		}
		if (q.revents & POLLERR || q.revents & POLLHUP || q.revents & POLLNVAL) {
			r = 17;
			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 sock...");
					r = 18;
					goto r;
				}
				break;
			}
			if (lp.tv_sec)
				lp = z; /* this loop ends nearly in an instant */
			if ((ž = logudp(o, f, b, u, š)) < -1) {
				fprintf(stderr, "logudp(o, f, b, u, š) == %d\n", ž);
				return 3;
			}
			fprintf(stderr, "received response from %s\n", inet_ntoa(f.sin_addr));
			ž = 0;
			struct in_addr i = parse_a(u, 65535, d, strlen(d), ž++);
			if (i.s_addr == a.s_addr) /* if we go back to multithread, change to write. */
				printf("WORKING %s\n", inet_ntoa(f.sin_addr));
			if (i.s_addr && i.s_addr != a.s_addr)
				printf("LYING %s WITH %s\n", inet_ntoa(f.sin_addr), inet_ntoa(i));

		}
		if (z.tv_sec)
			goto i;
	}
r:
	if (!r && j != -1) { /* TODO: tell EXACT packets that were sent before termination. */
		char * x = alloca(l*31+strlen("SCANNED  \n0"));	/* currently, if scan was */
		strcpy(x, "SCANNED ");				/* terminated, only networks before */
		for (int m = 0; m < (finish ? i : l); m++) {	/* network at which scan was */
			strcat(x, inet_ntoa(n[m].addr));	/* terminated are reported to be */
			strcat(x, "/");				/* scanned, not mentioning the */
			strcat(x, inet_ntoa(n[m].mask));	/* part of the last not mentioned */
			strcat(x, " ");				/* network that was scanned. */
		}						/* this may lead to statistical */
		strcat(x, "\n");				/* issues because it would appear */
		write(STDIN_FILENO, x, strlen(x));		/* as if we received packets from */
	}							/* hosts we haven't queried yet. */
	if (s != -1)
		if (close(s))
			perror("close(s)");
	if (o != -1)
		if (close(o))
			perror("close(o)");
	return r;
}