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