1 /*
2  * ldns-test-edns tries to get DNSKEY and RRSIG from an IP address.
3  * This can be used to test if a DNS cache supports DNSSEC (caching RRSIGs),
4  * i.e. for automatic configuration utilities or when you get a new DNS cache
5  * from DHCP and wonder if your local validator could use that as a cache.
6  *
7  * (c) NLnet Labs 2010
8  * See the file LICENSE for the license
9  */
10 
11 #include "config.h"
12 #include "errno.h"
13 #include <ldns/ldns.h>
14 
15 /** print error details */
16 static int verb = 1;
17 
cast_sockaddr_storage2sockaddr_in6(struct sockaddr_storage * s)18 static struct sockaddr_in6* cast_sockaddr_storage2sockaddr_in6(
19 		struct sockaddr_storage* s)
20 {
21 	return (struct sockaddr_in6*)s;
22 }
23 
cast_sockaddr_storage2sockaddr_in(struct sockaddr_storage * s)24 static struct sockaddr_in* cast_sockaddr_storage2sockaddr_in(
25 		struct sockaddr_storage* s)
26 {
27 	return (struct sockaddr_in*)s;
28 }
29 
30 /** parse IP address */
31 static int
convert_addr(char * str,int p,struct sockaddr_storage * addr,socklen_t * len)32 convert_addr(char* str, int p, struct sockaddr_storage* addr, socklen_t* len)
33 {
34 #ifdef AF_INET6
35 	if(strchr(str, ':')) {
36 		*len = (socklen_t)sizeof(struct sockaddr_in6);
37 		cast_sockaddr_storage2sockaddr_in6(addr)->sin6_family =
38 			AF_INET6;
39 		cast_sockaddr_storage2sockaddr_in6(addr)->sin6_port =
40 			htons((uint16_t)p);
41 		if(inet_pton(AF_INET6, str,
42 			&((struct sockaddr_in6*)addr)->sin6_addr) == 1)
43 			return 1;
44 	} else {
45 #endif
46 		*len = (socklen_t)sizeof(struct sockaddr_in);
47 #ifndef S_SPLINT_S
48 		cast_sockaddr_storage2sockaddr_in(addr)->sin_family =
49 			AF_INET;
50 #endif
51 		cast_sockaddr_storage2sockaddr_in(addr)->sin_port =
52 			htons((uint16_t)p);
53 		if(inet_pton(AF_INET, str,
54 			&((struct sockaddr_in*)addr)->sin_addr) == 1)
55 			return 1;
56 #ifdef AF_INET6
57 	}
58 #endif
59 	if(verb) printf("error: cannot parse IP address %s\n", str);
60 	return 0;
61 }
62 
63 /** create a query to test */
64 static ldns_buffer*
make_query(const char * nm,int tp)65 make_query(const char* nm, int tp)
66 {
67 	/* with EDNS DO and CDFLAG */
68 	ldns_buffer* b = ldns_buffer_new(512);
69 	ldns_pkt* p;
70 	ldns_status s;
71 	if(!b) {
72 		if(verb) printf("error: out of memory\n");
73 		return NULL;
74 	}
75 
76 	s = ldns_pkt_query_new_frm_str(&p, nm, tp, LDNS_RR_CLASS_IN,
77 		(uint16_t)(LDNS_RD|LDNS_CD));
78 	if(s != LDNS_STATUS_OK) {
79 		if(verb) printf("error: %s\n", ldns_get_errorstr_by_id(s));
80 		ldns_buffer_free(b);
81 		return NULL;
82 	}
83 	if(!p) {
84 		if(verb) printf("error: out of memory\n");
85 		ldns_buffer_free(b);
86 		return NULL;
87 	}
88 
89 	ldns_pkt_set_edns_do(p, 1);
90 	ldns_pkt_set_edns_udp_size(p, 4096);
91 	ldns_pkt_set_id(p, ldns_get_random());
92 	if( (s=ldns_pkt2buffer_wire(b, p)) != LDNS_STATUS_OK) {
93 		if(verb) printf("error: %s\n", ldns_get_errorstr_by_id(s));
94 		ldns_pkt_free(p);
95 		ldns_buffer_free(b);
96 		return NULL;
97 	}
98 	ldns_pkt_free(p);
99 
100 	return b;
101 }
102 
103 /** try 3 times to get an EDNS reply from the server, exponential backoff */
104 static int
get_packet(struct sockaddr_storage * addr,socklen_t len,const char * nm,int tp,uint8_t ** wire,size_t * wlen)105 get_packet(struct sockaddr_storage* addr, socklen_t len, const char* nm,
106 	int tp, uint8_t **wire, size_t* wlen)
107 {
108 	struct timeval t;
109 	ldns_buffer* qbin;
110 	ldns_status s;
111 	int tries = 0;
112 
113 	memset(&t, 0, sizeof(t));
114 	t.tv_usec = 100 * 1000; /* 100 milliseconds (then 200, 400, 800) */
115 
116 	qbin = make_query(nm, tp);
117 	if(!qbin)
118 		return 0;
119 	while(tries < 4) {
120 		tries ++;
121 		s = ldns_udp_send(wire, qbin, addr, len, t, wlen);
122 		if(s != LDNS_STATUS_NETWORK_ERR) {
123 			break;
124 		}
125 		t.tv_usec *= 2;
126 		if(t.tv_usec > 1000*1000) {
127 			t.tv_usec -= 1000*1000;
128 			t.tv_sec += 1;
129 		}
130 	}
131 	ldns_buffer_free(qbin);
132 	if(tries == 4) {
133 		if(verb) printf("timeout\n");
134 		return 0;
135 	}
136 	if(s != LDNS_STATUS_OK) {
137 		if(verb) printf("error: %s\n", ldns_get_errorstr_by_id(s));
138 		return 0;
139 	}
140 	return 1;
141 }
142 
143 /** test if type is present in returned packet */
144 static int
check_type_in_answer(ldns_pkt * p,int t)145 check_type_in_answer(ldns_pkt* p, int t)
146 {
147 	ldns_rr_list *l = ldns_pkt_rr_list_by_type(p, t, LDNS_SECTION_ANSWER);
148 	if(!l) {
149 		char* s = ldns_rr_type2str(t);
150 		if(verb) printf("no DNSSEC %s\n", s?s:"(out of memory)");
151 		LDNS_FREE(s);
152 		return 0;
153 	}
154 	ldns_rr_list_deep_free(l);
155 	return 1;
156 }
157 
158 /** check the packet and make sure that EDNS and DO and the type and RRSIG */
159 static int
check_packet(uint8_t * wire,size_t len,int tp)160 check_packet(uint8_t* wire, size_t len, int tp)
161 {
162 	ldns_pkt *p = NULL;
163 	ldns_status s;
164 	if( (s=ldns_wire2pkt(&p, wire, len)) != LDNS_STATUS_OK) {
165 		if(verb) printf("error: %s\n", ldns_get_errorstr_by_id(s));
166 		goto failed;
167 	}
168 	if(!p) {
169 		if(verb) printf("error: out of memory\n");
170 		goto failed;
171 	}
172 
173 	/* does DNS work? */
174 	if(ldns_pkt_get_rcode(p) != LDNS_RCODE_NOERROR) {
175 		char* r = ldns_pkt_rcode2str(ldns_pkt_get_rcode(p));
176 		if(verb) printf("no answer, %s\n", r?r:"(out of memory)");
177 		LDNS_FREE(r);
178 		goto failed;
179 	}
180 
181 	/* test EDNS0 presence, of OPT record */
182 	/* LDNS forgets during pkt parse, but we test the ARCOUNT;
183 	 * 0 additional means no EDNS(on the wire), and after parsing the
184 	 * same additional RRs as before means no EDNS OPT */
185 	if(LDNS_ARCOUNT(wire) == 0 ||
186 		ldns_pkt_arcount(p) == LDNS_ARCOUNT(wire)) {
187 		if(verb) printf("no EDNS\n");
188 		goto failed;
189 	}
190 
191 	/* test if the type, RRSIG present */
192 	if(!check_type_in_answer(p, tp) ||
193 	   !check_type_in_answer(p, LDNS_RR_TYPE_RRSIG)) {
194 		goto failed;
195 	}
196 
197 	LDNS_FREE(wire);
198 	ldns_pkt_free(p);
199 	return 1;
200 failed:
201 	LDNS_FREE(wire);
202 	ldns_pkt_free(p);
203 	return 0;
204 }
205 
206 /** check EDNS at this IP and port */
207 static int
check_edns_ip(char * ip,int port,int info)208 check_edns_ip(char* ip, int port, int info)
209 {
210 	struct sockaddr_storage addr;
211 	socklen_t len = 0;
212 	uint8_t* wire;
213 	size_t wlen;
214 	memset(&addr, 0, sizeof(addr));
215 	if(verb) printf("%s ", ip);
216 	if(!convert_addr(ip, port, &addr, &len))
217 		return 2;
218 	/* try to send 3 times to the IP address, test root key */
219 	if(!get_packet(&addr, len, ".", LDNS_RR_TYPE_DNSKEY, &wire, &wlen))
220 		return 2;
221 	if(!check_packet(wire, wlen, LDNS_RR_TYPE_DNSKEY))
222 		return 1;
223 	/* check support for caching type DS for chains of trust */
224 	if(!get_packet(&addr, len, "se.", LDNS_RR_TYPE_DS, &wire, &wlen))
225 		return 2;
226 	if(!check_packet(wire, wlen, LDNS_RR_TYPE_DS))
227 		return 1;
228 	if(verb) printf("OK\n");
229 	if(info) printf(" %s", ip);
230 	return 0;
231 }
232 
233 int
main(int argc,char ** argv)234 main(int argc, char **argv)
235 {
236 	int i, r=0, info=0, ok=0;
237 #ifdef USE_WINSOCK
238 	WSADATA wsa_data;
239 	if(WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) {
240 		printf("WSAStartup failed\n"); exit(1);
241 	}
242 #endif
243 	if (argc < 2 || strncmp(argv[1], "-h", 3) == 0) {
244 		printf("Usage: ldns-test-edns [-i] {ip address}\n");
245 		printf("Tests if the DNS cache at IP address supports EDNS.\n");
246 		printf("if it works, print IP address OK.\n");
247 		printf("-i: print IPs that are OK or print 'off'.\n");
248 		printf("exit value, last IP is 0:OK, 1:fail, 2:net error.\n");
249 		exit(1);
250 	}
251 	if(strcmp(argv[1], "-i") == 0) {
252 		info = 1;
253 		verb = 0;
254 	}
255 
256 	for(i=1+info; i<argc; i++) {
257 		r = check_edns_ip(argv[i], LDNS_PORT, info);
258 		if(r == 0)
259 			ok++;
260 	}
261 	if(info && !ok)
262 		printf("off\n");
263 	return r;
264 }
265