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/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 f77fe8a6..c9c852ec 100644 --- a/src/dns.c +++ b/src/dns.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "bio.h" #include "dns.h" @@ -725,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 @@ -2133,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; @@ -2224,15 +2237,59 @@ hsk_dns_name_serialize( int size; 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] == '\\') { + // 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; + } + + // 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; } - size = i - begin; + size = i - begin - size_adjust; if (size > HSK_DNS_MAX_LABEL) { *len = off; @@ -2272,14 +2329,35 @@ hsk_dns_name_serialize( int j; for (j = begin; j < i; j++) { char ch = name[j]; - - // 0xff -> NUL - if (ch == -1) - ch = '\0'; - - // 0xfe -> . - if (ch == -2) - ch = '.'; + // Check for escaped byte codes and process into output result + if (ch == '\\') { + // 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++] = ch; } @@ -2288,7 +2366,10 @@ hsk_dns_name_serialize( } begin = i + 1; + size_adjust = 0; } + + escaped = false; } if (i > HSK_DNS_MAX_NAME) { @@ -2404,26 +2485,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/src/ns.c b/src/ns.c index 1325c590..92256dd7 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 */ @@ -297,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) { @@ -306,10 +300,6 @@ hsk_ns_onrecv( hsk_dns_req_print(req, "ns: "); - uint8_t *wire = NULL; - size_t wire_len = 0; - hsk_dns_msg_t *msg = NULL; - // Hit cache first. msg = hsk_cache_get(&ns->cache, req); @@ -333,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; @@ -342,7 +334,6 @@ hsk_ns_onrecv( hsk_dns_rrs_t *an = &msg->an; hsk_dns_rrs_t *rrns = &msg->ns; - hsk_dns_rrs_t *ar = &msg->ar; uint8_t ip[16]; uint16_t family; @@ -350,10 +341,19 @@ 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); + // 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)) { @@ -372,19 +372,20 @@ hsk_ns_onrecv( } if (!match) { - // Needs SOA. - // TODO: Make the reverse pointers TLDs. - // Empty proof: + char next[HSK_DNS_MAX_NAME] = "\\000."; + strcat(next, req->name); if (family == HSK_DNS_A) { - hsk_resource_to_empty( + hsk_resource_to_nsec( req->name, + next, hsk_type_map_a, sizeof(hsk_type_map_a), rrns ); } else { - hsk_resource_to_empty( + hsk_resource_to_nsec( req->name, + next, hsk_type_map_aaaa, sizeof(hsk_type_map_aaaa), rrns @@ -418,7 +419,7 @@ hsk_ns_onrecv( hsk_dns_rrs_push(an, rr); - hsk_dnssec_sign_zsk(ar, rrtype); + hsk_dnssec_sign_zsk(an, rrtype); } } @@ -434,6 +435,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. @@ -445,7 +468,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; @@ -538,9 +561,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/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 96e2f5fa..7d474d90 100644 --- a/src/resource.c +++ b/src/resource.c @@ -16,13 +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 -}; - /* * Helpers */ @@ -30,6 +23,12 @@ static const uint8_t hsk_type_map[] = { static void to_fqdn(char *name); +static void +next_name(const char *name, char *next); + +static void +prev_name(const char *name, char *prev); + /* * Resource serialization version 0 * Record types: read @@ -757,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); @@ -867,8 +866,9 @@ hsk_resource_root_to_ds(hsk_dns_rrs_t *an) { } bool -hsk_resource_to_empty( +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 @@ -878,13 +878,14 @@ hsk_resource_to_empty( if (!rr) return false; - rr->ttl = 86400; + rr->ttl = HSK_DEFAULT_TTL; hsk_dns_rr_set_name(rr, name); hsk_dns_nsec_rd_t *rd = rr->rd; - strcpy(rd->next_domain, "."); + strcpy(rd->next_domain, next); + rd->type_map = NULL; rd->type_map_len = 0; @@ -907,37 +908,6 @@ hsk_resource_to_empty( 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)); @@ -959,21 +929,59 @@ hsk_resource_to_dns(const hsk_resource_t *rs, const char *name, uint16_t type) { hsk_dns_rrs_t *ns = &msg->ns; // authority hsk_dns_rrs_t *ar = &msg->ar; // additional + // Even though the name here is a single label (the TLD) + // we use a larger buffer size of 255 (instead of 63) + // to allow escaped byte codes like /000 + char next[HSK_DNS_MAX_NAME]; + next_name(tld, next); + // Referral. if (labels > 1) { if (hsk_resource_has_ns(rs)) { 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, + next, + 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, + next, + hsk_type_map_txt, + sizeof(hsk_type_map_txt), + ns + ); + } else { + hsk_resource_to_nsec( + tld, + next, + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + ns + ); + } + + msg->flags |= HSK_DNS_AA; + hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + // Needs SOA. hsk_resource_root_to_soa(ns); hsk_dnssec_sign_zsk(ns, HSK_DNS_SOA); } @@ -984,39 +992,72 @@ 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); - else - hsk_dnssec_sign_zsk(ns, HSK_DNS_DS); + if (!hsk_resource_has(rs, HSK_DS)) { + // No DS proof: + // This allows unbound to treat the zone as unsigned (and not bogus) + hsk_resource_to_nsec( + tld, + next, + 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); + 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, + hsk_type_map_txt, + sizeof(hsk_type_map_txt), + ns + ); + } else { + // Otherwise, we prove there is nothing + hsk_resource_to_nsec( + tld, + next, + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + ns + ); + } 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); } @@ -1077,14 +1118,15 @@ 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. - 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); @@ -1095,7 +1137,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) @@ -1106,15 +1148,43 @@ hsk_resource_to_nx(void) { hsk_dns_rrs_t *ns = &msg->ns; - // NX Proof: - // Just make it look like an - // empty zone for the NX proof. - // It seems to fool unbound without - // breaking anything. - hsk_resource_root_to_nsec(ns); - hsk_resource_root_to_nsec(ns); + // Prove the wildcard doesn't exist + hsk_resource_to_nsec( + "!.", + "+.", + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + ns + ); + // Sign RR set with name `!.` hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + // Pop the NSEC and RRSIG out of the RR set... + hsk_dns_rr_t *rr1 = hsk_dns_rrs_pop(ns); + hsk_dns_rr_t *rr2 = hsk_dns_rrs_pop(ns); + + // Prove the name doesn't exist. + // Even though the name here is a single label (the TLD) + // we use a larger buffer size of 255 (instead of 63) + // to allow escaped byte codes like /000 + char next[HSK_DNS_MAX_NAME]; + char prev[HSK_DNS_MAX_NAME]; + next_name(tld, next); + prev_name(tld, prev); + hsk_resource_to_nsec( + prev, + next, + hsk_type_map_empty, + sizeof(hsk_type_map_empty), + ns + ); + // Sign RR set with name `prev` + hsk_dnssec_sign_zsk(ns, HSK_DNS_NSEC); + + // ...now push the first two RRs back in + hsk_dns_rrs_push(ns, rr2); + hsk_dns_rrs_push(ns, rr1); + hsk_resource_root_to_soa(ns); hsk_dnssec_sign_zsk(ns, HSK_DNS_SOA); @@ -1133,6 +1203,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(); @@ -1157,6 +1239,36 @@ to_fqdn(char *name) { name[len + 1] = '\0'; } +static void +next_name(const char *name, char *next) { + size_t len = strlen(name); + if (name[len - 1] == '.') + len--; + + strcpy(next, name); + + if (len < 63) { + memcpy(&next[len], "\\000.", 6); + } else { + next[len - 1]++; + memcpy(&next[len], ".", 2); + } +} + +static void +prev_name(const char *name, char *prev) { + size_t len = strlen(name); + if (name[len - 1] == '.') + len--; + + strcpy(prev, name); + prev[len - 1]--; + + if (len < 63) { + memcpy(&prev[len], "\\255.", 6); + } +} + bool pointer_to_ip(const char *name, uint8_t *ip, uint16_t *family) { char label[HSK_DNS_MAX_LABEL + 1]; diff --git a/src/resource.h b/src/resource.h index 46e73b1f..1384fe7e 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, 0x08, 0x00, 0x03 +}; + +// NS SOA RRSIG NSEC DNSKEY +static const uint8_t hsk_type_map_root[] = { + 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; @@ -83,11 +114,14 @@ 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); +hsk_dns_msg_t * +hsk_resource_to_refused(void); + hsk_dns_msg_t * hsk_resource_to_notimp(void); @@ -95,8 +129,9 @@ bool hsk_resource_is_ptr(const char *name); bool -hsk_resource_to_empty( +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 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. diff --git a/test/data/name_serialization_vectors.h b/test/data/name_serialization_vectors.h new file mode 100644 index 00000000..f502a76b --- /dev/null +++ b/test/data/name_serialization_vectors.h @@ -0,0 +1,159 @@ +/* + * Types + */ + +typedef struct name_serializtion_vector { + char *name; + uint8_t expected_data[24]; + size_t expected_len; + bool success; + char *parsed; +} name_serializtion_vector_t; + + +/* + * Vectors + */ + +static const name_serializtion_vector_t name_serializtion_vectors[] = { + { + "abcdef.", + { + 0x06, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00 + }, + 8, + true, + "abcdef." + }, + { + "abc.def.", + { + 0x03, 0x61, 0x62, 0x63, 0x03, 0x64, 0x65, 0x66, 0x00 + }, + 9, + true, + "abc.def." + }, + { + "abcdef\\000.", + { + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00, 0x00 + }, + 9, + true, + "abcdef\\000." + }, + { + "abcdef\\255.", + { + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0xff, 0x00 + }, + 9, + true, + "abcdef\\255." + }, + { + "abcdef\\256.", + {}, + 0, + false, // bad escape (value > 0xff) + NULL + }, + { + "abcdef\\LOL.", + { + 0x09, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x4c, 0x4f, 0x4c, 0x00 + }, + 11, + true, + "abcdefLOL." + }, + { + "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, // bad escape (value > 0xff) + NULL + }, + { + "\\\\999.", + { + 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, + "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/data/resource_vectors.h b/test/data/resource_vectors.h new file mode 100644 index 00000000..53f4433e --- /dev/null +++ b/test/data/resource_vectors.h @@ -0,0 +1,338 @@ +#include "resource.h" + +/* + * Types + */ + +typedef struct type_vector { + uint16_t type; + char *type_string; + uint8_t an_size; + uint8_t ns_size; + uint8_t ar_size; + bool nsec; + bool aa; +} type_vector_t; + +typedef struct resource_vector { + char *name; + uint8_t data[255]; + uint8_t data_len; + type_vector_t type_vectors[4]; + // Expected in NSEC records + size_t type_map_len; + const uint8_t *type_map; +} resource_vector_t; + +/* + * Vectors + */ + +static const resource_vector_t resource_vectors[] = { + // { + // "records": [ + // { + // "type": "SYNTH4", + // "address": "50.60.70.80" + // } + // ] + // } + { + "test-synth4.", + { + 0x00, 0x04, 0x32, 0x3c, 0x46, 0x50 + }, + 6, + { + {HSK_DNS_DS, "DS", 0, 4, 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 + }, + + // { + // "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, 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 + }, + + // { + // "records": [ + // { + // "type": "GLUE4", + // "ns": "ns2.hns.", + // "address": "10.20.30.40" + // } + // ] + // } + { + "test-glue4.", + { + 0x00, 0x02, 0x03, 0x6e, 0x73, 0x32, 0x03, 0x68, 0x6e, 0x73, 0x00, 0x0a, + 0x14, 0x1e, 0x28 + }, + 15, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true, 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 + }, + + // { + // "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, 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 + }, + + // { + // "records": [ + // { + // "type": "GLUE6", + // "ns": "ns2.hns.", + // "address": "1111:2222:3333:4444:5555:6666:7777:8888" + // } + // ] + // } + { + "test-glue6.", + { + 0x00, 0x03, 0x03, 0x6e, 0x73, 0x32, 0x03, 0x68, 0x6e, 0x73, 0x00, 0x11, + 0x11, 0x22, 0x22, 0x33, 0x33, 0x44, 0x44, 0x55, 0x55, 0x66, 0x66, 0x77, + 0x77, 0x88, 0x88 + }, + 27, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true, 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 + }, + + // { + // "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, 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 + }, + + // { + // "records": [ + // { + // "type": "NS", + // "ns": "ns1.hns." + // } + // ] + // } + { + "test-ns.", + { + 0x00, 0x01, 0x03, 0x6e, 0x73, 0x31, 0x03, 0x68, 0x6e, 0x73, 0x00 + }, + 11, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true, 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 + }, + + // { + // "records": [ + // { + // "type": "NS", + // "ns": "ns1.test-ns-glue." + // } + // ] + // } + { + "test-ns-glue.", + { + 0x00, 0x01, 0x03, 0x6e, 0x73, 0x31, 0x0c, 0x74, 0x65, 0x73, 0x74, 0x2d, + 0x6e, 0x73, 0x2d, 0x67, 0x6c, 0x75, 0x65, 0x00 + }, + 20, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true, 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 + }, + + // { + // "records": [ + // { + // "type": "DS", + // "keyTag": 57355, + // "algorithm": 8, + // "digestType": 2, + // "digest": "95a57c3bab7849dbcddf7c72ada71a88146b141110318ca5be672057e865c3e2" + // } + // ] + // } + { + "test-ds.", + { + 0x00, 0x00, 0xe0, 0x0b, 0x08, 0x02, 0x20, 0x95, 0xa5, 0x7c, 0x3b, 0xab, + 0x78, 0x49, 0xdb, 0xcd, 0xdf, 0x7c, 0x72, 0xad, 0xa7, 0x1a, 0x88, 0x14, + 0x6b, 0x14, 0x11, 0x10, 0x31, 0x8c, 0xa5, 0xbe, 0x67, 0x20, 0x57, 0xe8, + 0x65, 0xc3, 0xe2 + }, + 39, + { + {HSK_DNS_DS, "DS", 2, 0, 0, false, 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 + }, + + // { + // "records": [ + // { + // "type": "TXT", + // "txt": [ + // "hello world", + // "how are you" + // ] + // } + // ] + // } + { + "test-txt.", + { + 0x00, 0x06, 0x02, 0x0b, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, + 0x72, 0x6c, 0x64, 0x0b, 0x68, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, + 0x79, 0x6f, 0x75 + }, + 27, + { + {HSK_DNS_DS, "DS", 0, 4, 0, true, 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 + }, + + // { + // "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 7ec7b071..aad82a1d 100644 --- a/test/hnsd-test.c +++ b/test/hnsd-test.c @@ -2,17 +2,33 @@ #include "base32.h" #include "resource.h" #include "resource.c" +#include "dns.h" +#include "dns.c" +#include "data/name_serialization_vectors.h" +#include "data/resource_vectors.h" + +#define ARRAY_SIZE(x) ((sizeof(x))/(sizeof(x[0]))) + +/** + * 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 +45,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 +78,206 @@ test_pointer_to_ip() { assert(family6 == HSK_DNS_AAAA); } +void +test_name_serialize() { + printf("test_name_serialize\n"); + + 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}; + int len = 0; + + printf(" %s\n", name_serializtion_vector.name); + + bool success = hsk_dns_name_serialize( + name_serializtion_vector.name, + data, + &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"); + + for (int i = 0; i < ARRAY_SIZE(resource_vectors); i++) { + resource_vector_t resource_vector = resource_vectors[i]; + + hsk_resource_t *res = NULL; + hsk_resource_decode( + resource_vector.data, + resource_vector.data_len, + &res + ); + + for (int t = 0; t < ARRAY_SIZE(resource_vector.type_vectors); t++) { + type_vector_t type_vector = resource_vector.type_vectors[t]; + + hsk_dns_msg_t *msg = NULL; + msg = hsk_resource_to_dns(res, resource_vector.name, type_vector.type); + + printf(" %s %s \n", resource_vector.name, type_vector.type_string); + assert(msg->an.size == type_vector.an_size); + assert(msg->ns.size == type_vector.ns_size); + assert(msg->ar.size == type_vector.ar_size); + + // Check `aa` bit + assert((bool)(msg->flags & HSK_DNS_AA) == type_vector.aa); + + // Sanity check: NSEC never appears in ANSWER or ADDITIONAL + for (int i = 0; i < msg->an.size; i++) { + hsk_dns_rr_t *rr = msg->an.items[i]; + assert(rr->type != HSK_DNS_NSEC); + } + for (int i = 0; i < msg->ar.size; i++) { + hsk_dns_rr_t *rr = msg->ar.items[i]; + assert(rr->type != HSK_DNS_NSEC); + } + + // Check NSEC in AUTHORITY when appropriate and verify type map + for (int i = 0; i < msg->ns.size; i++) { + hsk_dns_rr_t *rr = msg->ns.items[i]; + if (rr->type != HSK_DNS_NSEC) + continue; + + // NSEC is expected + assert(type_vector.nsec); + + // Type map is correct + hsk_dns_nsec_rd_t *rd = rr->rd; + assert(resource_vector.type_map_len == rd->type_map_len); + assert(memcmp( + resource_vector.type_map, + rd->type_map, + rd->type_map_len + ) == 0); + } + + } + } +} + +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); +} + +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 + */ + int main() { printf("Testing hnsd...\n"); test_base32(); test_pointer_to_ip(); + test_name_serialize(); + test_name_parse(); + test_decode_resource(); + test_next_name(); + test_prev_name(); + test_verify_name(); printf("ok\n"); 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" + } +}