Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/lazy lookup auth #476

Closed
wants to merge 43 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5bb6d81
refactor: return raw dns response from auxiliary functions
developStorm Oct 9, 2024
5070760
feat: basic, same-level DNSSEC validation
developStorm Oct 10, 2024
2e432e7
fix: newLayer should not be updated if extractAuthority fails
developStorm Oct 23, 2024
b1f0b6b
refactor: extend trace, fix depth for couple places
developStorm Oct 23, 2024
f45eab2
refactor: more comprehensive validation of DNSKEY/RRSIG
developStorm Oct 30, 2024
f276da1
patch: disregard RRSIG in authorities
developStorm Oct 31, 2024
bf9c009
fix: remove depth parameter in lookup function
developStorm Oct 31, 2024
38b23f6
feat: implement DS verification
developStorm Nov 1, 2024
a922e1c
feat: cache DS in referrals
developStorm Nov 1, 2024
2b69182
feat: validate dnssec for referrals
developStorm Nov 1, 2024
ca5a2b5
build(ci): bump to go1.21
developStorm Nov 1, 2024
4d62421
fix: answer section is always validated
developStorm Nov 1, 2024
856f34f
fix: resolve linter issues
developStorm Nov 12, 2024
97d20e5
Merge branch 'main' into feat/basic-dnssec-validation
developStorm Nov 12, 2024
b73a77e
fix: resolve linter issues
developStorm Nov 13, 2024
148eb28
refactor: store current retry in resolver struct
developStorm Nov 13, 2024
7926796
patch: don't modify original res
developStorm Nov 16, 2024
a11ef2f
docs: rrset validation failure
developStorm Nov 16, 2024
2af2290
build(deps): bump root anchors
developStorm Nov 16, 2024
67f28a4
fix: add validity period check for RRSIG
developStorm Nov 16, 2024
cb6dde4
feat: add dnssec result types
developStorm Nov 17, 2024
3561c36
docs: clarify comments for retries and retriesRemaining in Resolver
developStorm Nov 17, 2024
12d8bda
feat: dnssec result field
developStorm Nov 17, 2024
1c8d186
refactor: simplify param list with validator struct
developStorm Nov 17, 2024
cf2a665
refactor: extended DNSSEC result handling
developStorm Nov 17, 2024
d508b85
feat: add JSON tags to DNSSEC types for improved serialization
developStorm Nov 17, 2024
ea51f64
refactor: DNSSEC functions now does not stop resolution
developStorm Nov 18, 2024
21ab2f7
fix: regression in authority caching
developStorm Nov 18, 2024
3126130
feat: add DNSSEC validation as CLI option
developStorm Nov 18, 2024
c0fb013
feat: implement circular query detection
developStorm Nov 18, 2024
8c258a4
chore: suppress some lint warnings
developStorm Nov 18, 2024
f2a9dad
fix: handle DNSSEC insecure and bogus statuses
developStorm Nov 18, 2024
2b074a2
test: add DNSSEC integration tests
developStorm Nov 18, 2024
6b8174b
fix: RRset should be identified by all of name, class and type
developStorm Nov 18, 2024
b213bd8
fix: dedup ds/dnskey
developStorm Nov 18, 2024
a69785b
docs: function comments for dnssec
developStorm Nov 18, 2024
211b9f1
fix: shortcut Insecure if entire answer is unsigned
developStorm Nov 18, 2024
d46c8ce
feat: add dnssec output fields
developStorm Nov 18, 2024
2baa4a4
Revert "test: add DNSSEC integration tests"
developStorm Nov 18, 2024
6721055
test: add DNSSEC integration tests
developStorm Nov 18, 2024
0e0b72c
fix: rrsig error handling and DS validation on DNSKEY response
developStorm Nov 19, 2024
3d7db6f
fix: KSK is a lie :(
developStorm Nov 19, 2024
3c8b204
feat: lazy query A/AAAA of NSes when iterating on authorities
developStorm Nov 19, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.20'
go-version: "1.21"
- name: Build
run: |
go version
Expand All @@ -46,7 +46,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: "1.23"
- name: Other lint
run: |
go install golang.org/x/tools/cmd/goimports@latest
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ result verbosity levels: `short`, `normal` (default), `long`, and `trace`:

Users can also include specific additional fields using the `--include-fields`
flag and specifying a list of fields, e.g., `--include-fields=flags,resolver`.
Additional fields are: class, protocol, ttl, resolver, flags.
Additional fields are: class, protocol, ttl, resolver, flags, dnssec.

Name Server Mode
----------------
Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/zmap/zdns

go 1.20
go 1.21.1

require (
github.com/hashicorp/go-version v1.7.0
Expand All @@ -10,6 +10,7 @@ require (
github.com/schollz/progressbar/v3 v3.15.0
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.9.0
github.com/zmap/go-dns-root-anchors v0.0.0-20241116225636-aa592d6ee59f
github.com/zmap/go-iptree v0.0.0-20210731043055-d4e632617837
github.com/zmap/zcrypto v0.0.0-20240803002437-3a861682ac77
github.com/zmap/zflags v1.4.0-beta.1.0.20200204220219-9d95409821b6
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zmap/dns v1.1.63-zdns1 h1:vaZIXXLZqjmZTGcpu9qPDcunqWfxeRMh0OwLiCxcjsI=
github.com/zmap/dns v1.1.63-zdns1/go.mod h1:L50pXblXGxDFLaon9W4vGSfC1rGIcBL29sS7sNvNKuI=
github.com/zmap/go-dns-root-anchors v0.0.0-20241116225636-aa592d6ee59f h1:XffIjsyncaK1BH7GpceohPRnesxBaO7lduwbXG05ICo=
github.com/zmap/go-dns-root-anchors v0.0.0-20241116225636-aa592d6ee59f/go.mod h1:AkYq/HWOOEFPx6YgVn2oYjaZjLw2R/HV5WUWfJrFQ6s=
github.com/zmap/go-iptree v0.0.0-20210731043055-d4e632617837 h1:DjHnADS2r2zynZ3WkCFAQ+PNYngMSNceRROi0pO6c3M=
github.com/zmap/go-iptree v0.0.0-20210731043055-d4e632617837/go.mod h1:9vp0bxqozzQwcjBwenEXfKVq8+mYbwHkQ1NF9Ap0DMw=
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
Expand Down
3 changes: 2 additions & 1 deletion src/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ type QueryOptions struct {
ClassString string `long:"class" default:"INET" description:"DNS class to query. Options: INET, CSNET, CHAOS, HESIOD, NONE, ANY."`
ClientSubnetString string `long:"client-subnet" description:"Client subnet in CIDR format for EDNS0."`
Dnssec bool `long:"dnssec" description:"Requests DNSSEC records by setting the DNSSEC OK (DO) bit"`
ValidateDNSSEC bool `long:"validate-dnssec" description:"Validate DNSSEC records, only applicable with --iterative"`
UseNSID bool `long:"nsid" description:"Request NSID."`
}

Expand All @@ -90,7 +91,7 @@ type InputOutputOptions struct {
BlacklistFilePath string `long:"blacklist-file" description:"blacklist file for servers to exclude from lookups"`
DNSConfigFilePath string `long:"conf-file" default:"/etc/resolv.conf" description:"config file for DNS servers"`
MultipleModuleConfigFilePath string `short:"c" long:"multi-config-file" description:"config file path for multiple module"`
IncludeInOutput string `long:"include-fields" description:"Comma separated list of fields to additionally output beyond result verbosity. Options: class, protocol, ttl, resolver, flags"`
IncludeInOutput string `long:"include-fields" description:"Comma separated list of fields to additionally output beyond result verbosity. Options: class, protocol, ttl, resolver, flags, dnssec"`
InputFilePath string `short:"f" long:"input-file" default:"-" description:"names to read, defaults to stdin"`
LogFilePath string `long:"log-file" default:"-" description:"where should JSON logs be saved, defaults to stderr"`
MetadataFilePath string `long:"metadata-file" description:"where should JSON metadata be saved, defaults to no metadata output. Use '-' for stderr."`
Expand Down
12 changes: 11 additions & 1 deletion src/cli/worker_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,17 @@ func populateResolverConfig(gc *CLIConf) *zdns.ResolverConfig {
config.MaxDepth = gc.MaxDepth
config.CheckingDisabledBit = gc.CheckingDisabled
config.ShouldRecycleSockets = !gc.DisableRecycleSockets
config.DNSSecEnabled = gc.Dnssec

config.ShouldValidateDNSSEC = gc.ValidateDNSSEC
if config.ShouldValidateDNSSEC {
config.DNSSecEnabled = true
if !gc.IterativeResolution {
log.Fatal("DNSSEC validation is only supported with iterative resolution")
}
} else {
config.DNSSecEnabled = gc.Dnssec
}

config.DNSConfigFilePath = gc.DNSConfigFilePath

config.LogLevel = log.Level(gc.Verbosity)
Expand Down
61 changes: 61 additions & 0 deletions src/zdns/answers.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,21 @@ type DNSKEYAnswer struct {
PublicKey string `json:"public_key" groups:"short,normal,long,trace"`
}

func (r *DNSKEYAnswer) ToVanillaType() *dns.DNSKEY {
return &dns.DNSKEY{
Hdr: dns.RR_Header{
Name: dns.CanonicalName(r.Name),
Rrtype: r.RrType,
Class: dns.StringToClass[r.Class],
Ttl: r.TTL,
},
Flags: r.Flags,
Protocol: r.Protocol,
Algorithm: r.Algorithm,
PublicKey: r.PublicKey,
}
}

type DSAnswer struct {
Answer
KeyTag uint16 `json:"key_tag" groups:"short,normal,long,trace"`
Expand All @@ -86,6 +101,22 @@ type DSAnswer struct {
Digest string `json:"digest" groups:"short,normal,long,trace"`
}

func (r *DSAnswer) ToVanillaType() *dns.DS {
return &dns.DS{
Hdr: dns.RR_Header{

Name: dns.CanonicalName(r.Name),
Rrtype: r.RrType,
Class: dns.StringToClass[r.Class],
Ttl: r.TTL,
},
KeyTag: r.KeyTag,
Algorithm: r.Algorithm,
DigestType: r.DigestType,
Digest: r.Digest,
}
}

type GPOSAnswer struct {
Answer
Longitude string `json:"preference" groups:"short,normal,long,trace"`
Expand Down Expand Up @@ -186,6 +217,36 @@ type RRSIGAnswer struct {
Signature string `json:"signature" groups:"short,normal,long,trace"`
}

func (r *RRSIGAnswer) ToVanillaType() *dns.RRSIG {
expiration, err := dns.StringToTime(r.Expiration)
if err != nil {
panic("failed to parse expiration time: " + r.Expiration)
}

inception, err := dns.StringToTime(r.Inception)
if err != nil {
panic("failed to parse inception time: " + r.Inception)
}

return &dns.RRSIG{
Hdr: dns.RR_Header{
Name: dns.CanonicalName(r.Name),
Rrtype: r.RrType,
Class: dns.StringToClass[r.Class],
Ttl: r.TTL,
},
TypeCovered: r.TypeCovered,
Algorithm: r.Algorithm,
Labels: r.Labels,
OrigTtl: r.OriginalTTL,
Expiration: expiration,
Inception: inception,
KeyTag: r.KeyTag,
SignerName: r.SignerName,
Signature: r.Signature,
}
}

type RPAnswer struct {
Answer
Mbox string `json:"mbox" groups:"short,normal,long,trace"`
Expand Down
33 changes: 32 additions & 1 deletion src/zdns/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,38 @@ func (s *Cache) SafeAddCachedAuthority(res *SingleQueryResult, ns *NameServer, d
nsString = ns.String()
}

cachedRes := s.buildCachedResult(res, depth, layer)
// Referrals may contain DS records in the authority section. These need to be cached under the child name.
delegateToDSRRs := make(map[string][]interface{})
var otherRRs []interface{}
for _, rr := range res.Authorities {
if dsRR, ok := rr.(DSAnswer); ok {
delegateName := removeTrailingDotIfNotRoot(dsRR.BaseAns().Name)
delegateToDSRRs[delegateName] = append(delegateToDSRRs[delegateName], dsRR)
} else {
otherRRs = append(otherRRs, rr)
}
}

if len(delegateToDSRRs) > 0 {
s.VerboseLog(depth+1, "SafeAddCachedAuthority: found DS records in authority section, caching under child names")

for delegateName, dsRRs := range delegateToDSRRs {
dsRes := &SingleQueryResult{
Answers: dsRRs,
Protocol: res.Protocol,
Resolver: res.Resolver,
Flags: res.Flags,
TLSServerHandshake: res.TLSServerHandshake,
}
dsRes.Flags.Authoritative = true
dsCachedRes := s.buildCachedResult(dsRes, depth, layer)
s.addCachedAnswer(Question{Name: delegateName, Type: dns.TypeDS, Class: dns.ClassINET}, nsString, false, dsCachedRes, depth)
}
}

copiedRes := *res
copiedRes.Authorities = otherRRs
cachedRes := s.buildCachedResult(&copiedRes, depth, layer)
if len(cachedRes.Answers) == 0 && len(cachedRes.Authorities) == 0 && len(cachedRes.Additionals) == 0 {
s.VerboseLog(depth+1, "SafeAddCachedAnswer: no cacheable records found, aborting")
return
Expand Down
9 changes: 9 additions & 0 deletions src/zdns/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,17 @@ const (
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
StatusCircular Status = "CIRCULAR" // When circular query dependencies are detected
)

func isStatusRetryable(status Status) bool {
switch status {
case StatusServFail, StatusNXDomain, StatusRefused, StatusTruncated, StatusError, StatusTimeout, StatusIterTimeout:
return true
}
return false
}

var RootServersV4 = []NameServer{
{IP: net.ParseIP("198.41.0.4"), Port: 53}, // A
{IP: net.ParseIP("170.247.170.2"), Port: 53}, // B - Changed several times, this is current as of July '24
Expand Down
Loading