summaryrefslogtreecommitdiffstats
path: root/utils/dns.c
blob: e365e7302729ada063667ea50fef771aa263e2b4 (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
#include <stdio.h>		// example of an asyncronous resolver that resolves
#include <stdlib.h>		// a SRV record to obtain address:port combinations
#include <sys/socket.h>		// of useful nodes
#include <netinet/in.h>		// BUGS: does not handle CNAMES - this is
#include <arpa/nameser.h>	// technically against the standard, but would be
#include <resolv.h>		// nice if the domain pointed to by the SRV record
#include <arpa/inet.h>		// travnik.sijanec.eu suddenly becomes a CNAME,
#include <error.h>		// since it's not under my control
#include <errno.h>
#include <unistd.h>
#include <sys/poll.h>
#include <string.h>
#include <signal.h>
#define S0(x) (x ? x : "")
int main (int argc, char ** argv) { // does not free/close on error
	alarm(1);
	if (argc != 1+1)
		error_at_line(1, 0, __FILE__, __LINE__, "%s: _dht._udp.travnik.sijanec.eu", S0(argv[0]));
	struct __res_state state;
	if (res_ninit(&state) == -1)
		error_at_line(2, 0, __FILE__, __LINE__, "res_ninit");
	int sock = socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
	if (!sock)
		error_at_line(3, errno, __FILE__, __LINE__, "socket");
	struct sockaddr_in6 a = {
		.sin6_family = AF_INET6,
		.sin6_addr = in6addr_any
	};
	if (bind(sock, (struct sockaddr *) &a, sizeof a) == -1)
		error_at_line(4, errno, __FILE__, __LINE__, "bind");
	unsigned char packet[65536];
	int size = res_nmkquery(&state, QUERY, argv[1], ns_c_in, ns_t_srv, NULL, 0, NULL, packet, 65536);
	if (size == -1)
		error_at_line(5, 0, __FILE__, __LINE__, "res_mkquery");
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
	for (int i = 0; i < state.nscount; i++)
		if (state.nsaddr_list[i].sin_family == AF_INET) // leider only ipv4
			if (sendto(sock, packet, size, MSG_DONTWAIT | MSG_NOSIGNAL, &state.nsaddr_list[i], sizeof (state.nsaddr_list[i])) == -1)
			error_at_line(6, errno, __FILE__, __LINE__, "sendto(AF_INET)");
/*	for (int i = 0; i < state._u._ext.nscount6; i++)
		if (sendto(sock, packet, size, MSG_DONTWAIT | MSG_NOSIGNAL, &state._u._ext.nsaddrs[i], sizeof (state._u._ext.nsaddrs[i])) == -1)
			error_at_line(7, errno, __FILE__, __LINE__, "sendto(AF_INET6)"); */ // does not work
#pragma GCC diagnostic pop
	struct pollfd pollfd = {
		.fd = sock,
		.events = POLLIN
	};
	r:
	;
	int status = poll(&pollfd, 1, -1);
	if (status == 1) {
		socklen_t l = sizeof a;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
		int len = recvfrom(sock, packet, 65536, MSG_DONTWAIT | MSG_TRUNC, &a, &l);
#pragma GCC diagnostic pop
		if (len == -1)
			error_at_line(8, errno, __FILE__, __LINE__, "recvfrom");
		ns_msg handle;
		char remote[INET6_ADDRSTRLEN+7];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
		if (!inet_ntop(AF_INET6, &a.sin6_addr, remote, INET6_ADDRSTRLEN+7))
			error_at_line(9, errno, __FILE__, __LINE__, "inet_pton");
#pragma GCC diagnostic pop
		sprintf(remote+strlen(remote), ":%u", ntohs(a.sin6_port));
		if (ns_initparse(packet, len, &handle) == -1)
			error_at_line(10, 0, __FILE__, __LINE__, "ns_initparse %s", remote);
		for (int i = 0; i < ns_msg_count(handle, ns_s_an); i++) {
			struct __ns_rr rr;
			if (ns_parserr(&handle, ns_s_an, i, &rr) == -1)
				break;
			if (rr.type != ns_t_srv && rr.type != ns_t_a && rr.type != ns_t_aaaa)
				continue;
			char target[NS_MAXDNAME];
			char address[INET_ADDRSTRLEN+INET6_ADDRSTRLEN+7];
			switch (rr.rdlength) {
				case 4:
					if (!inet_ntop(AF_INET, rr.rdata, address, INET6_ADDRSTRLEN+INET_ADDRSTRLEN+7))
						error_at_line(11, errno, __FILE__, __LINE__, "inet_ntop(AF_INET)");
					sprintf(address+strlen(address), ":%u", ntohs(*((uint16_t *) packet)));
					printf("%s\tA\t%s\n", remote, address);
					break;
				case 16:
					if (!inet_ntop(AF_INET6, rr.rdata, address, INET6_ADDRSTRLEN+INET_ADDRSTRLEN+7))
						error_at_line(12, errno, __FILE__, __LINE__, "inet_ntop(AF_INET6)");
					sprintf(address+strlen(address), ":%u", ntohs(*((uint16_t *) packet)));
					printf("%s\tAAAA\t%s\n", remote, address);
					break;
				default:
					if (rr.rdlength < 3*2+3)
						continue;
					if (ns_name_uncompress(packet, packet+len, rr.rdata+3*2, target, NS_MAXDNAME) == -1)
						error_at_line(13, 0, __FILE__, __LINE__, "ns_name_uncompress %s", remote);
					// printf("%s\tSRV\t%u\t%s\n", remote, htons(*((uint16_t *) (rr.rdata + 4))), target);
					for (int j = 0; j <= 1; j++) {
						size = res_nmkquery(&state, QUERY, target, ns_c_in, j ? ns_t_a : ns_t_aaaa, NULL, 0, NULL, packet, 65536);
						if (size == -1)
							error_at_line(14, 0, __FILE__, __LINE__, "res_mkquery(A)");
						memcpy(packet, rr.rdata+4, 2);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"
						if (sendto(sock, packet, size, MSG_DONTWAIT | MSG_NOSIGNAL, &a, l) == -1)
							error_at_line(15, errno, __FILE__, __LINE__, "sendto");
					}
					break;
			}
#pragma GCC diagnostic pop
		}
		goto r;
	}
	res_nclose(&state);
	if (close(sock) == -1)
		error_at_line(16, errno, __FILE__, __LINE__, "close");
}