From 012dd3d7cd63d998372ecd02783b2b4b6c8fe72d 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/21] 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 10d814219ee2d0aa8731f976db52de6c282e55af 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/21] 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 fdbb861881202b52eafaff78c2ca9caca13b51af Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Thu, 18 Nov 2021 11:01:05 -0500 Subject: [PATCH 03/21] 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 9f41ff0d3c2291405c0db522c55038f23b626aea Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Sat, 20 Nov 2021 14:38:42 -0500 Subject: [PATCH 04/21] dns: parse escaped byte codes in name serialize --- src/dns.c | 24 ++++++++- test/data/name_serialization_vectors.h | 70 ++++++++++++++++++++++++++ test/hnsd-test.c | 45 ++++++++++++++++- 3 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 test/data/name_serialization_vectors.h diff --git a/src/dns.c b/src/dns.c index f77fe8a6..7f6a46e4 100644 --- a/src/dns.c +++ b/src/dns.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "bio.h" #include "dns.h" @@ -2224,6 +2225,7 @@ hsk_dns_name_serialize( int size; int i; char *s; + int size_byte; for (s = (char *)name, i = 0; *s; s++, i++) { if (name[i] == '.') { @@ -2244,7 +2246,8 @@ hsk_dns_name_serialize( *len = off; return false; } - data[off] = size; + // Size will be written here once we know what size is + size_byte = off; } if (cmp) { @@ -2272,6 +2275,23 @@ hsk_dns_name_serialize( int j; for (j = begin; j < i; j++) { char ch = name[j]; + if (ch == '\\') { + // Read the next three digits and form a single byte + if (!isdigit(name[j + 1]) || + !isdigit(name[j + 2]) || + !isdigit(name[j + 3])) { + *len = off; + return false; + } + + uint16_t value = (name[++j] - 0x30) * 100; + value += (name[++j] - 0x30) * 10; + value += (name[++j] - 0x30); + + data[off++] = byte; + size -= 3; + continue; + } // 0xff -> NUL if (ch == -1) @@ -2283,6 +2303,8 @@ hsk_dns_name_serialize( data[off++] = ch; } + + data[size_byte] = size; } else { off += size; } diff --git a/test/data/name_serialization_vectors.h b/test/data/name_serialization_vectors.h new file mode 100644 index 00000000..4a2ba45e --- /dev/null +++ b/test/data/name_serialization_vectors.h @@ -0,0 +1,70 @@ +/* + * Types + */ + +typedef struct name_serializtion_vector { + char *name; + uint8_t expected_data[24]; + int expected_len; +} name_serializtion_vector_t; + + +/* + * Vectors + */ + +static const name_serializtion_vector_t name_serializtion_vectors[7] = { + { + "abcdef.", + { + 0x06, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00 + }, + 8 + }, + { + "abc.def.", + { + 0x03, 0x61, 0x62, 0x63, 0x03, 0x64, 0x65, 0x66, 0x00 + }, + 9 + }, + { + "abcdef\\000.", + { + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00, 0x00 + }, + 9 + }, + { + "abcdef\\255.", + { + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0xff, 0x00 + }, + 9, + true + }, + { + "abcdef\\256.", + { + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 + }, + 7, + false + }, + { + "abcdef\\LOL.", + { + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 + }, + 7, + false + }, + { + "abc\\032def.", + { + 0x07, 0x61, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00 + }, + 9, + true + } +}; diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 7ec7b071..3ffe7848 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -2,17 +2,30 @@ #include "base32.h" #include "resource.h" #include "resource.c" +#include "dns.h" +#include "dns.c" +#include "data/name_serialization_vectors.h" + +/** + * UTILITY + */ void print_array(uint8_t *arr, size_t size){ for (int i = 0; i < size; i++) { - printf("%x", arr[i]); + printf("%02x", arr[i]); } printf("\n"); } +/* + * TESTS + */ + void test_base32() { + printf("test_base32\n"); + const char *str = "5l6tm80"; const uint8_t expected[4] = {45, 77, 219, 32}; @@ -29,6 +42,8 @@ test_base32() { void test_pointer_to_ip() { + printf("test_pointer_to_ip\n"); + const char *str4 = "_5l6tm80._synth"; const uint8_t expected4[4] = {45, 77, 219, 32}; uint8_t ip4[4]; @@ -60,11 +75,39 @@ test_pointer_to_ip() { assert(family6 == HSK_DNS_AAAA); } +void +test_name_serialize() { + printf("test_name_serialize\n"); + + for (int i = 0; i < 7; i++) { + name_serializtion_vector_t name_serializtion_vector = name_serializtion_vectors[i]; + + uint8_t data[24] = {0}; + int len; + + hsk_dns_name_serialize( + name_serializtion_vector.name, + data, + &len, + NULL + ); + + printf(" %s\n", name_serializtion_vector.name); + assert(len == name_serializtion_vector.expected_len); + assert(memcmp(data, name_serializtion_vector.expected_data, len) == 0); + } +} + +/* + * TEST RUNNER + */ + int main() { printf("Testing hnsd...\n"); test_base32(); test_pointer_to_ip(); + test_name_serialize(); printf("ok\n"); From 608cfe3c6ec3d25690431163153d0dea67a54feb Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Sat, 20 Nov 2021 14:45:02 -0500 Subject: [PATCH 05/21] resource/ns: replace empty proofs with minimally-covering NSEC --- src/ns.c | 8 +- src/resource.c | 93 ++++++++++-- src/resource.h | 2 +- test/data/resource_vectors.h | 269 +++++++++++++++++++++++++++++++++++ test/hnsd-test.c | 30 ++++ 5 files changed, 385 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 15101c2d..ee6bd0e1 100644 --- a/src/resource.c +++ b/src/resource.c @@ -23,6 +23,9 @@ static void to_fqdn(char *name); +static void +next_name(const char *name, char *next); + /* * Resource serialization version 0 * Record types: read @@ -860,7 +863,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, @@ -877,7 +880,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; @@ -958,15 +964,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); } @@ -1005,16 +1037,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); } @@ -1155,6 +1207,23 @@ 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]++; + next[len] = '.'; + next[len + 1] = '\0'; + } +} + bool pointer_to_ip(const char *name, uint8_t *ip, uint16_t *family) { char label[HSK_DNS_MAX_LABEL + 1]; 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..1119db80 --- /dev/null +++ b/test/data/resource_vectors.h @@ -0,0 +1,269 @@ +/* + * Types + */ + +typedef struct type_vector { + uint16_t type; + char *type_string; + uint8_t an_size; + uint8_t ns_size; + uint8_t ar_size; +} type_vector_t; + +typedef struct resource_vector { + char *name; + uint8_t data[255]; + uint8_t data_len; + type_vector_t type_vectors[4]; +} resource_vector_t; + +/* + * Vectors + */ + +static const resource_vector_t resource_vectors[10] = { + // { + // "records": [ + // { + // "type": "SYNTH4", + // "address": "50.60.70.80" + // } + // ] + // } + { + "test-synth4.", + { + 0x00, 0x04, 0x32, 0x3c, 0x46, 0x50 + }, + 6, + { + {HSK_DNS_DS, "DS", 0, 4, 1}, + {HSK_DNS_NS, "NS", 0, 2, 1}, + {HSK_DNS_TXT, "TXT", 0, 4, 1}, + {HSK_DNS_A, "A", 0, 4, 1} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 2, 1}, + {HSK_DNS_TXT, "TXT", 0, 4, 1}, + {HSK_DNS_A, "A", 0, 4, 1} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 2, 0}, + {HSK_DNS_TXT, "TXT", 0, 4, 0}, + {HSK_DNS_A, "A", 0, 4, 0} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 2, 1}, + {HSK_DNS_TXT, "TXT", 0, 4, 1}, + {HSK_DNS_A, "A", 0, 4, 1} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 2, 0}, + {HSK_DNS_TXT, "TXT", 0, 4, 0}, + {HSK_DNS_A, "A", 0, 4, 0} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 2, 1}, + {HSK_DNS_TXT, "TXT", 0, 4, 1}, + {HSK_DNS_A, "A", 0, 4, 1} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 2, 0}, + {HSK_DNS_TXT, "TXT", 0, 4, 0}, + {HSK_DNS_A, "A", 0, 4, 0} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 2, 0}, + {HSK_DNS_TXT, "TXT", 0, 4, 0}, + {HSK_DNS_A, "A", 0, 4, 0} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 4, 0}, + {HSK_DNS_TXT, "TXT", 0, 4, 0}, + {HSK_DNS_A, "A", 0, 4, 0} + } + }, + + // { + // "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}, + {HSK_DNS_NS, "NS", 0, 4, 0}, + {HSK_DNS_TXT, "TXT", 2, 0, 0}, + {HSK_DNS_A, "A", 0, 4, 0} + } + } +}; diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 3ffe7848..1e872b91 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -5,6 +5,7 @@ #include "dns.h" #include "dns.c" #include "data/name_serialization_vectors.h" +#include "data/resource_vectors.h" /** * UTILITY @@ -98,6 +99,34 @@ test_name_serialize() { } } +void +test_decode_resource() { + printf("test_decode_resource\n"); + + for (int i = 0; i < 10; 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 < 4; 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); + } + } +} + /* * TEST RUNNER */ @@ -108,6 +137,7 @@ main() { test_base32(); test_pointer_to_ip(); test_name_serialize(); + test_decode_resource(); printf("ok\n"); From 87921cf1588a5d082e59e8499e60da516b4e87b7 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 10:08:14 -0500 Subject: [PATCH 06/21] resource: implement nsec previous name and test --- src/resource.c | 19 +++++++++++++++++-- test/hnsd-test.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/resource.c b/src/resource.c index ee6bd0e1..c6263723 100644 --- a/src/resource.c +++ b/src/resource.c @@ -26,6 +26,9 @@ 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 @@ -1219,8 +1222,20 @@ next_name(const char *name, char *next) { memcpy(&next[len], "\\000.", 6); } else { next[len - 1]++; - next[len] = '.'; - next[len + 1] = '\0'; + } +} + +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); } } diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 1e872b91..cdc55b2d 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -127,6 +127,50 @@ test_decode_resource() { } } +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); +} + /* * TEST RUNNER */ @@ -138,6 +182,8 @@ main() { test_pointer_to_ip(); test_name_serialize(); test_decode_resource(); + test_next_name(); + test_prev_name(); printf("ok\n"); From 71bce3087678e0cff0ee46e8b8f811a6f51c2e98 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 12:43:36 -0500 Subject: [PATCH 07/21] dns: send REFUSED is name contains special characters --- src/cache.c | 3 --- src/dns.c | 12 ++++++----- src/ns.c | 28 +++++++++++++++++++++++--- src/req.c | 13 ------------ src/resource.c | 12 +++++++++++ src/resource.h | 3 +++ test/data/name_serialization_vectors.h | 10 ++++++--- test/hnsd-test.c | 7 ++++--- 8 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/cache.c b/src/cache.c index 88a57521..fa2e0b16 100644 --- a/src/cache.c +++ b/src/cache.c @@ -262,9 +262,6 @@ hsk_cache_key_set(hsk_cache_key_t *ck, const char *name, uint16_t type) { if (!hsk_dns_name_verify(name)) return false; - if (hsk_dns_name_dirty(name)) - return false; - int labels = hsk_dns_label_count(name); bool ref = false; diff --git a/src/dns.c b/src/dns.c index 7f6a46e4..9b55bfcb 100644 --- a/src/dns.c +++ b/src/dns.c @@ -726,9 +726,6 @@ hsk_dns_rr_set_name(hsk_dns_rr_t *rr, const char *name) { if (!hsk_dns_name_verify(name)) return false; - if (hsk_dns_name_dirty(name)) - return false; - if (hsk_dns_name_is_fqdn(name)) strcpy(rr->name, name); else @@ -2225,7 +2222,7 @@ hsk_dns_name_serialize( int size; int i; char *s; - int size_byte; + int size_byte = 0; for (s = (char *)name, i = 0; *s; s++, i++) { if (name[i] == '.') { @@ -2288,7 +2285,12 @@ hsk_dns_name_serialize( value += (name[++j] - 0x30) * 10; value += (name[++j] - 0x30); - data[off++] = byte; + if (value > 0xff) { + *len = off; + return false; + } + + data[off++] = value; size -= 3; continue; } diff --git a/src/ns.c b/src/ns.c index 6c455da4..bb121685 100644 --- a/src/ns.c +++ b/src/ns.c @@ -287,6 +287,10 @@ hsk_ns_onrecv( const struct sockaddr *addr, uint32_t flags ) { + uint8_t *wire = NULL; + size_t wire_len = 0; + hsk_dns_msg_t *msg = NULL; + hsk_dns_req_t *req = hsk_dns_req_create(data, data_len, addr); if (!req) { @@ -296,9 +300,27 @@ hsk_ns_onrecv( hsk_dns_req_print(req, "ns: "); - uint8_t *wire = NULL; - size_t wire_len = 0; - hsk_dns_msg_t *msg = NULL; + // Send REFUSED if name is dirty + // (contains escaped byte codes or special characters) + if (hsk_dns_name_dirty(req->tld)) { + msg = hsk_resource_to_refused(); + + if (!msg) { + hsk_ns_log(ns, "failed creating refused\n"); + goto fail; + } + + if (!hsk_dns_msg_finalize(&msg, req, ns->ec, ns->key, &wire, &wire_len)) { + hsk_ns_log(ns, "could not reply\n"); + goto done; + } + + hsk_ns_log(ns, "refusing query for msg (%u): %u\n", req->id, wire_len); + + hsk_ns_send(ns, wire, wire_len, addr, true); + + goto done; + } // Hit cache first. msg = hsk_cache_get(&ns->cache, req); diff --git a/src/req.c b/src/req.c index 062aaa06..cb2f7e8e 100644 --- a/src/req.c +++ b/src/req.c @@ -83,22 +83,9 @@ hsk_dns_req_create( // Grab the first question. hsk_dns_qs_t *qs = msg->qd.items[0]; -#if 0 - if (qs->class != HSK_DNS_IN) - goto fail; - - // Don't allow dirty names. - if (hsk_dns_name_dirty(qs->name)) - goto fail; -#endif - // Check for a TLD. hsk_dns_label_get(qs->name, -1, req->tld); - // Don't allow dirty TLDs. - if (hsk_dns_name_dirty(req->tld)) - goto fail; - // Lowercase. hsk_to_lower(req->tld); diff --git a/src/resource.c b/src/resource.c index c6263723..1b59ea8c 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1186,6 +1186,18 @@ hsk_resource_to_servfail(void) { return msg; } +hsk_dns_msg_t * +hsk_resource_to_refused(void) { + hsk_dns_msg_t *msg = hsk_dns_msg_alloc(); + + if (!msg) + return NULL; + + msg->code = HSK_DNS_REFUSED; + + return msg; +} + hsk_dns_msg_t * hsk_resource_to_notimp(void) { hsk_dns_msg_t *msg = hsk_dns_msg_alloc(); diff --git a/src/resource.h b/src/resource.h index e758e6cb..f11e34b4 100644 --- a/src/resource.h +++ b/src/resource.h @@ -119,6 +119,9 @@ hsk_resource_to_nx(void); hsk_dns_msg_t * hsk_resource_to_servfail(void); +hsk_dns_msg_t * +hsk_resource_to_refused(void); + hsk_dns_msg_t * hsk_resource_to_notimp(void); diff --git a/test/data/name_serialization_vectors.h b/test/data/name_serialization_vectors.h index 4a2ba45e..860aaddd 100644 --- a/test/data/name_serialization_vectors.h +++ b/test/data/name_serialization_vectors.h @@ -6,6 +6,7 @@ typedef struct name_serializtion_vector { char *name; uint8_t expected_data[24]; int expected_len; + bool success; } name_serializtion_vector_t; @@ -19,21 +20,24 @@ static const name_serializtion_vector_t name_serializtion_vectors[7] = { { 0x06, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00 }, - 8 + 8, + true }, { "abc.def.", { 0x03, 0x61, 0x62, 0x63, 0x03, 0x64, 0x65, 0x66, 0x00 }, - 9 + 9, + true }, { "abcdef\\000.", { 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00, 0x00 }, - 9 + 9, + true }, { "abcdef\\255.", diff --git a/test/hnsd-test.c b/test/hnsd-test.c index cdc55b2d..150885a3 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -86,14 +86,15 @@ test_name_serialize() { uint8_t data[24] = {0}; int len; - hsk_dns_name_serialize( + printf(" %s\n", name_serializtion_vector.name); + + bool success = hsk_dns_name_serialize( name_serializtion_vector.name, data, &len, NULL ); - - printf(" %s\n", name_serializtion_vector.name); + assert(name_serializtion_vector.success == success); assert(len == name_serializtion_vector.expected_len); assert(memcmp(data, name_serializtion_vector.expected_data, len) == 0); } From 84dd308afb327544b49cd42602083c1c0ab98a7c Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 13:22:17 -0500 Subject: [PATCH 08/21] resource/ns: replace empty NX proofs with minimally-covering NSEC --- src/ns.c | 18 ++++++++++------- src/resource.c | 55 ++++++++++++++++++++++++++++++++++++++++---------- src/resource.h | 3 ++- 3 files changed, 57 insertions(+), 19 deletions(-) diff --git a/src/ns.c b/src/ns.c index bb121685..71cbe955 100644 --- a/src/ns.c +++ b/src/ns.c @@ -384,12 +384,12 @@ hsk_ns_onrecv( } if (!match) { - // Needs SOA. - // TODO: Make the reverse pointers TLDs. - // Empty proof: + char next[HSK_DNS_MAX_NAME] = "\\000."; + memcpy(&next[6], req->name, strlen(req->name)); if (family == HSK_DNS_A) { hsk_resource_to_nsec( req->name, + next, hsk_type_map_a, sizeof(hsk_type_map_a), rrns @@ -397,6 +397,7 @@ hsk_ns_onrecv( } else { hsk_resource_to_nsec( req->name, + next, hsk_type_map_aaaa, sizeof(hsk_type_map_aaaa), rrns @@ -457,7 +458,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; @@ -550,9 +551,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 1b59ea8c..c4e33611 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,9 @@ 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 + char next[HSK_DNS_MAX_NAME]; + next_name(tld, next); + // Referral. if (labels > 1) { if (hsk_resource_has_ns(rs)) { @@ -972,6 +974,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 +991,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 +999,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 +1046,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 +1062,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 +1070,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 +1156,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 +1167,40 @@ 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 + 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 f11e34b4..3c8dba42 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); @@ -131,6 +131,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 8f590a83c4dfbd044ae1dce4c48a9be89160c586 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 14:20:46 -0500 Subject: [PATCH 09/21] test: better coverage for NSEC in resource decoding --- test/data/resource_vectors.h | 126 +++++++++++++++++++++-------------- test/hnsd-test.c | 30 +++++++++ 2 files changed, 106 insertions(+), 50 deletions(-) diff --git a/test/data/resource_vectors.h b/test/data/resource_vectors.h index 1119db80..22a3acfc 100644 --- a/test/data/resource_vectors.h +++ b/test/data/resource_vectors.h @@ -1,3 +1,5 @@ +#include "resource.h" + /* * Types */ @@ -8,6 +10,7 @@ typedef struct type_vector { uint8_t an_size; uint8_t ns_size; uint8_t ar_size; + bool nsec; } type_vector_t; typedef struct resource_vector { @@ -15,6 +18,9 @@ typedef struct resource_vector { 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; /* @@ -37,11 +43,13 @@ static const resource_vector_t resource_vectors[10] = { }, 6, { - {HSK_DNS_DS, "DS", 0, 4, 1}, - {HSK_DNS_NS, "NS", 0, 2, 1}, - {HSK_DNS_TXT, "TXT", 0, 4, 1}, - {HSK_DNS_A, "A", 0, 4, 1} - } + {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 }, // { @@ -60,11 +68,13 @@ static const resource_vector_t resource_vectors[10] = { }, 18, { - {HSK_DNS_DS, "DS", 0, 4, 1}, - {HSK_DNS_NS, "NS", 0, 2, 1}, - {HSK_DNS_TXT, "TXT", 0, 4, 1}, - {HSK_DNS_A, "A", 0, 4, 1} - } + {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 }, // { @@ -84,11 +94,13 @@ static const resource_vector_t resource_vectors[10] = { }, 15, { - {HSK_DNS_DS, "DS", 0, 4, 0}, - {HSK_DNS_NS, "NS", 0, 2, 0}, - {HSK_DNS_TXT, "TXT", 0, 4, 0}, - {HSK_DNS_A, "A", 0, 4, 0} - } + {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 }, // { @@ -109,11 +121,13 @@ static const resource_vector_t resource_vectors[10] = { }, 27, { - {HSK_DNS_DS, "DS", 0, 4, 1}, - {HSK_DNS_NS, "NS", 0, 2, 1}, - {HSK_DNS_TXT, "TXT", 0, 4, 1}, - {HSK_DNS_A, "A", 0, 4, 1} - } + {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 }, // { @@ -134,11 +148,13 @@ static const resource_vector_t resource_vectors[10] = { }, 27, { - {HSK_DNS_DS, "DS", 0, 4, 0}, - {HSK_DNS_NS, "NS", 0, 2, 0}, - {HSK_DNS_TXT, "TXT", 0, 4, 0}, - {HSK_DNS_A, "A", 0, 4, 0} - } + {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 }, // { @@ -160,11 +176,13 @@ static const resource_vector_t resource_vectors[10] = { }, 39, { - {HSK_DNS_DS, "DS", 0, 4, 1}, - {HSK_DNS_NS, "NS", 0, 2, 1}, - {HSK_DNS_TXT, "TXT", 0, 4, 1}, - {HSK_DNS_A, "A", 0, 4, 1} - } + {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 }, // { @@ -182,11 +200,13 @@ static const resource_vector_t resource_vectors[10] = { }, 11, { - {HSK_DNS_DS, "DS", 0, 4, 0}, - {HSK_DNS_NS, "NS", 0, 2, 0}, - {HSK_DNS_TXT, "TXT", 0, 4, 0}, - {HSK_DNS_A, "A", 0, 4, 0} - } + {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 }, // { @@ -205,11 +225,13 @@ static const resource_vector_t resource_vectors[10] = { }, 20, { - {HSK_DNS_DS, "DS", 0, 4, 0}, - {HSK_DNS_NS, "NS", 0, 2, 0}, - {HSK_DNS_TXT, "TXT", 0, 4, 0}, - {HSK_DNS_A, "A", 0, 4, 0} - } + {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 }, // { @@ -233,11 +255,13 @@ static const resource_vector_t resource_vectors[10] = { }, 39, { - {HSK_DNS_DS, "DS", 2, 0, 0}, - {HSK_DNS_NS, "NS", 0, 4, 0}, - {HSK_DNS_TXT, "TXT", 0, 4, 0}, - {HSK_DNS_A, "A", 0, 4, 0} - } + {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 }, // { @@ -260,10 +284,12 @@ static const resource_vector_t resource_vectors[10] = { }, 27, { - {HSK_DNS_DS, "DS", 0, 4, 0}, - {HSK_DNS_NS, "NS", 0, 4, 0}, - {HSK_DNS_TXT, "TXT", 2, 0, 0}, - {HSK_DNS_A, "A", 0, 4, 0} - } + {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 } }; diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 150885a3..018365dc 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -124,6 +124,36 @@ test_decode_resource() { assert(msg->an.size == type_vector.an_size); assert(msg->ns.size == type_vector.ns_size); assert(msg->ar.size == type_vector.ar_size); + + // 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); + } + } } } From 3a10a909988af8eea21db317397388cb8dd22a12 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 14:50:51 -0500 Subject: [PATCH 10/21] 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 c4e33611..bf22aade 100644 --- a/src/resource.c +++ b/src/resource.c @@ -1138,10 +1138,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 4426b2e765ae80ac3d4c40e6792e03361446260f Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 15:01:48 -0500 Subject: [PATCH 11/21] 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 bf22aade..227d0270 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)); @@ -1139,9 +1108,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 3c8dba42..22734c74 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 86ae85366dcf08089b1e07abbca80daf2e3db14b Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 22 Nov 2021 15:07:04 -0500 Subject: [PATCH 12/21] 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 227d0270..33f68da5 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 8ae46f8af8a1b04a9840a4fdbf0d6f6962242d3d Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 23 Nov 2021 08:10:48 -0500 Subject: [PATCH 13/21] resource: fix aa flag when querying root --- src/resource.c | 40 +++++++++++------- test/data/resource_vectors.h | 81 ++++++++++++++++++------------------ test/hnsd-test.c | 3 ++ 3 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/resource.c b/src/resource.c index 33f68da5..e38b0b8f 100644 --- a/src/resource.c +++ b/src/resource.c @@ -974,6 +974,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); @@ -986,32 +989,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( @@ -1026,9 +1024,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, @@ -1037,6 +1043,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, @@ -1047,6 +1054,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 22a3acfc..20de6f21 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[10] = { }, 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[10] = { }, 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[10] = { }, 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[10] = { }, 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[10] = { }, 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[10] = { }, 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[10] = { }, 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[10] = { }, 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[10] = { }, 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[10] = { }, 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 diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 018365dc..d21316a4 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -125,6 +125,9 @@ test_decode_resource() { 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]; From d5adee6e10ff5877485d9cefed6212a697e77534 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 23 Nov 2021 08:28:27 -0500 Subject: [PATCH 14/21] test: add vector with NS and DS --- test/data/resource_vectors.h | 44 +++++++++++++++++++++++++++++++++++- test/hnsd-test.c | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/test/data/resource_vectors.h b/test/data/resource_vectors.h index 20de6f21..b64cdcb6 100644 --- a/test/data/resource_vectors.h +++ b/test/data/resource_vectors.h @@ -28,7 +28,7 @@ typedef struct resource_vector { * Vectors */ -static const resource_vector_t resource_vectors[10] = { +static const resource_vector_t resource_vectors[11] = { // { // "records": [ // { @@ -292,5 +292,47 @@ static const resource_vector_t resource_vectors[10] = { }, 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 d21316a4..2abe06f0 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -104,7 +104,7 @@ void test_decode_resource() { printf("test_decode_resource\n"); - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 11; i++) { resource_vector_t resource_vector = resource_vectors[i]; hsk_resource_t *res = NULL; From 4f8e85179b13e078e69cfd3b97bb581268b77e9a Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 29 Nov 2021 14:03:35 -0500 Subject: [PATCH 15/21] dns/resource: fix nsec for 63 char names --- src/dns.c | 29 ++++++++++++++------------ src/resource.c | 9 +++++++- test/data/name_serialization_vectors.h | 8 +++---- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/src/dns.c b/src/dns.c index 9b55bfcb..c9fe6394 100644 --- a/src/dns.c +++ b/src/dns.c @@ -2222,16 +2222,28 @@ hsk_dns_name_serialize( int size; int i; char *s; - int size_byte = 0; + int size_adjust = 0; for (s = (char *)name, i = 0; *s; s++, i++) { + if (name[i] == '\\') { + if (!isdigit(name[++i]) || + !isdigit(name[++i]) || + !isdigit(name[++i])) { + *len = off; + return false; + } + + size_adjust += 3; + s += 3; + } + if (name[i] == '.') { if (i > 0 && name[i - 1] == '.') { *len = off; return false; } - size = i - begin; + size = i - begin - size_adjust; if (size > HSK_DNS_MAX_LABEL) { *len = off; @@ -2243,8 +2255,7 @@ hsk_dns_name_serialize( *len = off; return false; } - // Size will be written here once we know what size is - size_byte = off; + data[off] = size; } if (cmp) { @@ -2274,13 +2285,6 @@ hsk_dns_name_serialize( char ch = name[j]; if (ch == '\\') { // Read the next three digits and form a single byte - if (!isdigit(name[j + 1]) || - !isdigit(name[j + 2]) || - !isdigit(name[j + 3])) { - *len = off; - return false; - } - uint16_t value = (name[++j] - 0x30) * 100; value += (name[++j] - 0x30) * 10; value += (name[++j] - 0x30); @@ -2305,13 +2309,12 @@ hsk_dns_name_serialize( data[off++] = ch; } - - data[size_byte] = size; } else { off += size; } begin = i + 1; + size_adjust = 0; } } diff --git a/src/resource.c b/src/resource.c index e38b0b8f..7d474d90 100644 --- a/src/resource.c +++ b/src/resource.c @@ -929,6 +929,9 @@ 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); @@ -1160,7 +1163,10 @@ hsk_resource_to_nx(const char *tld) { 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 + // 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); @@ -1245,6 +1251,7 @@ next_name(const char *name, char *next) { memcpy(&next[len], "\\000.", 6); } else { next[len - 1]++; + memcpy(&next[len], ".", 2); } } diff --git a/test/data/name_serialization_vectors.h b/test/data/name_serialization_vectors.h index 860aaddd..eb392200 100644 --- a/test/data/name_serialization_vectors.h +++ b/test/data/name_serialization_vectors.h @@ -50,17 +50,15 @@ static const name_serializtion_vector_t name_serializtion_vectors[7] = { { "abcdef\\256.", { - 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 }, 7, false }, { "abcdef\\LOL.", - { - 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 - }, - 7, + {}, + 0, false }, { From c837b2db82824a612348f8c0d44ad90766c31d2f Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 30 Nov 2021 08:29:29 -0500 Subject: [PATCH 16/21] test: do not hard code test vector size --- test/data/name_serialization_vectors.h | 2 +- test/data/resource_vectors.h | 2 +- test/hnsd-test.c | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/test/data/name_serialization_vectors.h b/test/data/name_serialization_vectors.h index eb392200..531de3e8 100644 --- a/test/data/name_serialization_vectors.h +++ b/test/data/name_serialization_vectors.h @@ -14,7 +14,7 @@ typedef struct name_serializtion_vector { * Vectors */ -static const name_serializtion_vector_t name_serializtion_vectors[7] = { +static const name_serializtion_vector_t name_serializtion_vectors[] = { { "abcdef.", { diff --git a/test/data/resource_vectors.h b/test/data/resource_vectors.h index b64cdcb6..53f4433e 100644 --- a/test/data/resource_vectors.h +++ b/test/data/resource_vectors.h @@ -28,7 +28,7 @@ typedef struct resource_vector { * Vectors */ -static const resource_vector_t resource_vectors[11] = { +static const resource_vector_t resource_vectors[] = { // { // "records": [ // { diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 2abe06f0..5049ec70 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -7,6 +7,8 @@ #include "data/name_serialization_vectors.h" #include "data/resource_vectors.h" +#define ARRAY_SIZE(x) ((sizeof(x))/(sizeof(x[0]))) + /** * UTILITY */ @@ -80,7 +82,7 @@ void test_name_serialize() { printf("test_name_serialize\n"); - for (int i = 0; i < 7; i++) { + for (int i = 0; i < ARRAY_SIZE(name_serializtion_vectors); i++) { name_serializtion_vector_t name_serializtion_vector = name_serializtion_vectors[i]; uint8_t data[24] = {0}; @@ -104,7 +106,7 @@ void test_decode_resource() { printf("test_decode_resource\n"); - for (int i = 0; i < 11; i++) { + for (int i = 0; i < ARRAY_SIZE(resource_vectors); i++) { resource_vector_t resource_vector = resource_vectors[i]; hsk_resource_t *res = NULL; @@ -114,7 +116,7 @@ test_decode_resource() { &res ); - for (int t = 0; t < 4; t++) { + 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; From 491088f969cdaf335430acaf9fbf50cbdb763496 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 1 Dec 2021 10:21:27 -0500 Subject: [PATCH 17/21] dns: make name_dirty test more like hsd rule.verifyName() --- src/dns.c | 33 +++++++++++++++++++-------------- test/hnsd-test.c | 23 +++++++++++++++++++++++ 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/src/dns.c b/src/dns.c index c9fe6394..644c99b9 100644 --- a/src/dns.c +++ b/src/dns.c @@ -2431,26 +2431,31 @@ hsk_dns_name_alloc( bool hsk_dns_name_dirty(const char *name) { - char *s = (char *)name; + int len = strlen(name); + if (len > HSK_DNS_MAX_LABEL) + return true; - while (*s) { - uint8_t c = (uint8_t)*s; + for (int i = 0; i < len; i++) { + uint8_t c = name[i]; + + if (c >= 0x41 && c <= 0x5a) + c ^= 0x20; switch (c) { - case 0x28 /*(*/: - case 0x29 /*)*/: - case 0x3b /*;*/: - case 0x20 /* */: - case 0x40 /*@*/: - case 0x22 /*"*/: - case 0x5c /*\\*/: - return true; + case 0x5f: /* _ */ + case 0x2d: /* - */ + if (i == 0 || i == len - 1) { + return true; + } else { + continue; + } } - if (c < 0x20 || c > 0x7e) + if (c < 0x30 || + (c > 0x39 && c < 0x61) || + c > 0x7a) { return true; - - s += 1; + } } return false; diff --git a/test/hnsd-test.c b/test/hnsd-test.c index 5049ec70..a71267ed 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -207,6 +207,28 @@ test_prev_name() { ) == 0); } +void +test_verify_name() { + printf("test_verify_name\n"); + + assert(!hsk_dns_name_dirty("hello")); + assert(!hsk_dns_name_dirty("HELLO")); + assert(!hsk_dns_name_dirty("heLLo")); + assert(!hsk_dns_name_dirty("HeLl0")); + assert(!hsk_dns_name_dirty("hel-lo")); + assert(!hsk_dns_name_dirty("1")); + assert(!hsk_dns_name_dirty("000_000")); + assert(!hsk_dns_name_dirty("this-domain-name-has-sixty-three-octets-taking-max-label-length")); + assert(hsk_dns_name_dirty("hel!lo")); + assert(hsk_dns_name_dirty("-hello")); + assert(hsk_dns_name_dirty("hello_")); + assert(hsk_dns_name_dirty("1@1")); + assert(hsk_dns_name_dirty("x\\000y")); + assert(hsk_dns_name_dirty("H&ELLO")); + assert(hsk_dns_name_dirty("3 3")); + assert(hsk_dns_name_dirty("this-domain-name-has-sixtyfour-octets-exceeding-max-label-length")); +} + /* * TEST RUNNER */ @@ -220,6 +242,7 @@ main() { test_decode_resource(); test_next_name(); test_prev_name(); + test_verify_name(); printf("ok\n"); From 9bb9fd81db57cc9e3d4724c40fe0cfac6a4e7d50 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Mon, 13 Dec 2021 16:15:10 -0500 Subject: [PATCH 18/21] ns/resource: fix NSEC handling for _synth --- src/ns.c | 58 ++++++++++++++++++++++++++++---------------------- src/resource.h | 2 +- 2 files changed, 34 insertions(+), 26 deletions(-) diff --git a/src/ns.c b/src/ns.c index 71cbe955..f9b147a6 100644 --- a/src/ns.c +++ b/src/ns.c @@ -300,28 +300,6 @@ hsk_ns_onrecv( hsk_dns_req_print(req, "ns: "); - // Send REFUSED if name is dirty - // (contains escaped byte codes or special characters) - if (hsk_dns_name_dirty(req->tld)) { - msg = hsk_resource_to_refused(); - - if (!msg) { - hsk_ns_log(ns, "failed creating refused\n"); - goto fail; - } - - if (!hsk_dns_msg_finalize(&msg, req, ns->ec, ns->key, &wire, &wire_len)) { - hsk_ns_log(ns, "could not reply\n"); - goto done; - } - - hsk_ns_log(ns, "refusing query for msg (%u): %u\n", req->id, wire_len); - - hsk_ns_send(ns, wire, wire_len, addr, true); - - goto done; - } - // Hit cache first. msg = hsk_cache_get(&ns->cache, req); @@ -354,7 +332,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; @@ -366,6 +343,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)) { @@ -385,7 +371,7 @@ hsk_ns_onrecv( if (!match) { char next[HSK_DNS_MAX_NAME] = "\\000."; - memcpy(&next[6], req->name, strlen(req->name)); + memcpy(&next[5], req->name, strlen(req->name)); if (family == HSK_DNS_A) { hsk_resource_to_nsec( req->name, @@ -431,7 +417,7 @@ hsk_ns_onrecv( hsk_dns_rrs_push(an, rr); - hsk_dnssec_sign_zsk(ar, rrtype); + hsk_dnssec_sign_zsk(an, rrtype); } } @@ -447,6 +433,28 @@ hsk_ns_onrecv( goto done; } + // Send REFUSED if name is dirty + // (contains escaped byte codes or special characters) + if (hsk_dns_name_dirty(req->tld)) { + msg = hsk_resource_to_refused(); + + if (!msg) { + hsk_ns_log(ns, "failed creating refused\n"); + goto fail; + } + + if (!hsk_dns_msg_finalize(&msg, req, ns->ec, ns->key, &wire, &wire_len)) { + hsk_ns_log(ns, "could not reply\n"); + goto done; + } + + hsk_ns_log(ns, "refusing query for msg (%u): %u\n", req->id, wire_len); + + hsk_ns_send(ns, wire, wire_len, addr, true); + + goto done; + } + // Requesting a lookup. if (req->labels > 0) { // Check blacklist. diff --git a/src/resource.h b/src/resource.h index 22734c74..1384fe7e 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 34457f48f5c25a90be7d04ec31b677dd6797dd61 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Wed, 15 Dec 2021 12:21:54 -0500 Subject: [PATCH 19/21] ns: use strcat() --- src/ns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ns.c b/src/ns.c index f9b147a6..b370696b 100644 --- a/src/ns.c +++ b/src/ns.c @@ -371,7 +371,7 @@ hsk_ns_onrecv( if (!match) { char next[HSK_DNS_MAX_NAME] = "\\000."; - memcpy(&next[5], req->name, strlen(req->name)); + strcat(next, req->name); if (family == HSK_DNS_A) { hsk_resource_to_nsec( req->name, From 58bb9b390b761e3143e267ecbd7995085350ba65 Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 21 Dec 2021 10:41:53 -0500 Subject: [PATCH 20/21] dns: fix name read and write, parse escape characters --- src/dns.c | 128 ++++++++++++++++++------- test/data/name_serialization_vectors.h | 113 +++++++++++++++++++--- test/hnsd-test.c | 37 ++++++- 3 files changed, 227 insertions(+), 51 deletions(-) diff --git a/src/dns.c b/src/dns.c index 644c99b9..c9c852ec 100644 --- a/src/dns.c +++ b/src/dns.c @@ -2131,15 +2131,30 @@ hsk_dns_name_parse( for (j = off; j < off + c; j++) { uint8_t b = data[j]; - // Hack because we're - // using c-strings. - if (b == 0x00) - b = 0xff; - - // This allows for double-dots - // too easily in our design. - if (b == 0x2e) - b = 0xfe; + switch (b) { + // Escape special characters + case 0x2e /*.*/: + case 0x28 /*(*/: + case 0x29 /*)*/: + case 0x3b /*;*/: + case 0x20 /* */: + case 0x40 /*@*/: + case 0x22 /*"*/: + case 0x5c /*\\*/: + if (name) + sprintf(&name[noff], "\\%c", (char)b); + + noff += 2; + continue; + default: + if (b < 0x20 || b > 0x7e) { + if (name) + sprintf(&name[noff], "\\%03d", b); + + noff += 4; + continue; + } + } if (name) name[noff] = b; @@ -2223,22 +2238,53 @@ hsk_dns_name_serialize( int i; char *s; int size_adjust = 0; + bool escaped = false; + size_t max = strlen(name) - 1; for (s = (char *)name, i = 0; *s; s++, i++) { + // Check for escaped byte codes and adjust length measurement. if (name[i] == '\\') { - if (!isdigit(name[++i]) || - !isdigit(name[++i]) || - !isdigit(name[++i])) { - *len = off; - return false; + // Check the next three bytes (if within string length) + // for three-digit code which must encode a one-byte value. + if (i + 3 <= max && + isdigit(name[i + 1]) && + isdigit(name[i + 2]) && + isdigit(name[i + 3])) { + uint16_t value = (name[i + 1] - 0x30) * 100; + value += (name[i + 2] - 0x30) * 10; + value += (name[i + 3] - 0x30); + + // Bad escape, byte code out of range. + if (value > 0xff) { + *len = off; + return false; + } + + // The next three characters don't count towards final size. + size_adjust += 2; + s += 2; + i += 2; + + // Escaped dot by byte code + if (value == 0x2e) + escaped = true; } - size_adjust += 3; - s += 3; + // Literal escaped dot + if (i + 1 <= max && name[i + 1] == 0x2e) + escaped = true; + + // Remove single slash and skip next character. + size_adjust += 1; + s += 1; + i += 1; + + continue; } if (name[i] == '.') { - if (i > 0 && name[i - 1] == '.') { + if (i > 0 && name[i - 1] == '.' && !escaped) { + // Multiple dots (escaped dot is ok) *len = off; return false; } @@ -2283,30 +2329,36 @@ hsk_dns_name_serialize( int j; for (j = begin; j < i; j++) { char ch = name[j]; + // Check for escaped byte codes and process into output result if (ch == '\\') { - // Read the next three digits and form a single byte - uint16_t value = (name[++j] - 0x30) * 100; - value += (name[++j] - 0x30) * 10; - value += (name[++j] - 0x30); - - if (value > 0xff) { - *len = off; - return false; + // Check the next three bytes (if within label length) + // for three-digit code which must encode a one-byte value. + if (j + 3 <= i && + isdigit(name[j + 1]) && + isdigit(name[j + 2]) && + isdigit(name[j + 3])) { + // Compute byte from next three characters. + uint16_t value = (name[++j] - 0x30) * 100; + value += (name[++j] - 0x30) * 10; + value += (name[++j] - 0x30); + + // Bad escape, byte code out of range. + if (value > 0xff) { + *len = off; + return false; + } + + // Write + data[off++] = value; + continue; + } else { + // Remove single slash and write next character + // as long as there is a next character. + if (j + 1 < i) + ch = name[++j]; } - - data[off++] = value; - size -= 3; - continue; } - // 0xff -> NUL - if (ch == -1) - ch = '\0'; - - // 0xfe -> . - if (ch == -2) - ch = '.'; - data[off++] = ch; } } else { @@ -2316,6 +2368,8 @@ hsk_dns_name_serialize( begin = i + 1; size_adjust = 0; } + + escaped = false; } if (i > HSK_DNS_MAX_NAME) { diff --git a/test/data/name_serialization_vectors.h b/test/data/name_serialization_vectors.h index 531de3e8..f502a76b 100644 --- a/test/data/name_serialization_vectors.h +++ b/test/data/name_serialization_vectors.h @@ -5,8 +5,9 @@ typedef struct name_serializtion_vector { char *name; uint8_t expected_data[24]; - int expected_len; + size_t expected_len; bool success; + char *parsed; } name_serializtion_vector_t; @@ -21,7 +22,8 @@ static const name_serializtion_vector_t name_serializtion_vectors[] = { 0x06, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00 }, 8, - true + true, + "abcdef." }, { "abc.def.", @@ -29,7 +31,8 @@ static const name_serializtion_vector_t name_serializtion_vectors[] = { 0x03, 0x61, 0x62, 0x63, 0x03, 0x64, 0x65, 0x66, 0x00 }, 9, - true + true, + "abc.def." }, { "abcdef\\000.", @@ -37,7 +40,8 @@ static const name_serializtion_vector_t name_serializtion_vectors[] = { 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00, 0x00 }, 9, - true + true, + "abcdef\\000." }, { "abcdef\\255.", @@ -45,28 +49,111 @@ static const name_serializtion_vector_t name_serializtion_vectors[] = { 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0xff, 0x00 }, 9, - true + true, + "abcdef\\255." }, { "abcdef\\256.", + {}, + 0, + false, // bad escape (value > 0xff) + NULL + }, + { + "abcdef\\LOL.", { - 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66 + 0x09, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x4c, 0x4f, 0x4c, 0x00 }, - 7, - false + 11, + true, + "abcdefLOL." }, { - "abcdef\\LOL.", + "abc\\031def.", + { + 0x07, 0x61, 0x62, 0x63, 0x1f, 0x64, 0x65, 0x66, 0x00 + }, + 9, + true, + "abc\\031def." + }, + { + "abc\\\\def.", + { + 0x07, 0x61, 0x62, 0x63, 0x5c, 0x64, 0x65, 0x66, 0x00 + }, + 9, + true, + "abc\\\\def." + }, + { + "\\999.", {}, 0, - false + false, // bad escape (value > 0xff) + NULL }, { - "abc\\032def.", + "\\\\999.", { - 0x07, 0x61, 0x62, 0x63, 0x20, 0x64, 0x65, 0x66, 0x00 + 0x04, 0x5c, 0x39, 0x39, 0x39, 0x00 + }, + 6, + true, + "\\\\999." + }, + { + "\\\\222.", + { + 0x04, 0x5c, 0x32, 0x32, 0x32, 0x00 + }, + 6, + true, + "\\\\222." + }, + { + "abc\\\\999.", + { + 0x07, 0x61, 0x62, 0x63, 0x5c, 0x39, 0x39, 0x39, 0x00 }, 9, - true + true, + "abc\\\\999." + }, + { + "abc\\\\99.", + { + 0x06, 0x61, 0x62, 0x63, 0x5c, 0x39, 0x39, 0x00 + }, + 8, + true, + "abc\\\\99." + }, + { + "abc\\\\.", + { + 0x04, 0x61, 0x62, 0x63, 0x5c, 0x00 + }, + 6, + true, + "abc\\\\." + }, + { + "\\..", + { + 0x01, 0x2e, 0x00 + }, + 3, + true, + "\\.." + }, + { + "\\046.", + { + 0x01, 0x2e, 0x00 + }, + 3, + true, + "\\.." } }; diff --git a/test/hnsd-test.c b/test/hnsd-test.c index a71267ed..aad82a1d 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -86,7 +86,7 @@ test_name_serialize() { name_serializtion_vector_t name_serializtion_vector = name_serializtion_vectors[i]; uint8_t data[24] = {0}; - int len; + int len = 0; printf(" %s\n", name_serializtion_vector.name); @@ -96,12 +96,46 @@ test_name_serialize() { &len, NULL ); + + + // printf("len: %d expected_len: %d\n",len,name_serializtion_vector.expected_len); + // print_array(name_serializtion_vector.expected_data, name_serializtion_vector.expected_len); + // print_array(data, name_serializtion_vector.expected_len); + assert(name_serializtion_vector.success == success); assert(len == name_serializtion_vector.expected_len); assert(memcmp(data, name_serializtion_vector.expected_data, len) == 0); } } +void +test_name_parse() { + printf("test_name_parse\n"); + + for (int i = 0; i < ARRAY_SIZE(name_serializtion_vectors); i++) { + name_serializtion_vector_t name_serializtion_vector = name_serializtion_vectors[i]; + + char name[255]; + + if (!name_serializtion_vector.parsed) + continue; + + printf(" %s\n", name_serializtion_vector.name); + + uint8_t *ptr = (uint8_t *)&name_serializtion_vector.expected_data; + size_t len = name_serializtion_vector.expected_len; + + int ret = hsk_dns_name_parse( + (uint8_t **)&ptr, + &len, + NULL, + name + ); + assert(ret == strlen(name_serializtion_vector.parsed)); + assert(strcmp(name_serializtion_vector.parsed, name) == 0); + } +} + void test_decode_resource() { printf("test_decode_resource\n"); @@ -239,6 +273,7 @@ main() { test_base32(); test_pointer_to_ip(); test_name_serialize(); + test_name_parse(); test_decode_resource(); test_next_name(); test_prev_name(); From d413814b971866543cbf1726faa9b95ac9d34abb Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 21 Dec 2021 19:50:29 -0500 Subject: [PATCH 21/21] test: integration test with hsd --- .github/workflows/test-windows.yaml | 13 +- .github/workflows/test.yml | 11 +- .gitignore | 1 + src/ns.c | 4 +- test/integration/hnsd-integration-test.js | 278 ++++++++++++++++++ test/integration/package-lock.json | 325 ++++++++++++++++++++++ test/integration/package.json | 16 ++ 7 files changed, 642 insertions(+), 6 deletions(-) create mode 100644 test/integration/hnsd-integration-test.js create mode 100644 test/integration/package-lock.json create mode 100644 test/integration/package.json diff --git a/.github/workflows/test-windows.yaml b/.github/workflows/test-windows.yaml index 164d966c..9e53200d 100644 --- a/.github/workflows/test-windows.yaml +++ b/.github/workflows/test-windows.yaml @@ -18,7 +18,7 @@ jobs: with: msystem: MINGW64 update: true - install: git mingw-w64-x86_64-toolchain base-devel mingw-w64-x86_64-unbound + install: git mingw-w64-x86_64-toolchain base-devel mingw-w64-x86_64-unbound autotools - run: git config --global core.autocrlf input shell: bash @@ -26,9 +26,16 @@ jobs: - uses: actions/checkout@v2 - name: Build - run: ./autogen.sh && ./configure && make + run: ./autogen.sh && ./configure --with-network=regtest && make - name: Unit Tests run: ./test_hnsd - # TODO: Install nodejs, intall hsd and test end-to-end integration. + - name: Setup Integration + uses: actions/setup-node@v1 + + - name: Integration Tests + working-directory: ./test/integration + run: | + npm install + ./node_modules/bmocha/bin/bmocha --reporter spec hnsd-integration-test.js diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c00e40f5..f03504ef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,9 +25,16 @@ jobs: run: sudo apt-get install -y libunbound-dev - name: Build - run: ./autogen.sh && ./configure && make + run: ./autogen.sh && ./configure --with-network=regtest && make - name: Unit Tests run: ./test_hnsd - # TODO: Install nodejs, intall hsd and test end-to-end integration. + - name: Setup Integration + uses: actions/setup-node@v1 + + - name: Integration Tests + working-directory: ./test/integration + run: | + npm install + ./node_modules/bmocha/bin/bmocha --reporter spec hnsd-integration-test.js diff --git a/.gitignore b/.gitignore index 3f8b3e8c..3bfb8821 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,4 @@ m4/ltversion.m4 m4/lt~obsolete.m4 src/stamp-h1 test_hnsd +test/integration/node_modules/ diff --git a/src/ns.c b/src/ns.c index b370696b..92256dd7 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; diff --git a/test/integration/hnsd-integration-test.js b/test/integration/hnsd-integration-test.js new file mode 100644 index 00000000..167d479b --- /dev/null +++ b/test/integration/hnsd-integration-test.js @@ -0,0 +1,278 @@ +/* eslint-env mocha */ +/* eslint prefer-arrow-callback: "off" */ +/* eslint max-len: "off" */ +/* eslint no-return-assign: "off" */ +'use strict'; + +const {spawn} = require('child_process'); +const path = require('path'); +const assert = require('bsert'); +const {FullNode} = require('hsd'); +const dns = require('bns/lib/dns'); +const wire = require('bns/lib/wire'); + +describe('hnsd Integration Test', function() { + const node = new FullNode({ + memory: true, + network: 'regtest', + listen: true, + port: 10000, + noDns: true, + plugins: [require('hsd/lib/wallet/plugin')] + }); + + const resolver = new dns.Resolver({ + host: '127.0.0.1', + port: 25349, + dnssec: true + }); + resolver.setServers(['127.0.0.1:25349']); + + const hnsdPath = path.join(__dirname, '..', '..', 'hnsd'); + let hnsd; + + function find(rrs, type) { + for (const rr of rrs) { + // return first found + if (rr.type === wire.types[type]) + return rr; + } + return null; + } + + before(async () => { + await node.open(); + await node.connect(); + + hnsd = spawn( + hnsdPath, + ['-s', '127.0.0.1:10000'], + {shell: false} + ); + }); + + after(async () => { + hnsd.kill('SIGKILL'); + await node.close(); + }); + + it('should register names', async() => { + const addr = await node.plugins.walletdb.rpc.getNewAddress(['default']); + await node.rpc.generateToAddress([100, addr]); + await node.plugins.walletdb.rpc.sendOpen(['test-ds']); + await node.plugins.walletdb.rpc.sendOpen(['test-ns']); + await node.plugins.walletdb.rpc.sendOpen(['test-txt']); + await node.plugins.walletdb.rpc.sendOpen(['test-glue4-glue']); + await node.rpc.generateToAddress([6, addr]); + await node.plugins.walletdb.rpc.sendBid(['test-ds', 1, 1]); + await node.plugins.walletdb.rpc.sendBid(['test-ns', 1, 1]); + await node.plugins.walletdb.rpc.sendBid(['test-txt', 1, 1]); + await node.plugins.walletdb.rpc.sendBid(['test-glue4-glue', 1, 1]); + await node.rpc.generateToAddress([6, addr]); + await node.plugins.walletdb.rpc.sendReveal([]); + await node.rpc.generateToAddress([10, addr]); + await node.plugins.walletdb.rpc.sendUpdate([ + 'test-ds', + { + 'records': + [ + { + 'type': 'DS', + 'keyTag': 57355, + 'algorithm': 8, + 'digestType': 2, + 'digest': '95a57c3bab7849dbcddf7c72ada71a88146b141110318ca5be672057e865c3e2' + } + ] + } + ]); + await node.plugins.walletdb.rpc.sendUpdate([ + 'test-ns', + { + 'records': + [ + { + 'type': 'NS', + 'ns': 'ns1.hns.' + } + ] + } + ]); + await node.plugins.walletdb.rpc.sendUpdate([ + 'test-txt', + { + 'records': + [ + { + 'type': 'TXT', + 'txt': ['hello world'] + } + ] + } + ]); + await node.plugins.walletdb.rpc.sendUpdate([ + 'test-glue4-glue', + { + 'records': + [ + { + 'type': 'GLUE4', + 'ns': 'ns1.test-glue4-glue.', + 'address': '10.20.30.40' + } + ] + } + ]); + await node.rpc.generateToAddress([10, addr]); + }); + + it('doesnotexist / A', async() => { + const result = await resolver.resolveRaw('doesnotexist.', 'A'); + const nsec = find(result.authority, 'NSEC'); + assert(nsec); + assert.strictEqual(nsec.name, 'doesnotexiss\\255.'); + assert.strictEqual(nsec.data.nextDomain, 'doesnotexist\\000.'); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NSEC)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.RRSIG)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.A)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.AAAA)); + }); + + it('_synth / A', async() => { + const result = await resolver.resolveRaw('_synth.', 'A'); + const nsec = find(result.authority, 'NSEC'); + assert(nsec); + assert.strictEqual(nsec.name, '_synth.'); + assert.strictEqual(nsec.data.nextDomain, '\\000._synth.'); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NSEC)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.RRSIG)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.A)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.AAAA)); + }); + + it('_fs00008._synth. / A', async() => { + const result = await resolver.resolveRaw('_fs00008._synth.', 'A'); + const a = find(result.answer, 'A'); + assert(a); + assert.strictEqual(a.data.address, '127.0.0.1'); + }); + + it('_fs00008._synth. / AAAA', async() => { + const result = await resolver.resolveRaw('_fs00008._synth.', 'AAAA'); + const nsec = find(result.authority, 'NSEC'); + assert(nsec); + assert.strictEqual(nsec.name, '_fs00008._synth.'); + assert.strictEqual(nsec.data.nextDomain, '\\000._fs00008._synth.'); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.A)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NSEC)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.RRSIG)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.AAAA)); + }); + + it('badsynth._synth. / A', async() => { + const result = await resolver.resolveRaw('badsynth._synth.', 'A'); + assert.strictEqual(result.question[0].name, 'badsynth._synth.'); + assert.strictEqual(result.code, wire.codes.REFUSED); + }); + + it('test-ds / DS', async() => { + const result = await resolver.resolveRaw('test-ds.', 'DS'); + assert(result.authority.length === 0); + const ds = find(result.answer, 'DS'); + assert(ds); + assert.bufferEqual( + ds.data.digest, + Buffer.from('95a57c3bab7849dbcddf7c72ada71a88146b141110318ca5be672057e865c3e2', 'hex') + ); + }); + + it('test-ds / NS', async() => { + const result = await resolver.resolveRaw('test-ds.', 'NS'); + const nsec = find(result.authority, 'NSEC'); + assert(nsec); + assert.strictEqual(nsec.name, 'test-ds.'); + assert.strictEqual(nsec.data.nextDomain, 'test-ds\\000.'); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NSEC)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.RRSIG)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.DS)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.NS)); + }); + + it('test-ns / NS', async() => { + const result = await resolver.resolveRaw('test-ns.', 'NS'); + const ns = find(result.authority, 'NS'); + assert(ns); + assert.strictEqual(ns.data.ns, 'ns1.hns.'); + + const nsec = find(result.authority, 'NSEC'); + assert(nsec); + assert.strictEqual(nsec.name, 'test-ns.'); + assert.strictEqual(nsec.data.nextDomain, 'test-ns\\000.'); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NSEC)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.RRSIG)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NS)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.DS)); + }); + + it('test-txt / TXT', async() => { + const result = await resolver.resolveRaw('test-txt.', 'TXT'); + const txt = find(result.answer, 'TXT'); + assert(txt); + assert.strictEqual(txt.data.txt[0], 'hello world'); + }); + + it('test-txt / NS', async() => { + const result = await resolver.resolveRaw('test-txt.', 'NS'); + const nsec = find(result.authority, 'NSEC'); + assert(nsec); + assert.strictEqual(nsec.name, 'test-txt.'); + assert.strictEqual(nsec.data.nextDomain, 'test-txt\\000.'); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NSEC)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.RRSIG)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.TXT)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.DS)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.NS)); + }); + + it('test-glue4-glue / NS', async() => { + const result = await resolver.resolveRaw('test-glue4-glue.', 'NS'); + const ns = find(result.authority, 'NS'); + assert(ns); + assert.strictEqual(ns.data.ns, 'ns1.test-glue4-glue.'); + + const a = find(result.additional, 'A'); + assert(a); + assert.strictEqual(a.data.address, '10.20.30.40'); + + const nsec = find(result.authority, 'NSEC'); + assert(nsec); + assert.strictEqual(nsec.name, 'test-glue4-glue.'); + assert.strictEqual(nsec.data.nextDomain, 'test-glue4-glue\\000.'); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NSEC)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.RRSIG)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NS)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.TXT)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.DS)); + }); + + it('foo\\200 / A', async() => { + const result = await resolver.resolveRaw('foo\\200.', 'A'); + assert.strictEqual(result.question[0].name, 'foo\\200.'); + assert.strictEqual(result.code, wire.codes.REFUSED); + }); + + it('\\\\ducks.doesnotexist2. / A', async() => { + const result = await resolver.resolveRaw('\\\\ducks.doesnotexist2.', 'A'); + assert.strictEqual(result.question[0].name, '\\\\ducks.doesnotexist2.'); + + const nsec = find(result.authority, 'NSEC'); + assert(nsec); + assert.strictEqual(nsec.name, 'doesnotexist1\\255.'); + assert.strictEqual(nsec.data.nextDomain, 'doesnotexist2\\000.'); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.NSEC)); + assert(nsec.data.toJSON().typeBitmap.includes(wire.types.RRSIG)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.NS)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.TXT)); + assert(!nsec.data.toJSON().typeBitmap.includes(wire.types.DS)); + }); +}); diff --git a/test/integration/package-lock.json b/test/integration/package-lock.json new file mode 100644 index 00000000..abc781d5 --- /dev/null +++ b/test/integration/package-lock.json @@ -0,0 +1,325 @@ +{ + "name": "hnsd_integration_test", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "bcfg": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/bcfg/-/bcfg-0.1.7.tgz", + "integrity": "sha512-+4beq5bXwfmxdcEoHYQsaXawh1qFzjLcRvPe5k5ww/NEWzZTm56Jk8LuPmfeGB7X584jZ8xGq6UgMaZnNDa5Ww==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bcrypto": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/bcrypto/-/bcrypto-5.4.0.tgz", + "integrity": "sha512-KDX2CR29o6ZoqpQndcCxFZAtYA1jDMnXU3jmCfzP44g++Cu7AHHtZN/JbrN/MXAg9SLvtQ8XISG+eVD9zH1+Jg==", + "requires": { + "bufio": "~1.0.7", + "loady": "~0.0.5" + } + }, + "bcurl": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/bcurl/-/bcurl-0.1.10.tgz", + "integrity": "sha512-NPxrIkc61tI2FvuibKW8IHaWs7YpgGvOWTgYEJYoE0vOiCvjuL6PXeKYq6bZqadeZfQ6v+R74qvj3Siiv+/Pvg==", + "requires": { + "brq": "~0.1.8", + "bsert": "~0.0.10", + "bsock": "~0.1.9" + } + }, + "bdb": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/bdb/-/bdb-1.3.0.tgz", + "integrity": "sha512-oJnWnHOTcnJhazwpEzQvPFtSR1IdHtS3PczuLY3klgZTTtRUbARX7tdphQS8iNUUwEVMfuO93eHDWwTICoeJlg==", + "requires": { + "bsert": "~0.0.10", + "loady": "~0.0.5" + } + }, + "bdns": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/bdns/-/bdns-0.1.5.tgz", + "integrity": "sha512-LNVkfM7ynlAD0CvPvO9cKxW8YXt1KOCRQZlRsGZWeMyymUWVdHQpZudAzH9chaFAz6HiwAnQxwDemCKDPy6Mag==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bevent": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/bevent/-/bevent-0.1.5.tgz", + "integrity": "sha512-hs6T3BjndibrAmPSoKTHmKa3tz/c6Qgjv9iZw+tAoxuP6izfTCkzfltBQrW7SuK5xnY22gv9jCEf51+mRH+Qvg==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bfile": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/bfile/-/bfile-0.2.2.tgz", + "integrity": "sha512-X205SsJ7zFAnjeJ/pBLqDqF10x/4Su3pBy8UdVKw4hdGJk7t5pLoRi+uG4rPaDAClGbrEfT/06PGUbYiMYKzTg==" + }, + "bfilter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bfilter/-/bfilter-1.0.5.tgz", + "integrity": "sha512-GupIidtCvLbKhXnA1sxvrwa+gh95qbjafy7P1U1x/2DHxNabXq4nGW0x3rmgzlJMYlVl+c8fMxoMRIwpKYlgcQ==", + "requires": { + "bsert": "~0.0.10", + "bufio": "~1.0.6", + "mrmr": "~0.1.6" + } + }, + "bheep": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/bheep/-/bheep-0.1.5.tgz", + "integrity": "sha512-0KR5Zi8hgJBKL35+aYzndCTtgSGakOMxrYw2uszd5UmXTIfx3+drPGoETlVbQ6arTdAzSoQYA1j35vbaWpQXBg==", + "requires": { + "bsert": "~0.0.10" + } + }, + "binet": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/binet/-/binet-0.3.6.tgz", + "integrity": "sha512-6pm+Gc3uNiiJZEv0k8JDWqQlo9ki/o9UNAkLmr0EGm7hI5MboOJVIOlO1nw3YuDkLHWN78OPsaC4JhRkn2jMLw==", + "requires": { + "bs32": "~0.1.5", + "bsert": "~0.0.10" + } + }, + "blgr": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/blgr/-/blgr-0.2.0.tgz", + "integrity": "sha512-2jZdqajYCGD5rwGdOooQpxgjKsiAAV2g8LapwSnbTjAYTZAqmqBAS+GsVGFi+/y7t1Pspidv/5HsWBbJrsEuFw==", + "requires": { + "bsert": "~0.0.10" + } + }, + "blru": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/blru/-/blru-0.1.6.tgz", + "integrity": "sha512-34+xZ2u4ys/aUzWCU9m6Eee4nVuN1ywdxbi8b3Z2WULU6qvnfeHvCWEdGzlVfRbbhimG2xxJX6R77GD2cuVO6w==", + "requires": { + "bsert": "~0.0.10" + } + }, + "blst": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/blst/-/blst-0.1.5.tgz", + "integrity": "sha512-TPl04Cx3CHdPFAJ2x9Xx1Z1FOfpAzmNPfHkfo+pGAaNH4uLhS58ExvamVkZh3jadF+B7V5sMtqvrqdf9mHINYA==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bmocha": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/bmocha/-/bmocha-2.1.5.tgz", + "integrity": "sha512-hEO+jQC+6CMxdxSqKPjqAdIDvRWHfdGgsMh4fUmatkMewbYr2O6qMIbW7Lhcmkcnz8bwRHZuEdDaBt/16NofoA==" + }, + "bmutex": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bmutex/-/bmutex-0.1.6.tgz", + "integrity": "sha512-nXWOXtQHbfPaMl6jyEF/rmRMrcemj2qn+OCAI/uZYurjfx7Dg3baoXdPzHOL0U8Cfvn8CWxKcnM/rgxL7DR4zw==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bns": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/bns/-/bns-0.15.0.tgz", + "integrity": "sha512-iJWQVE399vQzPfhalFMJGEQ7k5Ot2D6Mz8dkoPeLO8huWAMOiJNJ1tHzOu5j+ZyNNew6ITgG/LsSyaRPxvkXuw==", + "requires": { + "bcrypto": "~5.4.0", + "bfile": "~0.2.2", + "bheep": "~0.1.5", + "binet": "~0.3.6", + "bs32": "~0.1.6", + "bsert": "~0.0.10", + "btcp": "~0.1.5", + "budp": "~0.1.6", + "bufio": "~1.0.7", + "unbound": "~0.4.3" + } + }, + "brq": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/brq/-/brq-0.1.8.tgz", + "integrity": "sha512-6SDY1lJMKXgt5TZ6voJQMH2zV1XPWWtm203PSkx3DSg9AYNYuRfOPFSBDkNemabzgpzFW9/neR4YhTvyJml8rQ==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bs32": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bs32/-/bs32-0.1.6.tgz", + "integrity": "sha512-usjDesQqZ8ihHXOnOEQuAdymBHnJEfSd+aELFSg1jN/V3iAf12HrylHlRJwIt6DTMmXpBDQ+YBg3Q3DIYdhRgQ==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bsert": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/bsert/-/bsert-0.0.10.tgz", + "integrity": "sha512-NHNwlac+WPy4t2LoNh8pXk8uaIGH3NSaIUbTTRXGpE2WEbq0te/tDykYHkFK57YKLPjv/aGHmbqvnGeVWDz57Q==" + }, + "bsock": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/bsock/-/bsock-0.1.9.tgz", + "integrity": "sha512-/l9Kg/c5o+n/0AqreMxh2jpzDMl1ikl4gUxT7RFNe3A3YRIyZkiREhwcjmqxiymJSRI/Qhew357xGn1SLw/xEw==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bsocks": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bsocks/-/bsocks-0.2.6.tgz", + "integrity": "sha512-66UkjoB9f7lhT+WKgYq8MQa6nkr96mlX64JYMlIsXe/X4VeqNwvsx7UOE3ZqD6lkwg8GvBhapRTWj0qWO3Pw8w==", + "requires": { + "binet": "~0.3.5", + "bsert": "~0.0.10" + } + }, + "btcp": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/btcp/-/btcp-0.1.5.tgz", + "integrity": "sha512-tkrtMDxeJorn5p0KxaLXELneT8AbfZMpOFeoKYZ5qCCMMSluNuwut7pGccLC5YOJqmuk0DR774vNVQLC9sNq/A==" + }, + "budp": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/budp/-/budp-0.1.6.tgz", + "integrity": "sha512-o+a8NPq3DhV91j4nInjht2md6mbU1XL+7ciPltP66rw5uD3KP1m5r8lA94LZVaPKcFdJ0l2HVVzRNxnY26Pefg==" + }, + "buffer-map": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/buffer-map/-/buffer-map-0.0.7.tgz", + "integrity": "sha512-95try3p/vMRkIAAnJDaGkFhGpT/65NoeW6XelEPjAomWYR58RQtW4khn0SwKj34kZoE7uxL7w2koZSwbnszvQQ==" + }, + "bufio": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", + "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==" + }, + "bupnp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bupnp/-/bupnp-0.2.6.tgz", + "integrity": "sha512-J6ykzJhZMxXKN78K+1NzFi3v/51X2Mvzp2hW42BWwmxIVfau6PaN99gyABZ8x05e8MObWbsAis23gShhj9qpbw==", + "requires": { + "binet": "~0.3.5", + "brq": "~0.1.7", + "bsert": "~0.0.10" + } + }, + "bval": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/bval/-/bval-0.1.6.tgz", + "integrity": "sha512-jxNH9gSx7g749hQtS+nTxXYz/bLxwr4We1RHFkCYalNYcj12RfbW6qYWsKu0RYiKAdFcbNoZRHmWrIuXIyhiQQ==", + "requires": { + "bsert": "~0.0.10" + } + }, + "bweb": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/bweb/-/bweb-0.1.10.tgz", + "integrity": "sha512-3Kkz/rfsyAWUS+8DV5XYhwcgVN4DfDewrP+iFTcpQfdZzcF6+OypAq7dHOtXV0sW7U/3msA/sEEqz0MHZ9ERWg==", + "requires": { + "bsert": "~0.0.10", + "bsock": "~0.1.8" + } + }, + "goosig": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/goosig/-/goosig-0.10.0.tgz", + "integrity": "sha512-+BVVLfxmawAmGVjjJpXzu5LNcFIOfgXgP7kWEyc3qu/xn9RMqbPbNfYDdHBZKfZkDMIO7Q4vD790iNYQAXhoFA==", + "requires": { + "bcrypto": "~5.4.0", + "bsert": "~0.0.10", + "loady": "~0.0.5" + } + }, + "hsd": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hsd/-/hsd-3.0.1.tgz", + "integrity": "sha512-AI7ruyDhxyzKQzaDdEj0N2zjjYXjEPwQGlWQTootg85ByNKWQsAfZChvWqi2ek0FLPuNlM1toaDT+jcYi+UmqA==", + "requires": { + "bcfg": "~0.1.7", + "bcrypto": "~5.4.0", + "bdb": "~1.3.0", + "bdns": "~0.1.5", + "bevent": "~0.1.5", + "bfile": "~0.2.2", + "bfilter": "~1.0.5", + "bheep": "~0.1.5", + "binet": "~0.3.6", + "blgr": "~0.2.0", + "blru": "~0.1.6", + "blst": "~0.1.5", + "bmutex": "~0.1.6", + "bns": "~0.15.0", + "bsert": "~0.0.10", + "bsock": "~0.1.9", + "bsocks": "~0.2.6", + "btcp": "~0.1.5", + "buffer-map": "~0.0.7", + "bufio": "~1.0.7", + "bupnp": "~0.2.6", + "bval": "~0.1.6", + "bweb": "~0.1.10", + "goosig": "~0.10.0", + "hs-client": "~0.0.10", + "n64": "~0.2.10", + "urkel": "~0.7.0" + }, + "dependencies": { + "hs-client": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/hs-client/-/hs-client-0.0.10.tgz", + "integrity": "sha512-15tfeQEMRS1FZA0q9gFbQ1jYs8v4z9oKw9xFwVEyRuckn72hoVAglN4IrFxkOCDMYV7TWCY/nO/yNZp5njYFBw==", + "requires": { + "bcfg": "~0.1.7", + "bcurl": "~0.1.9", + "bsert": "~0.0.10" + } + } + } + }, + "loady": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/loady/-/loady-0.0.5.tgz", + "integrity": "sha512-uxKD2HIj042/HBx77NBcmEPsD+hxCgAtjEWlYNScuUjIsh/62Uyu39GOR68TBR68v+jqDL9zfftCWoUo4y03sQ==" + }, + "mrmr": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/mrmr/-/mrmr-0.1.10.tgz", + "integrity": "sha512-NJRJs+yJyRWwcTqLRf7O32n56UP1+UQoTrGVEoB3LMj0h2jlon790drDbxKvi5mK5k4HfC0cpNkxqHcrJK/evg==", + "requires": { + "bsert": "~0.0.10", + "loady": "~0.0.5" + } + }, + "n64": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/n64/-/n64-0.2.10.tgz", + "integrity": "sha512-uH9geV4+roR1tohsrrqSOLCJ9Mh1iFcDI+9vUuydDlDxUS1UCAWUfuGb06p3dj3flzywquJNrGsQ7lHP8+4RVQ==" + }, + "unbound": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/unbound/-/unbound-0.4.3.tgz", + "integrity": "sha512-2ISqZLXtzp1l9f1V8Yr6S+zuhXxEwE1CjKHjXULFDHJcfhc9Gm3mn19hdPp4rlNGEdCivKYGKjYe3WRGnafYdA==", + "optional": true, + "requires": { + "loady": "~0.0.5" + } + }, + "urkel": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/urkel/-/urkel-0.7.0.tgz", + "integrity": "sha512-7Z3Gor4DkKKi0Ehp6H9xehWXqyL12+PA4JM41dcVc1LWks4zI4PGWv6DWgxaLCC+otpEuGdq3Vh5ayD/Mvzfbg==", + "requires": { + "bfile": "~0.2.1", + "bmutex": "~0.1.6", + "bsert": "~0.0.10" + } + } + } +} diff --git a/test/integration/package.json b/test/integration/package.json new file mode 100644 index 00000000..4221a071 --- /dev/null +++ b/test/integration/package.json @@ -0,0 +1,16 @@ +{ + "name": "hnsd_integration_test", + "version": "1.0.0", + "description": "hnsd integration test", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC", + "dependencies": { + "bmocha": "^2.1.5", + "bsert": "0.0.10", + "hsd": "^3.0.1" + } +}