From: Wouter Wijngaards Date: Tue, 14 Dec 2010 14:26:31 +0000 (+0000) Subject: ldns-test-edns tool. X-Git-Tag: release-1.6.8rc1~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bf153acfb56723ae27576d7612e37fb94ef988fa;p=thirdparty%2Fldns.git ldns-test-edns tool. --- diff --git a/Changelog b/Changelog index ecb20353..b7cf9c27 100644 --- a/Changelog +++ b/Changelog @@ -6,6 +6,7 @@ Justin Ferguson). * Drill: Print both SHA-1 and SHA-256 corresponding DS records. * Print correct WHEN in query packet (is not always 1-1-1970) + * ldns-test-edns: new example tool that detects EDNS support. 1.6.7 2010-11-08 * EXPERIMENTAL ecdsa implementation, please do not enable on real diff --git a/examples/Makefile.in b/examples/Makefile.in index 9b712d8d..c7d7e9c6 100644 --- a/examples/Makefile.in +++ b/examples/Makefile.in @@ -46,6 +46,7 @@ MAIN_SOURCES = ldns-read-zone.c \ ldns-zcat.c \ ldns-dpa.c \ ldns-resolver.c \ + ldns-test-edns.c \ ldns-keyfetcher.c \ ldns-notify.c \ ldns-testns.c \ diff --git a/examples/ldns-test-edns.1 b/examples/ldns-test-edns.1 new file mode 100644 index 00000000..6223e8fc --- /dev/null +++ b/examples/ldns-test-edns.1 @@ -0,0 +1,32 @@ +.TH ldns-test-edns 1 "14 Dec 2010" +.SH NAME +ldns-test-edns \- test if dns cache supports EDNS and DNSSEC. +.SH SYNOPSIS +.B ldns-test-edns +.IR {ip} +.SH DESCRIPTION +\fBldns-test-edns\fR tests a DNS cache and checks if it supports EDNS0 and +DNSSEC types so that it can be used as a dnssec-enabled DNS cache. It sends +two queries to the cache, one for the root key and one for a DS record. +These must succeed, the answer must have EDNS, that type and signatures. +.PP +If the IP address is good for DNSSEC, it is printed with 'OK'. Otherwise +short description is given of the failure. +If OK is given, the cache should be good to use as a cache for a local +configured DNSSEC validator. +.PP +The tool assumes the root is signed and Sweden is signed. +Also, the queries are sent with the CD flag, the tool does not check that the +results are validated, but that they \fBcan\fR be validated. +.SH OPTIONS +\fBldns-test-edns\fR takes one or more IP addresses, it checks them in turn. +IPv4 and IPv6 addresses can be given. The exit value is for the last checked +IP address: 0 is OK, 1 is failure, 2 is some sort of network failure. +.SH AUTHOR +Written by the ldns team as an example for ldns usage. +.SH REPORTING BUGS +Report bugs to . +.SH COPYRIGHT +Copyright (C) 2010 NLnet Labs. This is free software. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. diff --git a/examples/ldns-test-edns.c b/examples/ldns-test-edns.c new file mode 100644 index 00000000..98b82065 --- /dev/null +++ b/examples/ldns-test-edns.c @@ -0,0 +1,227 @@ +/* + * ldns-test-edns tries to get DNSKEY and RRSIG from an IP address. + * This can be used to test if a DNS cache supports DNSSEC (caching RRSIGs), + * i.e. for automatic configuration utilities or when you get a new DNS cache + * from DHCP and wonder if your local validator could use that as a cache. + * + * (c) NLnet Labs 2010 + * See the file LICENSE for the license + */ + +#include "config.h" +#include "errno.h" +#include + +/** parse IP address */ +static int +convert_addr(char* str, int p, struct sockaddr_storage* addr, socklen_t* len) +{ +#ifdef AF_INET6 + if(strchr(str, ':')) { + *len = sizeof(struct sockaddr_in6); + ((struct sockaddr_in6*)addr)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)addr)->sin6_port = htons(p); + if(inet_pton(AF_INET6, str, + &((struct sockaddr_in6*)addr)->sin6_addr) == 1) + return 1; + } else { +#endif + *len = sizeof(struct sockaddr_in); + ((struct sockaddr_in*)addr)->sin_family = AF_INET; + ((struct sockaddr_in*)addr)->sin_port = htons(p); + if(inet_pton(AF_INET, str, + &((struct sockaddr_in*)addr)->sin_addr) == 1) + return 1; +#ifdef AF_INET6 + } +#endif + printf("error: cannot parse IP address %s\n", str); + return 0; +} + +/** create a query to test */ +static ldns_buffer* +make_query(char* nm, int tp) +{ + /* with EDNS DO and CDFLAG */ + ldns_buffer* b = ldns_buffer_new(512); + ldns_pkt* p; + ldns_status s; + if(!b) { + printf("error: out of memory\n"); + return NULL; + } + + s = ldns_pkt_query_new_frm_str(&p, nm, tp, LDNS_RR_CLASS_IN, + LDNS_RD|LDNS_CD); + if(s != LDNS_STATUS_OK) { + printf("error: %s\n", ldns_get_errorstr_by_id(s)); + ldns_buffer_free(b); + return NULL; + } + if(!p) { + printf("error: out of memory\n"); + ldns_buffer_free(b); + return NULL; + } + + ldns_pkt_set_edns_do(p, 1); + ldns_pkt_set_edns_udp_size(p, 4096); + ldns_pkt_set_id(p, ldns_get_random()); + if( (s=ldns_pkt2buffer_wire(b, p)) != LDNS_STATUS_OK) { + printf("error: %s\n", ldns_get_errorstr_by_id(s)); + ldns_pkt_free(p); + ldns_buffer_free(b); + return NULL; + } + ldns_pkt_free(p); + + return b; +} + +/** try 3 times to get an EDNS reply from the server, exponential backoff */ +static int +get_packet(struct sockaddr_storage* addr, socklen_t len, char* nm, int tp, + uint8_t **wire, size_t* wlen) +{ + struct timeval t; + ldns_buffer* qbin; + ldns_status s; + int tries = 0; + + memset(&t, 0, sizeof(t)); + t.tv_usec = 100 * 1000; /* 100 milliseconds (then 200, 400, 800) */ + + qbin = make_query(nm, tp); + if(!qbin) + return 0; + while(tries < 4) { + tries ++; + s = ldns_udp_send(wire, qbin, addr, len, t, wlen); + if(s != LDNS_STATUS_NETWORK_ERR) { + break; + } + t.tv_usec *= 2; + if(t.tv_usec > 1000*1000) { + t.tv_usec -= 1000*1000; + t.tv_sec += 1; + } + } + ldns_buffer_free(qbin); + if(tries == 4) { + printf("timeout\n"); + return 0; + } + if(s != LDNS_STATUS_OK) { + printf("error: %s\n", ldns_get_errorstr_by_id(s)); + return 0; + } + return 1; +} + +/** test if type is present in returned packet */ +static int +check_type_in_answer(ldns_pkt* p, int t) +{ + ldns_rr_list *l = ldns_pkt_rr_list_by_type(p, t, LDNS_SECTION_ANSWER); + if(!l) { + char* s = ldns_rr_type2str(t); + printf("no DNSSEC %s\n", s?s:"(out of memory)"); + LDNS_FREE(s); + return 0; + } + ldns_rr_list_deep_free(l); + return 1; +} + +/** check the packet and make sure that EDNS and DO and the type and RRSIG */ +static int +check_packet(uint8_t* wire, size_t len, int tp) +{ + ldns_pkt *p = NULL; + ldns_rr_list* l; + ldns_status s; + if( (s=ldns_wire2pkt(&p, wire, len)) != LDNS_STATUS_OK) { + printf("error: %s\n", ldns_get_errorstr_by_id(s)); + goto failed; + } + if(!p) { + printf("error: out of memory\n"); + goto failed; + } + + /* does DNS work? */ + if(ldns_pkt_get_rcode(p) != LDNS_RCODE_NOERROR) { + char* r = ldns_pkt_rcode2str(ldns_pkt_get_rcode(p)); + printf("no answer, %s\n", r?r:"(out of memory)"); + LDNS_FREE(r); + goto failed; + } + + /* test EDNS0 presence, of OPT record */ + /* LDNS forgets during pkt parse, but we test the ARCOUNT; + * 0 additionals means no EDNS(on the wire), and after parsing the + * same additional RRs as before means no EDNS OPT */ + if(LDNS_ARCOUNT(wire) == 0 || + ldns_pkt_arcount(p) == LDNS_ARCOUNT(wire)) { + printf("no EDNS\n"); + goto failed; + } + + /* test if the type, RRSIG present */ + if(!check_type_in_answer(p, tp) || + !check_type_in_answer(p, LDNS_RR_TYPE_RRSIG)) { + goto failed; + } + + LDNS_FREE(wire); + ldns_pkt_free(p); + return 1; +failed: + LDNS_FREE(wire); + ldns_pkt_free(p); + return 0; +} + +/** check EDNS at this IP and port */ +static int +check_edns_ip(char* ip, int port) +{ + struct sockaddr_storage addr; + socklen_t len = 0; + uint8_t* wire; + size_t wlen; + memset(&addr, 0, sizeof(addr)); + printf("%s ", ip); + if(!convert_addr(ip, port, &addr, &len)) + return 2; + /* try to send 3 times to the IP address, test root key */ + if(!get_packet(&addr, len, ".", LDNS_RR_TYPE_DNSKEY, &wire, &wlen)) + return 2; + if(!check_packet(wire, wlen, LDNS_RR_TYPE_DNSKEY)) + return 1; + /* check support for caching type DS for chains of trust */ + if(!get_packet(&addr, len, "se.", LDNS_RR_TYPE_DS, &wire, &wlen)) + return 2; + if(!check_packet(wire, wlen, LDNS_RR_TYPE_DS)) + return 1; + printf("OK\n"); + return 0; +} + +int +main(int argc, char **argv) +{ + int i, r; + if (argc < 2 || strncmp(argv[1], "-h", 3) == 0) { + printf("Usage: ldns-test-edns {ip address}\n"); + printf("Tests if the DNS cache at IP address supports EDNS.\n"); + printf("if it works, print IP address OK.\n"); + printf("exit value, last IP is 0:OK, 1:fail, 2:net error.\n"); + exit(1); + } + + for(i=1; i