From a2a3be06d7517e8552c7048f06c3bc0e1076dc69 Mon Sep 17 00:00:00 2001 From: Buffrr <41967894+buffrr@users.noreply.github.com> Date: Sat, 12 Sep 2020 04:36:36 -0700 Subject: [PATCH 01/12] rs: respond with servfail if bogus --- src/rs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rs.c b/src/rs.c index 3794190d..4f183dc3 100644 --- a/src/rs.c +++ b/src/rs.c @@ -437,8 +437,10 @@ hsk_rs_respond( hsk_rs_log(ns, " secure: %d\n", result->secure); hsk_rs_log(ns, " bogus: %d\n", result->bogus); - if (result->why_bogus) + if (result->bogus) { hsk_rs_log(ns, " why_bogus: %s\n", result->why_bogus); + goto fail; + } uint8_t *data = result->answer_packet; size_t data_len = result->answer_len; @@ -455,7 +457,7 @@ hsk_rs_respond( msg->code = result->rcode; msg->flags |= HSK_DNS_RA; - if (result->secure && !result->bogus) + if (result->secure) msg->flags |= HSK_DNS_AD; // Strip out non-answer sections. From d00cb17cbd63148035ded5e8f1fec332b35b3721 Mon Sep 17 00:00:00 2001 From: Buffrr <41967894+buffrr@users.noreply.github.com> Date: Sat, 6 Mar 2021 16:13:03 -0700 Subject: [PATCH 02/12] resource: add no ds proof Adds an NSEC record to indicate that we only have NS NSEC RRSIG. This proves that we don't have a DS rr and allows unbound to treat the zone as unsigned. --- src/resource.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/resource.c b/src/resource.c index 96e2f5fa..2b76daa7 100644 --- a/src/resource.c +++ b/src/resource.c @@ -23,6 +23,11 @@ static const uint8_t hsk_type_map[] = { 0x00, 0x00, 0x03, 0x80 }; +// NS RRSIG NSEC +static const uint8_t hsk_type_map_ns[] = { + 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00, 0x03 +}; + /* * Helpers */ @@ -1008,10 +1013,15 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { hsk_resource_to_ns(rs, name, ns); hsk_resource_to_ds(rs, name, ns); hsk_resource_to_glue(rs, name, ar); - if (!hsk_resource_has(rs, HSK_DS)) - hsk_dnssec_sign_zsk(ns, HSK_DNS_NS); - else - hsk_dnssec_sign_zsk(ns, HSK_DNS_DS); + if (!hsk_resource_has(rs, HSK_DS)) { + hsk_dnssec_sign_zsk(ns, HSK_DNS_NS); + // No DS proof: + // This allows unbound to treat the zone as unsigned (and not bogus) + hsk_resource_to_empty(name, hsk_type_map_ns, sizeof(hsk_type_map_ns), ns); + hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + } else { + hsk_dnssec_sign_zsk(ns, HSK_DNS_DS); + } } else { // Needs SOA. // Empty proof: From 5a8611f91fe12e3d7f3a15ebd20b5db9d577a913 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Thu, 18 Nov 2021 11:01:05 -0500 Subject: [PATCH 03/12] resource: move all NSEC type maps to resource.h --- src/ns.c | 10 ---------- src/resource.c | 12 ------------ src/resource.h | 31 +++++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/ns.c b/src/ns.c index 1325c590..3707162d 100644 --- a/src/ns.c +++ b/src/ns.c @@ -25,16 +25,6 @@ #include "uv.h" #include "dnssec.h" -// A RRSIG NSEC -static const uint8_t hsk_type_map_a[] = { - 0x00, 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03 -}; - -// AAAA RRSIG NSEC -static const uint8_t hsk_type_map_aaaa[] = { - 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x03 -}; - /* * Types */ diff --git a/src/resource.c b/src/resource.c index 2b76daa7..15101c2d 100644 --- a/src/resource.c +++ b/src/resource.c @@ -16,18 +16,6 @@ #include "resource.h" #include "utils.h" -// NS SOA RRSIG NSEC DNSKEY -// Possibly add A, AAAA, and DS -static const uint8_t hsk_type_map[] = { - 0x00, 0x07, 0x22, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x80 -}; - -// NS RRSIG NSEC -static const uint8_t hsk_type_map_ns[] = { - 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00, 0x03 -}; - /* * Helpers */ diff --git a/src/resource.h b/src/resource.h index 46e73b1f..506ef2ff 100644 --- a/src/resource.h +++ b/src/resource.h @@ -16,6 +16,37 @@ #include "addr.h" #include "dns.h" +// A RRSIG NSEC +static const uint8_t hsk_type_map_a[] = { + 0x00, 0x06, 0x40, 0x00, 0x00, 0x00, 0x00, 0x03 +}; + +// AAAA RRSIG NSEC +static const uint8_t hsk_type_map_aaaa[] = { + 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x03 +}; + +// NS SOA RRSIG NSEC DNSKEY +static const uint8_t hsk_type_map[] = { + 0x00, 0x07, 0x22, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x80 +}; + +// NS RRSIG NSEC +static const uint8_t hsk_type_map_ns[] = { + 0x00, 0x06, 0x20, 0x00, 0x00, 0x00, 0x00, 0x03 +}; + +// RRSIG NSEC +static const uint8_t hsk_type_map_empty[] = { + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 +}; + +// TXT RRSIG NSEC +static const uint8_t hsk_type_map_txt[] = { + 0x00, 0x06, 0x00, 0x00, 0x80, 0x00, 0x00, 0x03 +}; + // Dummy record placeholder typedef struct hsk_record_s { uint8_t type; From 635b17cb86beef51e8cde74c9202859dee88c1cd Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 22 Dec 2021 13:36:27 -0500 Subject: [PATCH 04/12] resource: implement prev/next name for NSEC --- src/resource.c | 36 ++++++++++++++++++++++++++++++++++++ test/hnsd-test.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/src/resource.c b/src/resource.c index 15101c2d..1308395d 100644 --- a/src/resource.c +++ b/src/resource.c @@ -23,6 +23,12 @@ static void to_fqdn(char *name); +static void +next_name(const char *name, char *next); + +static void +prev_name(const char *name, char *prev); + /* * Resource serialization version 0 * Record types: read @@ -1155,6 +1161,36 @@ to_fqdn(char *name) { name[len + 1] = '\0'; } +static void +next_name(const char *name, char *next) { + size_t len = strlen(name); + if (name[len - 1] == '.') + len--; + + strcpy(next, name); + + if (len < 63) { + memcpy(&next[len], "\\000.", 6); + } else { + next[len - 1]++; + memcpy(&next[len], ".", 2); + } +} + +static void +prev_name(const char *name, char *prev) { + size_t len = strlen(name); + if (name[len - 1] == '.') + len--; + + strcpy(prev, name); + prev[len - 1]--; + + if (len < 63) { + memcpy(&prev[len], "\\255.", 6); + } +} + bool pointer_to_ip(const char *name, uint8_t *ip, uint16_t *family) { char label[HSK_DNS_MAX_LABEL + 1]; diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 7ec7b071..76adc93d 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -60,11 +60,57 @@ test_pointer_to_ip() { assert(family6 == HSK_DNS_AAAA); } +void +test_next_name() { + printf("test_next_name\n"); + + const char *name1 = "icecream."; + const char *name2 = "this-domain-name-has-sixty-three-octets-taking-max-label-length."; + char next1[HSK_DNS_MAX_NAME]; + char next2[HSK_DNS_MAX_NAME]; + + next_name(name1, next1); + next_name(name2, next2); + + assert(strcmp( + next1, + "icecream\\000." + ) == 0); + assert(strcmp( + next2, + "this-domain-name-has-sixty-three-octets-taking-max-label-lengti." + ) == 0); +} + +void +test_prev_name() { + printf("test_prev_name\n"); + + const char *name1 = "icecream."; + const char *name2 = "this-domain-name-has-sixty-three-octets-taking-max-label-length."; + char prev1[HSK_DNS_MAX_NAME]; + char prev2[HSK_DNS_MAX_NAME]; + + prev_name(name1, prev1); + prev_name(name2, prev2); + + assert(strcmp( + prev1, + "icecreal\\255." + ) == 0); + assert(strcmp( + prev2, + "this-domain-name-has-sixty-three-octets-taking-max-label-lengtg." + ) == 0); +} + int main() { printf("Testing hnsd...\n"); test_base32(); test_pointer_to_ip(); + test_next_name(); + test_prev_name(); printf("ok\n"); From 6053c6a45587ac9039c66aee68a826ae64fdfbba Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 22 Dec 2021 13:37:40 -0500 Subject: [PATCH 05/12] resource/ns: replace to_empty proofs with minimally-covering NSEC --- src/ns.c | 8 +- src/resource.c | 73 ++++++-- src/resource.h | 2 +- test/data/resource_vectors.h | 337 +++++++++++++++++++++++++++++++++++ test/hnsd-test.c | 66 +++++++ 5 files changed, 469 insertions(+), 17 deletions(-) create mode 100644 test/data/resource_vectors.h diff --git a/src/ns.c b/src/ns.c index 3707162d..6c455da4 100644 --- a/src/ns.c +++ b/src/ns.c @@ -340,8 +340,8 @@ hsk_ns_onrecv( hsk_dns_label_from(req->name, -2, synth); if (req->labels == 1) { - hsk_resource_to_empty(req->tld, NULL, 0, rrns); - hsk_dnssec_sign_zsk(rrns, HSK_DNS_NSEC); + // TLD '._synth' is being queried on its own, send SOA + // so recursive asks again with complete synth record. hsk_resource_root_to_soa(rrns); hsk_dnssec_sign_zsk(rrns, HSK_DNS_SOA); } @@ -366,14 +366,14 @@ hsk_ns_onrecv( // TODO: Make the reverse pointers TLDs. // Empty proof: if (family == HSK_DNS_A) { - hsk_resource_to_empty( + hsk_resource_to_nsec( req->name, hsk_type_map_a, sizeof(hsk_type_map_a), rrns ); } else { - hsk_resource_to_empty( + hsk_resource_to_nsec( req->name, hsk_type_map_aaaa, sizeof(hsk_type_map_aaaa), diff --git a/src/resource.c b/src/resource.c index 1308395d..1e72207c 100644 --- a/src/resource.c +++ b/src/resource.c @@ -866,7 +866,7 @@ hsk_resource_root_to_ds(hsk_dns_rrs_t *an) { } bool -hsk_resource_to_empty( +hsk_resource_to_nsec( const char *name, const uint8_t *type_map, size_t type_map_len, @@ -883,7 +883,10 @@ hsk_resource_to_empty( hsk_dns_nsec_rd_t *rd = rr->rd; - strcpy(rd->next_domain, "."); + char next[HSK_DNS_MAX_NAME]; + next_name(name, next); + strcpy(rd->next_domain, next); + rd->type_map = NULL; rd->type_map_len = 0; @@ -964,15 +967,41 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { hsk_resource_to_ns(rs, tld, ns); hsk_resource_to_ds(rs, tld, ns); hsk_resource_to_glue(rs, tld, ar); - if (!hsk_resource_has(rs, HSK_DS)) - hsk_dnssec_sign_zsk(ns, HSK_DNS_NS); - else + if (!hsk_resource_has(rs, HSK_DS)) { + // Prove there is an NS but no DS and sign NSEC + // Root doesn't sign NS for anything other than "." + hsk_resource_to_nsec( + tld, + hsk_type_map_ns, + sizeof(hsk_type_map_ns), + ns + ); + hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + } else { + // Domain has a DS and an NS + // Root only signs the DS hsk_dnssec_sign_zsk(ns, HSK_DNS_DS); + } } else { - // Needs SOA. - // Empty proof: - hsk_resource_to_empty(tld, NULL, 0, ns); + // Domain has no NS + // We can prove there is a TXT or empty and sign NSEC + if (hsk_resource_has(rs, HSK_TEXT)) { + hsk_resource_to_nsec( + tld, + hsk_type_map_txt, + sizeof(hsk_type_map_txt), + ns + ); + } else { + hsk_resource_to_nsec( + tld, + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + ns + ); + } hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + // Needs SOA. hsk_resource_root_to_soa(ns); hsk_dnssec_sign_zsk(ns, HSK_DNS_SOA); } @@ -1011,16 +1040,36 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { hsk_dnssec_sign_zsk(ns, HSK_DNS_NS); // No DS proof: // This allows unbound to treat the zone as unsigned (and not bogus) - hsk_resource_to_empty(name, hsk_type_map_ns, sizeof(hsk_type_map_ns), ns); + hsk_resource_to_nsec( + name, + hsk_type_map_ns, + sizeof(hsk_type_map_ns), + ns + ); hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); } else { hsk_dnssec_sign_zsk(ns, HSK_DNS_DS); } } else { - // Needs SOA. - // Empty proof: - hsk_resource_to_empty(name, NULL, 0, ns); + // Domain has no NS + // We can prove there is a TXT or empty and sign NSEC + if (hsk_resource_has(rs, HSK_TEXT)) { + hsk_resource_to_nsec( + tld, + hsk_type_map_txt, + sizeof(hsk_type_map_txt), + ns + ); + } else { + hsk_resource_to_nsec( + tld, + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + ns + ); + } hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + // Needs SOA. hsk_resource_root_to_soa(ns); hsk_dnssec_sign_zsk(ns, HSK_DNS_SOA); } diff --git a/src/resource.h b/src/resource.h index 506ef2ff..e758e6cb 100644 --- a/src/resource.h +++ b/src/resource.h @@ -126,7 +126,7 @@ bool hsk_resource_is_ptr(const char *name); bool -hsk_resource_to_empty( +hsk_resource_to_nsec( const char *name, const uint8_t *type_map, size_t type_map_len, diff --git a/test/data/resource_vectors.h b/test/data/resource_vectors.h new file mode 100644 index 00000000..27f8a698 --- /dev/null +++ b/test/data/resource_vectors.h @@ -0,0 +1,337 @@ +#include "resource.h" + +/* + * Types + */ + +typedef struct type_vector { + uint16_t type; + char *type_string; + uint8_t an_size; + uint8_t ns_size; + uint8_t ar_size; + bool nsec; +} type_vector_t; + +typedef struct resource_vector { + char *name; + uint8_t data[255]; + uint8_t data_len; + type_vector_t type_vectors[4]; + // Expected in NSEC records + size_t type_map_len; + const uint8_t *type_map; +} resource_vector_t; + +/* + * Vectors + */ + +static const resource_vector_t resource_vectors[] = { + // { + // "records": [ + // { + // "type": "SYNTH4", + // "address": "50.60.70.80" + // } + // ] + // } + { + "test-synth4.", + { + 0x00, 0x04, 0x32, 0x3c, 0x46, 0x50 + }, + 6, + { + {HSK_DNS_DS, "DS", 0, 4, 1, true}, + {HSK_DNS_NS, "NS", 0, 2, 1, false}, + {HSK_DNS_TXT, "TXT", 0, 4, 1, true}, + {HSK_DNS_A, "A", 0, 4, 1, true} + }, + sizeof(hsk_type_map_ns), + hsk_type_map_ns + }, + + // { + // "records": [ + // { + // "type": "SYNTH6", + // "address": "8888:7777:6666:5555:4444:3333:2222:1111" + // } + // ] + // } + { + "test-synth6.", + { + 0x00, 0x05, 0x88, 0x88, 0x77, 0x77, 0x66, 0x66, 0x55, 0x55, 0x44, 0x44, + 0x33, 0x33, 0x22, 0x22, 0x11, 0x11 + }, + 18, + { + {HSK_DNS_DS, "DS", 0, 4, 1, true}, + {HSK_DNS_NS, "NS", 0, 2, 1, false}, + {HSK_DNS_TXT, "TXT", 0, 4, 1, true}, + {HSK_DNS_A, "A", 0, 4, 1, true} + }, + sizeof(hsk_type_map_ns), + hsk_type_map_ns + }, + + // { + // "records": [ + // { + // "type": "GLUE4", + // "ns": "ns2.hns.", + // "address": "10.20.30.40" + // } + // ] + // } + { + "test-glue4.", + { + 0x00, 0x02, 0x03, 0x6e, 0x73, 0x32, 0x03, 0x68, 0x6e, 0x73, 0x00, 0x0a, + 0x14, 0x1e, 0x28 + }, + 15, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true}, + {HSK_DNS_NS, "NS", 0, 2, 0, false}, + {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, + {HSK_DNS_A, "A", 0, 4, 0, true} + }, + sizeof(hsk_type_map_ns), + hsk_type_map_ns + }, + + // { + // "records": [ + // { + // "type": "GLUE4", + // "ns": "ns2.test-glue4-glue.", + // "address": "10.20.30.40" + // } + // ] + // } + { + "test-glue4-glue.", + { + 0x00, 0x02, 0x03, 0x6e, 0x73, 0x32, 0x0f, 0x74, 0x65, 0x73, 0x74, 0x2d, + 0x67, 0x6c, 0x75, 0x65, 0x34, 0x2d, 0x67, 0x6c, 0x75, 0x65, 0x00, 0x0a, + 0x14, 0x1e, 0x28 + }, + 27, + { + {HSK_DNS_DS, "DS", 0, 4, 1, true}, + {HSK_DNS_NS, "NS", 0, 2, 1, false}, + {HSK_DNS_TXT, "TXT", 0, 4, 1, true}, + {HSK_DNS_A, "A", 0, 4, 1, true} + }, + sizeof(hsk_type_map_ns), + hsk_type_map_ns + }, + + // { + // "records": [ + // { + // "type": "GLUE6", + // "ns": "ns2.hns.", + // "address": "1111:2222:3333:4444:5555:6666:7777:8888" + // } + // ] + // } + { + "test-glue6.", + { + 0x00, 0x03, 0x03, 0x6e, 0x73, 0x32, 0x03, 0x68, 0x6e, 0x73, 0x00, 0x11, + 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, + 0x77, 0x88, 0x88 + }, + 27, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true}, + {HSK_DNS_NS, "NS", 0, 2, 0, false}, + {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, + {HSK_DNS_A, "A", 0, 4, 0, true} + }, + sizeof(hsk_type_map_ns), + hsk_type_map_ns + }, + + // { + // "records": [ + // { + // "type": "GLUE6", + // "ns": "ns2.test-glue6-glue.", + // "address": "1111:2222:3333:4444:5555:6666:7777:8888" + // } + // ] + // } + { + "test-glue6-glue.", + { + 0x00, 0x03, 0x03, 0x6e, 0x73, 0x32, 0x0f, 0x74, 0x65, 0x73, 0x74, 0x2d, + 0x67, 0x6c, 0x75, 0x65, 0x36, 0x2d, 0x67, 0x6c, 0x75, 0x65, 0x00, 0x11, + 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, + 0x77, 0x88, 0x88 + }, + 39, + { + {HSK_DNS_DS, "DS", 0, 4, 1, true}, + {HSK_DNS_NS, "NS", 0, 2, 1, false}, + {HSK_DNS_TXT, "TXT", 0, 4, 1, true}, + {HSK_DNS_A, "A", 0, 4, 1, true} + }, + sizeof(hsk_type_map_ns), + hsk_type_map_ns + }, + + // { + // "records": [ + // { + // "type": "NS", + // "ns": "ns1.hns." + // } + // ] + // } + { + "test-ns.", + { + 0x00, 0x01, 0x03, 0x6e, 0x73, 0x31, 0x03, 0x68, 0x6e, 0x73, 0x00 + }, + 11, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true}, + {HSK_DNS_NS, "NS", 0, 2, 0, false}, + {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, + {HSK_DNS_A, "A", 0, 4, 0, true} + }, + sizeof(hsk_type_map_ns), + hsk_type_map_ns + }, + + // { + // "records": [ + // { + // "type": "NS", + // "ns": "ns1.test-ns-glue." + // } + // ] + // } + { + "test-ns-glue.", + { + 0x00, 0x01, 0x03, 0x6e, 0x73, 0x31, 0x0c, 0x74, 0x65, 0x73, 0x74, 0x2d, + 0x6e, 0x73, 0x2d, 0x67, 0x6c, 0x75, 0x65, 0x00 + }, + 20, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true}, + {HSK_DNS_NS, "NS", 0, 2, 0, false}, + {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, + {HSK_DNS_A, "A", 0, 4, 0, true} + }, + sizeof(hsk_type_map_ns), + hsk_type_map_ns + }, + + // { + // "records": [ + // { + // "type": "DS", + // "keyTag": 57355, + // "algorithm": 8, + // "digestType": 2, + // "digest": "95a57c3bab7849dbcddf7c72ada71a88146b141110318ca5be672057e865c3e2" + // } + // ] + // } + { + "test-ds.", + { + 0x00, 0x00, 0xe0, 0x0b, 0x08, 0x02, 0x20, 0x95, 0xa5, 0x7c, 0x3b, 0xab, + 0x78, 0x49, 0xdb, 0xcd, 0xdf, 0x7c, 0x72, 0xad, 0xa7, 0x1a, 0x88, 0x14, + 0x6b, 0x14, 0x11, 0x10, 0x31, 0x8c, 0xa5, 0xbe, 0x67, 0x20, 0x57, 0xe8, + 0x65, 0xc3, 0xe2 + }, + 39, + { + {HSK_DNS_DS, "DS", 2, 0, 0, false}, + {HSK_DNS_NS, "NS", 0, 4, 0, true}, + {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, + {HSK_DNS_A, "A", 0, 4, 0, true} + }, + sizeof(hsk_type_map_empty), + hsk_type_map_empty + }, + + // { + // "records": [ + // { + // "type": "TXT", + // "txt": [ + // "hello world", + // "how are you" + // ] + // } + // ] + // } + { + "test-txt.", + { + 0x00, 0x06, 0x02, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x0b, 0x68, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, + 0x79, 0x6f, 0x75 + }, + 27, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true}, + {HSK_DNS_NS, "NS", 0, 4, 0, true}, + {HSK_DNS_TXT, "TXT", 2, 0, 0, false}, + {HSK_DNS_A, "A", 0, 4, 0, true} + }, + sizeof(hsk_type_map_txt), + hsk_type_map_txt + }, + + // { + // "records": [ + // { + // "type": "DS", + // "keyTag": 57355, + // "algorithm": 8, + // "digestType": 2, + // "digest": "95a57c3bab7849dbcddf7c72ada71a88146b141110318ca5be672057e865c3e2" + // }, + // { + // "type": "GLUE6", + // "ns": "ns1.test-all.", + // "address": "4:8:15:16:23:42:108:815" + // }, + // { + // "type": "TXT", + // "txt": [":-)"] + // } + // ] + // } + { + "test-all.", + { + 0x00, 0x00, 0xe0, 0x0b, 0x08, 0x02, 0x20, 0x95, 0xa5, 0x7c, 0x3b, 0xab, + 0x78, 0x49, 0xdb, 0xcd, 0xdf, 0x7c, 0x72, 0xad, 0xa7, 0x1a, 0x88, 0x14, + 0x6b, 0x14, 0x11, 0x10, 0x31, 0x8c, 0xa5, 0xbe, 0x67, 0x20, 0x57, 0xe8, + 0x65, 0xc3, 0xe2, 0x03, 0x03, 0x6e, 0x73, 0x31, 0x08, 0x74, 0x65, 0x73, + 0x74, 0x2d, 0x61, 0x6c, 0x6c, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x15, + 0x00, 0x16, 0x00, 0x23, 0x00, 0x42, 0x01, 0x08, 0x08, 0x15, 0x06, 0x01, + 0x03, 0x3a, 0x2d, 0x29 + }, + 76, + { + {HSK_DNS_DS, "DS", 2, 0, 0, false, true}, + {HSK_DNS_NS, "NS", 0, 3, 1, false, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 1, false, false}, + {HSK_DNS_A, "A", 0, 3, 1, false, false} + }, + 0, + NULL + } +}; diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 76adc93d..d964f741 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -2,6 +2,11 @@ #include "base32.h" #include "resource.h" #include "resource.c" +#include "dns.h" +#include "dns.c" +#include "data/resource_vectors.h" + +#define ARRAY_SIZE(x) ((sizeof(x))/(sizeof(x[0]))) void print_array(uint8_t *arr, size_t size){ @@ -104,6 +109,66 @@ test_prev_name() { ) == 0); } +void +test_decode_resource() { + printf("test_decode_resource\n"); + + for (int i = 0; i < ARRAY_SIZE(resource_vectors); i++) { + resource_vector_t resource_vector = resource_vectors[i]; + + hsk_resource_t *res = NULL; + hsk_resource_decode( + resource_vector.data, + resource_vector.data_len, + &res + ); + + for (int t = 0; t < ARRAY_SIZE(resource_vector.type_vectors); t++) { + type_vector_t type_vector = resource_vector.type_vectors[t]; + + hsk_dns_msg_t *msg = NULL; + msg = hsk_resource_to_dns(res, resource_vector.name, type_vector.type); + + printf(" %s %s \n", resource_vector.name, type_vector.type_string); + assert(msg->an.size == type_vector.an_size); + assert(msg->ns.size == type_vector.ns_size); + assert(msg->ar.size == type_vector.ar_size); + + // Check `aa` bit + assert((bool)(msg->flags & HSK_DNS_AA) == type_vector.aa); + + // Sanity check: NSEC never appears in ANSWER or ADDITIONAL + for (int i = 0; i < msg->an.size; i++) { + hsk_dns_rr_t *rr = msg->an.items[i]; + assert(rr->type != HSK_DNS_NSEC); + } + for (int i = 0; i < msg->ar.size; i++) { + hsk_dns_rr_t *rr = msg->ar.items[i]; + assert(rr->type != HSK_DNS_NSEC); + } + + // Check NSEC in AUTHORITY when appropriate and verify type map + for (int i = 0; i < msg->ns.size; i++) { + hsk_dns_rr_t *rr = msg->ns.items[i]; + if (rr->type != HSK_DNS_NSEC) + continue; + + // NSEC is expected + assert(type_vector.nsec); + + // Type map is correct + hsk_dns_nsec_rd_t *rd = rr->rd; + assert(resource_vector.type_map_len == rd->type_map_len); + assert(memcmp( + resource_vector.type_map, + rd->type_map, + rd->type_map_len + ) == 0); + } + } + } +} + int main() { printf("Testing hnsd...\n"); @@ -111,6 +176,7 @@ main() { test_pointer_to_ip(); test_next_name(); test_prev_name(); + test_decode_resource(); printf("ok\n"); From 1025a33d8826772def2dd133faee8e6073872e3d Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 13:22:17 -0500 Subject: [PATCH 06/12] resource/ns: replace to_nx proofs with minimally-covering NSEC --- src/ns.c | 18 +++++++++------ src/resource.c | 61 +++++++++++++++++++++++++++++++++++++++++--------- src/resource.h | 3 ++- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/ns.c b/src/ns.c index 6c455da4..28e033b2 100644 --- a/src/ns.c +++ b/src/ns.c @@ -362,12 +362,12 @@ hsk_ns_onrecv( } if (!match) { - // Needs SOA. - // TODO: Make the reverse pointers TLDs. - // Empty proof: + char next[HSK_DNS_MAX_NAME] = "\\000."; + strcat(next, req->name); if (family == HSK_DNS_A) { hsk_resource_to_nsec( req->name, + next, hsk_type_map_a, sizeof(hsk_type_map_a), rrns @@ -375,6 +375,7 @@ hsk_ns_onrecv( } else { hsk_resource_to_nsec( req->name, + next, hsk_type_map_aaaa, sizeof(hsk_type_map_aaaa), rrns @@ -435,7 +436,7 @@ hsk_ns_onrecv( || strcmp(req->tld, "onion") == 0 // Tor || strcmp(req->tld, "tor") == 0 // OnioNS || strcmp(req->tld, "zkey") == 0) { // GNS - msg = hsk_resource_to_nx(); + msg = hsk_resource_to_nx(req->tld); } else { req->ns = (void *)ns; @@ -528,9 +529,12 @@ hsk_ns_respond( // not possible for SPV nodes since they // can't arbitrarily iterate over the tree. // - // Instead, we give a phony proof, which - // makes the root zone look empty. - msg = hsk_resource_to_nx(); + // Instead, we give a minimally covering + // NSEC record based on rfc4470 + // https://tools.ietf.org/html/rfc4470 + + // Proving the name doesn't exist + msg = hsk_resource_to_nx(req->tld); if (!msg) hsk_ns_log(ns, "could not create nx response (%u)\n", req->id); diff --git a/src/resource.c b/src/resource.c index 1e72207c..32efa62a 100644 --- a/src/resource.c +++ b/src/resource.c @@ -868,6 +868,7 @@ hsk_resource_root_to_ds(hsk_dns_rrs_t *an) { bool hsk_resource_to_nsec( const char *name, + const char *next, const uint8_t *type_map, size_t type_map_len, hsk_dns_rrs_t *an @@ -883,8 +884,6 @@ hsk_resource_to_nsec( hsk_dns_nsec_rd_t *rd = rr->rd; - char next[HSK_DNS_MAX_NAME]; - next_name(name, next); strcpy(rd->next_domain, next); rd->type_map = NULL; @@ -961,6 +960,12 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { hsk_dns_rrs_t *ns = &msg->ns; // authority hsk_dns_rrs_t *ar = &msg->ar; // additional + // Even though the name here is a single label (the TLD) + // we use a larger buffer size of 255 (instead of 63) + // to allow escaped byte codes like /000 + char next[HSK_DNS_MAX_NAME]; + next_name(tld, next); + // Referral. if (labels > 1) { if (hsk_resource_has_ns(rs)) { @@ -972,6 +977,7 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { // Root doesn't sign NS for anything other than "." hsk_resource_to_nsec( tld, + next, hsk_type_map_ns, sizeof(hsk_type_map_ns), ns @@ -988,6 +994,7 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { if (hsk_resource_has(rs, HSK_TEXT)) { hsk_resource_to_nsec( tld, + next, hsk_type_map_txt, sizeof(hsk_type_map_txt), ns @@ -995,6 +1002,7 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { } else { hsk_resource_to_nsec( tld, + next, hsk_type_map_empty, sizeof(hsk_type_map_empty), ns @@ -1041,7 +1049,8 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { // No DS proof: // This allows unbound to treat the zone as unsigned (and not bogus) hsk_resource_to_nsec( - name, + tld, + next, hsk_type_map_ns, sizeof(hsk_type_map_ns), ns @@ -1056,6 +1065,7 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { if (hsk_resource_has(rs, HSK_TEXT)) { hsk_resource_to_nsec( tld, + next, hsk_type_map_txt, sizeof(hsk_type_map_txt), ns @@ -1063,6 +1073,7 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { } else { hsk_resource_to_nsec( tld, + next, hsk_type_map_empty, sizeof(hsk_type_map_empty), ns @@ -1148,7 +1159,7 @@ hsk_resource_root(uint16_t type, const hsk_addr_t *addr) { } hsk_dns_msg_t * -hsk_resource_to_nx(void) { +hsk_resource_to_nx(const char *tld) { hsk_dns_msg_t *msg = hsk_dns_msg_alloc(); if (!msg) @@ -1159,15 +1170,43 @@ hsk_resource_to_nx(void) { hsk_dns_rrs_t *ns = &msg->ns; - // NX Proof: - // Just make it look like an - // empty zone for the NX proof. - // It seems to fool unbound without - // breaking anything. - hsk_resource_root_to_nsec(ns); - hsk_resource_root_to_nsec(ns); + // Prove the wildcard doesn't exist + hsk_resource_to_nsec( + "!.", + "+.", + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + ns + ); + // Sign RR set with name `!.` + hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + + // Pop the NSEC and RRSIG out of the RR set... + hsk_dns_rr_t *rr1 = hsk_dns_rrs_pop(ns); + hsk_dns_rr_t *rr2 = hsk_dns_rrs_pop(ns); + + // Prove the name doesn't exist. + // Even though the name here is a single label (the TLD) + // we use a larger buffer size of 255 (instead of 63) + // to allow escaped byte codes like /000 + char next[HSK_DNS_MAX_NAME]; + char prev[HSK_DNS_MAX_NAME]; + next_name(tld, next); + prev_name(tld, prev); + hsk_resource_to_nsec( + prev, + next, + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + ns + ); + // Sign RR set with name `prev` hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + // ...now push the first two RRs back in + hsk_dns_rrs_push(ns, rr2); + hsk_dns_rrs_push(ns, rr1); + hsk_resource_root_to_soa(ns); hsk_dnssec_sign_zsk(ns, HSK_DNS_SOA); diff --git a/src/resource.h b/src/resource.h index e758e6cb..639b0c0f 100644 --- a/src/resource.h +++ b/src/resource.h @@ -114,7 +114,7 @@ hsk_dns_msg_t * hsk_resource_root(uint16_t type, const hsk_addr_t *addr); hsk_dns_msg_t * -hsk_resource_to_nx(void); +hsk_resource_to_nx(const char *tld); hsk_dns_msg_t * hsk_resource_to_servfail(void); @@ -128,6 +128,7 @@ hsk_resource_is_ptr(const char *name); bool hsk_resource_to_nsec( const char *name, + const char *next, const uint8_t *type_map, size_t type_map_len, hsk_dns_rrs_t *an From 0cbbc8a59a05edbdd97f37e714f655c52357c8bd Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 14:50:51 -0500 Subject: [PATCH 07/12] ns: remove DS record from root zone apex --- src/resource.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/resource.c b/src/resource.c index 32efa62a..b0356a52 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1141,10 +1141,6 @@ hsk_resource_root(uint16_t type, const hsk_addr_t *addr) { hsk_resource_root_to_dnskey(an); hsk_dnssec_sign_ksk(an, HSK_DNS_DNSKEY); break; - case HSK_DNS_DS: - hsk_resource_root_to_ds(an); - hsk_dnssec_sign_zsk(an, HSK_DNS_DS); - break; default: // Empty Proof: // Show all the types that we signed. From 61296c8609ef0746a1ae2b5022599a5549de1672 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 15:01:48 -0500 Subject: [PATCH 08/12] resource: nsec proofs for root zone --- src/resource.c | 42 ++++++++---------------------------------- src/resource.h | 2 +- 2 files changed, 9 insertions(+), 35 deletions(-) diff --git a/src/resource.c b/src/resource.c index b0356a52..6c88c37d 100644 --- a/src/resource.c +++ b/src/resource.c @@ -908,37 +908,6 @@ hsk_resource_to_nsec( return true; } -static bool -hsk_resource_root_to_nsec(hsk_dns_rrs_t *an) { - hsk_dns_rr_t *rr = hsk_dns_rr_create(HSK_DNS_NSEC); - - if (!rr) - return false; - - uint8_t *bitmap = malloc(sizeof(hsk_type_map)); - - if (!bitmap) { - hsk_dns_rr_free(rr); - return false; - } - - memcpy(bitmap, &hsk_type_map[0], sizeof(hsk_type_map)); - - rr->ttl = 86400; - - hsk_dns_rr_set_name(rr, "."); - - hsk_dns_nsec_rd_t *rd = rr->rd; - - strcpy(rd->next_domain, "."); - rd->type_map = bitmap; - rd->type_map_len = sizeof(hsk_type_map); - - hsk_dns_rrs_push(an, rr); - - return true; -} - hsk_dns_msg_t * hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { assert(hsk_dns_name_is_fqdn(name)); @@ -1142,9 +1111,14 @@ hsk_resource_root(uint16_t type, const hsk_addr_t *addr) { hsk_dnssec_sign_ksk(an, HSK_DNS_DNSKEY); break; default: - // Empty Proof: - // Show all the types that we signed. - hsk_resource_root_to_nsec(ns); + // Minimally covering NSEC proof: + hsk_resource_to_nsec( + ".", + "\\000.", + hsk_type_map_root, + sizeof(hsk_type_map_root), + ns + ); hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); hsk_resource_root_to_soa(ns); hsk_dnssec_sign_zsk(ns, HSK_DNS_SOA); diff --git a/src/resource.h b/src/resource.h index 639b0c0f..ead52107 100644 --- a/src/resource.h +++ b/src/resource.h @@ -27,7 +27,7 @@ static const uint8_t hsk_type_map_aaaa[] = { }; // NS SOA RRSIG NSEC DNSKEY -static const uint8_t hsk_type_map[] = { +static const uint8_t hsk_type_map_root[] = { 0x00, 0x07, 0x22, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80 }; From b53eba355295b94a9ac3f648ccd35585e520d162 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 15:07:04 -0500 Subject: [PATCH 09/12] resource: use default TTL for NSEC --- src/resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/resource.c b/src/resource.c index 6c88c37d..31ba48fc 100644 --- a/src/resource.c +++ b/src/resource.c @@ -756,7 +756,7 @@ hsk_resource_root_to_soa(hsk_dns_rrs_t *an) { rd->refresh = 1800; rd->retry = 900; rd->expire = 604800; - rd->minttl = 86400; + rd->minttl = HSK_DEFAULT_TTL; hsk_dns_rrs_push(an, rr); @@ -878,7 +878,7 @@ hsk_resource_to_nsec( if (!rr) return false; - rr->ttl = 86400; + rr->ttl = HSK_DEFAULT_TTL; hsk_dns_rr_set_name(rr, name); From de6ced8f2a165a7f31832dea60ac792a0a3dcea3 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 23 Nov 2021 08:10:48 -0500 Subject: [PATCH 10/12] resource: fix aa flag when querying root --- src/resource.c | 40 +++++++++++------- test/data/resource_vectors.h | 81 ++++++++++++++++++------------------ 2 files changed, 65 insertions(+), 56 deletions(-) diff --git a/src/resource.c b/src/resource.c index 31ba48fc..edb52ab9 100644 --- a/src/resource.c +++ b/src/resource.c @@ -977,6 +977,9 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { ns ); } + + msg->flags |= HSK_DNS_AA; + hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); // Needs SOA. hsk_resource_root_to_soa(ns); @@ -989,32 +992,27 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { // Record types actually on-chain for HNS TLDs. switch (type) { case HSK_DNS_DS: + msg->flags |= HSK_DNS_AA; hsk_resource_to_ds(rs, name, an); hsk_dnssec_sign_zsk(an, HSK_DNS_DS); break; - case HSK_DNS_NS: - // Includes SYNTH and GLUE records. - hsk_resource_to_ns(rs, name, ns); - hsk_resource_to_glue(rs, name, ar); - hsk_dnssec_sign_zsk(ns, HSK_DNS_NS); - break; case HSK_DNS_TXT: - hsk_resource_to_txt(rs, name, an); - hsk_dnssec_sign_zsk(an, HSK_DNS_TXT); + if (!hsk_resource_has_ns(rs)) { + msg->flags |= HSK_DNS_AA; + hsk_resource_to_txt(rs, name, an); + hsk_dnssec_sign_zsk(an, HSK_DNS_TXT); + } break; } - if (an->size > 0) - msg->flags |= HSK_DNS_AA; - // Attempt to force a referral if we don't have an answer. if (an->size == 0 && ns->size == 0) { - if (hsk_resource_has_ns(rs)) { + // No referrals for DS or without NS to refer to! + if (hsk_resource_has_ns(rs) && type != HSK_DNS_DS) { hsk_resource_to_ns(rs, name, ns); hsk_resource_to_ds(rs, name, ns); hsk_resource_to_glue(rs, name, ar); if (!hsk_resource_has(rs, HSK_DS)) { - hsk_dnssec_sign_zsk(ns, HSK_DNS_NS); // No DS proof: // This allows unbound to treat the zone as unsigned (and not bogus) hsk_resource_to_nsec( @@ -1029,9 +1027,17 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { hsk_dnssec_sign_zsk(ns, HSK_DNS_DS); } } else { - // Domain has no NS - // We can prove there is a TXT or empty and sign NSEC - if (hsk_resource_has(rs, HSK_TEXT)) { + if (hsk_resource_has_ns(rs)) { + // If NS is present, prove it + hsk_resource_to_nsec( + tld, + next, + hsk_type_map_ns, + sizeof(hsk_type_map_ns), + ns + ); + } else if (hsk_resource_has(rs, HSK_TEXT)) { + // No NS means we can prove TXT if applicable hsk_resource_to_nsec( tld, next, @@ -1040,6 +1046,7 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { ns ); } else { + // Otherwise, we prove there is nothing hsk_resource_to_nsec( tld, next, @@ -1050,6 +1057,7 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { } hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); // Needs SOA. + msg->flags |= HSK_DNS_AA; hsk_resource_root_to_soa(ns); hsk_dnssec_sign_zsk(ns, HSK_DNS_SOA); } diff --git a/test/data/resource_vectors.h b/test/data/resource_vectors.h index 27f8a698..53f4433e 100644 --- a/test/data/resource_vectors.h +++ b/test/data/resource_vectors.h @@ -11,6 +11,7 @@ typedef struct type_vector { uint8_t ns_size; uint8_t ar_size; bool nsec; + bool aa; } type_vector_t; typedef struct resource_vector { @@ -43,10 +44,10 @@ static const resource_vector_t resource_vectors[] = { }, 6, { - {HSK_DNS_DS, "DS", 0, 4, 1, true}, - {HSK_DNS_NS, "NS", 0, 2, 1, false}, - {HSK_DNS_TXT, "TXT", 0, 4, 1, true}, - {HSK_DNS_A, "A", 0, 4, 1, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 3, 1, true, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 1, true, false}, + {HSK_DNS_A, "A", 0, 3, 1, true, false} }, sizeof(hsk_type_map_ns), hsk_type_map_ns @@ -68,10 +69,10 @@ static const resource_vector_t resource_vectors[] = { }, 18, { - {HSK_DNS_DS, "DS", 0, 4, 1, true}, - {HSK_DNS_NS, "NS", 0, 2, 1, false}, - {HSK_DNS_TXT, "TXT", 0, 4, 1, true}, - {HSK_DNS_A, "A", 0, 4, 1, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 3, 1, true, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 1, true, false}, + {HSK_DNS_A, "A", 0, 3, 1, true, false} }, sizeof(hsk_type_map_ns), hsk_type_map_ns @@ -94,10 +95,10 @@ static const resource_vector_t resource_vectors[] = { }, 15, { - {HSK_DNS_DS, "DS", 0, 4, 0, true}, - {HSK_DNS_NS, "NS", 0, 2, 0, false}, - {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, - {HSK_DNS_A, "A", 0, 4, 0, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 3, 0, true, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 0, true, false}, + {HSK_DNS_A, "A", 0, 3, 0, true, false} }, sizeof(hsk_type_map_ns), hsk_type_map_ns @@ -121,10 +122,10 @@ static const resource_vector_t resource_vectors[] = { }, 27, { - {HSK_DNS_DS, "DS", 0, 4, 1, true}, - {HSK_DNS_NS, "NS", 0, 2, 1, false}, - {HSK_DNS_TXT, "TXT", 0, 4, 1, true}, - {HSK_DNS_A, "A", 0, 4, 1, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 3, 1, true, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 1, true, false}, + {HSK_DNS_A, "A", 0, 3, 1, true, false} }, sizeof(hsk_type_map_ns), hsk_type_map_ns @@ -148,10 +149,10 @@ static const resource_vector_t resource_vectors[] = { }, 27, { - {HSK_DNS_DS, "DS", 0, 4, 0, true}, - {HSK_DNS_NS, "NS", 0, 2, 0, false}, - {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, - {HSK_DNS_A, "A", 0, 4, 0, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 3, 0, true, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 0, true, false}, + {HSK_DNS_A, "A", 0, 3, 0, true, false} }, sizeof(hsk_type_map_ns), hsk_type_map_ns @@ -176,10 +177,10 @@ static const resource_vector_t resource_vectors[] = { }, 39, { - {HSK_DNS_DS, "DS", 0, 4, 1, true}, - {HSK_DNS_NS, "NS", 0, 2, 1, false}, - {HSK_DNS_TXT, "TXT", 0, 4, 1, true}, - {HSK_DNS_A, "A", 0, 4, 1, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 3, 1, true, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 1, true, false}, + {HSK_DNS_A, "A", 0, 3, 1, true, false} }, sizeof(hsk_type_map_ns), hsk_type_map_ns @@ -200,10 +201,10 @@ static const resource_vector_t resource_vectors[] = { }, 11, { - {HSK_DNS_DS, "DS", 0, 4, 0, true}, - {HSK_DNS_NS, "NS", 0, 2, 0, false}, - {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, - {HSK_DNS_A, "A", 0, 4, 0, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 3, 0, true, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 0, true, false}, + {HSK_DNS_A, "A", 0, 3, 0, true, false} }, sizeof(hsk_type_map_ns), hsk_type_map_ns @@ -225,10 +226,10 @@ static const resource_vector_t resource_vectors[] = { }, 20, { - {HSK_DNS_DS, "DS", 0, 4, 0, true}, - {HSK_DNS_NS, "NS", 0, 2, 0, false}, - {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, - {HSK_DNS_A, "A", 0, 4, 0, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 3, 0, true, false}, + {HSK_DNS_TXT, "TXT", 0, 3, 0, true, false}, + {HSK_DNS_A, "A", 0, 3, 0, true, false} }, sizeof(hsk_type_map_ns), hsk_type_map_ns @@ -255,10 +256,10 @@ static const resource_vector_t resource_vectors[] = { }, 39, { - {HSK_DNS_DS, "DS", 2, 0, 0, false}, - {HSK_DNS_NS, "NS", 0, 4, 0, true}, - {HSK_DNS_TXT, "TXT", 0, 4, 0, true}, - {HSK_DNS_A, "A", 0, 4, 0, true} + {HSK_DNS_DS, "DS", 2, 0, 0, false, true}, + {HSK_DNS_NS, "NS", 0, 4, 0, true, true}, + {HSK_DNS_TXT, "TXT", 0, 4, 0, true, true}, + {HSK_DNS_A, "A", 0, 4, 0, true, true} }, sizeof(hsk_type_map_empty), hsk_type_map_empty @@ -284,10 +285,10 @@ static const resource_vector_t resource_vectors[] = { }, 27, { - {HSK_DNS_DS, "DS", 0, 4, 0, true}, - {HSK_DNS_NS, "NS", 0, 4, 0, true}, - {HSK_DNS_TXT, "TXT", 2, 0, 0, false}, - {HSK_DNS_A, "A", 0, 4, 0, true} + {HSK_DNS_DS, "DS", 0, 4, 0, true, true}, + {HSK_DNS_NS, "NS", 0, 4, 0, true, true}, + {HSK_DNS_TXT, "TXT", 2, 0, 0, false, true}, + {HSK_DNS_A, "A", 0, 4, 0, true, true} }, sizeof(hsk_type_map_txt), hsk_type_map_txt From 7d2c7e1b8d8608bcaa3e04a83f6e78f4bd110b8a Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 13 Dec 2021 16:15:10 -0500 Subject: [PATCH 11/12] ns/resource: fix NSEC handling for _synth --- src/ns.c | 16 +++++++++++++--- src/resource.h | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/ns.c b/src/ns.c index 28e033b2..e6bf91a7 100644 --- a/src/ns.c +++ b/src/ns.c @@ -323,7 +323,9 @@ hsk_ns_onrecv( // The synth name then resolves to an A/AAAA record that is derived // by decoding the name itself (it does not have to be looked up). bool should_cache = true; - if (strcmp(req->tld, "_synth") == 0 && req->labels <= 2) { + if (strcmp(req->tld, "_synth") == 0 && + req->labels <= 2 && + req->name[0] == '_') { msg = hsk_dns_msg_alloc(); should_cache = false; @@ -332,7 +334,6 @@ hsk_ns_onrecv( hsk_dns_rrs_t *an = &msg->an; hsk_dns_rrs_t *rrns = &msg->ns; - hsk_dns_rrs_t *ar = &msg->ar; uint8_t ip[16]; uint16_t family; @@ -344,6 +345,15 @@ hsk_ns_onrecv( // so recursive asks again with complete synth record. hsk_resource_root_to_soa(rrns); hsk_dnssec_sign_zsk(rrns, HSK_DNS_SOA); + // Empty non-terminal proof: + hsk_resource_to_nsec( + "_synth.", + "\\000._synth.", + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + rrns + ); + hsk_dnssec_sign_zsk(rrns, HSK_DNS_NSEC); } if (pointer_to_ip(synth, ip, &family)) { @@ -409,7 +419,7 @@ hsk_ns_onrecv( hsk_dns_rrs_push(an, rr); - hsk_dnssec_sign_zsk(ar, rrtype); + hsk_dnssec_sign_zsk(an, rrtype); } } diff --git a/src/resource.h b/src/resource.h index ead52107..96ef7140 100644 --- a/src/resource.h +++ b/src/resource.h @@ -23,7 +23,7 @@ static const uint8_t hsk_type_map_a[] = { // AAAA RRSIG NSEC static const uint8_t hsk_type_map_aaaa[] = { - 0x00, 0x06, 0x00, 0x00, 0x00, 0x80, 0x00, 0x03 + 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x03 }; // NS SOA RRSIG NSEC DNSKEY From 5e014871b6eb59c0540ca876993a860dffac9147 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 11 Jan 2022 11:53:57 -0500 Subject: [PATCH 12/12] rs: disable checking when requested --- src/rs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/rs.c b/src/rs.c index 4f183dc3..d91d7e08 100644 --- a/src/rs.c +++ b/src/rs.c @@ -439,7 +439,11 @@ hsk_rs_respond( if (result->bogus) { hsk_rs_log(ns, " why_bogus: %s\n", result->why_bogus); - goto fail; + + if (req->cd) + hsk_rs_log(ns, " (checking disabled)\n"); + else + goto fail; } uint8_t *data = result->answer_packet;