]> git.ipfire.org Git - thirdparty/ldns.git/commitdiff
ldns-test-edns tool.
authorWouter Wijngaards <wouter@NLnetLabs.nl>
Tue, 14 Dec 2010 14:26:31 +0000 (14:26 +0000)
committerWouter Wijngaards <wouter@NLnetLabs.nl>
Tue, 14 Dec 2010 14:26:31 +0000 (14:26 +0000)
Changelog
examples/Makefile.in
examples/ldns-test-edns.1 [new file with mode: 0644]
examples/ldns-test-edns.c [new file with mode: 0644]

index ecb20353826dc3b9aba49f8348ac9422fb016e56..b7cf9c2788c889889fd6e71b297aa7ff6ae5aeea 100644 (file)
--- 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
index 9b712d8d015615f8476d5ada76d6b7a3bb0e4929..c7d7e9c6c1078578f3b8685f195a44391313404f 100644 (file)
@@ -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 (file)
index 0000000..6223e8f
--- /dev/null
@@ -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 <ldns-team@nlnetlabs.nl>. 
+.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 (file)
index 0000000..98b8206
--- /dev/null
@@ -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 <ldns/ldns.h>
+
+/** 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<argc; i++)
+               r = check_edns_ip(argv[i], LDNS_PORT);
+       return r;
+}