From a192655c4e6bf571df68edfa1372e6d44d6de4ed Mon Sep 17 00:00:00 2001 From: phillip-stephens Date: Mon, 12 Aug 2024 11:56:25 -0400 Subject: [PATCH 1/5] stop handling a domain if the nameserver that should provide glue records doesn't --- src/zdns/conf.go | 1 + src/zdns/lookup.go | 15 +++++++++++---- src/zdns/util.go | 7 +++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/zdns/conf.go b/src/zdns/conf.go index c939bcb3..b9fbea1e 100644 --- a/src/zdns/conf.go +++ b/src/zdns/conf.go @@ -51,6 +51,7 @@ const ( StatusTimeout Status = "TIMEOUT" StatusIterTimeout Status = "ITERATIVE_TIMEOUT" StatusNoAuth Status = "NOAUTH" + StatusNoNeededGlue Status = "NONEEDEDGLUE" // When a nameserver is authoritative for itself and the parent nameserver doesn't provide the glue to look it up ) var RootServersV4 = []string{ diff --git a/src/zdns/lookup.go b/src/zdns/lookup.go index bc56e4b9..11203522 100644 --- a/src/zdns/lookup.go +++ b/src/zdns/lookup.go @@ -584,7 +584,6 @@ func (r *Resolver) iterateOnAuthorities(ctx context.Context, q Question, depth i if nsStatus != StatusNoError { var err error newStatus, err := handleStatus(nsStatus, err) - // default case we continue if err == nil { if i+1 == len(result.Authorities) { r.verboseLog((depth + 2), "--> Auth find Failed. Unknown error. No more authorities to try, terminating: ", nsStatus) @@ -608,8 +607,11 @@ func (r *Resolver) iterateOnAuthorities(ctx context.Context, q Question, depth i } } iterateResult, newTrace, status, err := r.iterativeLookup(ctx, q, ns, depth+1, newLayer, newTrace) - if isStatusAnswer(status) { - r.verboseLog((depth + 1), "--> Auth Resolution success: ", status) + if status == StatusNoNeededGlue { + r.verboseLog((depth + 2), "--> Auth resolution of ", ns, " was unsuccessful. No glue to follow", status) + return iterateResult, newTrace, status, err + } else if isStatusAnswer(status) { + r.verboseLog((depth + 1), "--> Auth Resolution of ", ns, " success: ", status) return iterateResult, newTrace, status, err } else if i+1 < len(result.Authorities) { r.verboseLog((depth + 2), "--> Auth resolution of ", ns, " Failed: ", status, ". Will try next authority") @@ -643,6 +645,11 @@ func (r *Resolver) extractAuthority(ctx context.Context, authority interface{}, // that would normally be cache poison. Because it's "ok" and quite common res, status := checkGlue(server, *result, r.ipVersionMode, r.iterationIPPreference) if status != StatusNoError { + if ok, _ = nameIsBeneath(server, layer); ok { + // The domain we're searching for is beneath us but no glue was returned. We cannot proceed without this Glue. + // Terminating + return "", StatusNoNeededGlue, "", trace + } // Fall through to normal query var q Question q.Name = server @@ -654,7 +661,7 @@ func (r *Resolver) extractAuthority(ctx context.Context, authority interface{}, } res, trace, status, _ = r.iterativeLookup(ctx, q, r.randomRootNameServer(), depth+1, ".", trace) } - if status == StatusIterTimeout { + if status == StatusIterTimeout || status == StatusNoNeededGlue { return "", status, "", trace } if status == StatusNoError { diff --git a/src/zdns/util.go b/src/zdns/util.go index ab08b297..3c3a6eb1 100644 --- a/src/zdns/util.go +++ b/src/zdns/util.go @@ -92,7 +92,8 @@ func checkGlueHelper(server, ansType string, result SingleQueryResult) (SingleQu if !ok { continue } - if ans.Type == ansType && strings.TrimSuffix(ans.Name, ".") == server { + // sanitize case and trailing dot + if ans.Type == ansType && strings.ToLower(strings.TrimSuffix(ans.Name, ".")) == strings.ToLower(server) { var retv SingleQueryResult retv.Authorities = make([]interface{}, 0) retv.Answers = make([]interface{}, 0, 1) @@ -150,11 +151,13 @@ func TranslateDNSErrorCode(err int) Status { } // handleStatus is a helper function to deal with a status and error. Error is only returned if the status is an -// Iterative Timeout +// Iterative Timeout or NoNeededGlueRecord func handleStatus(status Status, err error) (Status, error) { switch status { case StatusIterTimeout: return status, err + case StatusNoNeededGlue: + return status, err case StatusNXDomain: return status, nil case StatusServFail: From 41089b6462843a9badab6f9e06dab6951f473754 Mon Sep 17 00:00:00 2001 From: phillip-stephens Date: Mon, 12 Aug 2024 12:35:38 -0400 Subject: [PATCH 2/5] add rfc comment --- src/zdns/util.go | 1 + 1 file changed, 1 insertion(+) diff --git a/src/zdns/util.go b/src/zdns/util.go index 3c3a6eb1..e8852acd 100644 --- a/src/zdns/util.go +++ b/src/zdns/util.go @@ -93,6 +93,7 @@ func checkGlueHelper(server, ansType string, result SingleQueryResult) (SingleQu continue } // sanitize case and trailing dot + // RFC 4343 - states DNS names are case insensitive if ans.Type == ansType && strings.ToLower(strings.TrimSuffix(ans.Name, ".")) == strings.ToLower(server) { var retv SingleQueryResult retv.Authorities = make([]interface{}, 0) From 206a3f41e8e12898dfe8d1c9a0213576be3290d0 Mon Sep 17 00:00:00 2001 From: phillip-stephens Date: Mon, 12 Aug 2024 13:17:55 -0400 Subject: [PATCH 3/5] lint --- src/zdns/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/zdns/util.go b/src/zdns/util.go index e8852acd..3ad98aad 100644 --- a/src/zdns/util.go +++ b/src/zdns/util.go @@ -93,8 +93,8 @@ func checkGlueHelper(server, ansType string, result SingleQueryResult) (SingleQu continue } // sanitize case and trailing dot - // RFC 4343 - states DNS names are case insensitive - if ans.Type == ansType && strings.ToLower(strings.TrimSuffix(ans.Name, ".")) == strings.ToLower(server) { + // RFC 4343 - states DNS names are case-insensitive + if ans.Type == ansType && strings.EqualFold(strings.TrimSuffix(ans.Name, "."), server) { var retv SingleQueryResult retv.Authorities = make([]interface{}, 0) retv.Answers = make([]interface{}, 0, 1) From 05df4741259ec85a12ef561e3394acdb80b0847d Mon Sep 17 00:00:00 2001 From: phillip-stephens Date: Mon, 12 Aug 2024 14:13:25 -0400 Subject: [PATCH 4/5] updated ipv6 integration test --- testing/ipv6_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/ipv6_tests.py b/testing/ipv6_tests.py index 2de75f8c..be4c52fa 100644 --- a/testing/ipv6_tests.py +++ b/testing/ipv6_tests.py @@ -48,7 +48,7 @@ def test_ipv6_unreachable(self): name = "esrg.stanford.edu" cmd, res = self.run_zdns(c, name) # esrg.stanford.edu is hosted on NS's that do not have an IPv6 address. Therefore, this will fail. - self.assertServFail(res, cmd) + self.assertEqual(res["status"], "NONEEDEDGLUE", cmd) def test_ipv6_external_lookup_unreachable_nameserver(self): c = "A --6=true --4=false --name-servers=1.1.1.1" From 9e3a4351a919431ab60fda30c9469368eb9eb7c4 Mon Sep 17 00:00:00 2001 From: phillip-stephens Date: Mon, 12 Aug 2024 14:14:43 -0400 Subject: [PATCH 5/5] update comment --- testing/ipv6_tests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/ipv6_tests.py b/testing/ipv6_tests.py index be4c52fa..650f13d6 100644 --- a/testing/ipv6_tests.py +++ b/testing/ipv6_tests.py @@ -47,7 +47,7 @@ def test_ipv6_unreachable(self): c = "A --iterative --6=true --4=false" name = "esrg.stanford.edu" cmd, res = self.run_zdns(c, name) - # esrg.stanford.edu is hosted on NS's that do not have an IPv6 address. Therefore, this will fail. + # esrg.stanford.edu is hosted on NS's that do not have an IPv6 address. Therefore, the lookup won't get sufficient glue records to resolve the query. self.assertEqual(res["status"], "NONEEDEDGLUE", cmd) def test_ipv6_external_lookup_unreachable_nameserver(self):