summaryrefslogblamecommitdiffstats
path: root/main.c
blob: 02ca7ddad02470b4d1af869f2f03fe9e96f240d3 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
                                    










                                         


                                       
                                        
                        
                 




                                                           

                      
                                                                
                                                                                                     
                                                                                               
                                                                                                

                                                                                                 

                                                                                                     


                                                                                                     
                                                                                                  

                                                                                                     






                                                                                                     
                        

























                                                                                                    
  




































                                                                                                     





                                                                                                    

                              

                 











































                                                                


                 


                                                                

                                                                                                  
                                                                
                                                                                                   

                                                                
                                                                                                     



                                                                  

                                                                                           
                          
         
                 


















                     
                                                                                                    










              
  





                 
  







                    
  
















                                                                 
               




                                                                




                                      
  

























                                                                                           
          















                                                                                     


                                   



























                                                               



                                                      



                                                                                             

                                               














                                                                         






                                                                                                     
                                              

                                               


                                                                     
                                                                

                                       

                                                                                    
                                      


                                                                                             
                                      


                                       





                                                                         
                              










                                                                                  
                       



                                 
                       

                       



                                           


                                           

                 
#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;
}