Skip to content

Commit

Permalink
net: use the extended RCode from EDNS(0) OPT resources
Browse files Browse the repository at this point in the history
For a while now we support EDNS, but the current
implementation only sends the OPT resource and doesn't
do anything with the response OPT resource.

For reference the miekg/dns updates the RCode in the
header when there is a OPT resource:
https://github.com/miekg/dns/blob/48f38ebef989eedc6b57f1869ae849ccc8f5fe29/msg.go#L868-L872

Change-Id: I0a7146aed3e50654f340a3925f48612561cb85f4
GitHub-Last-Rev: adc3041
GitHub-Pull-Request: #61695
Reviewed-on: https://go-review.googlesource.com/c/go/+/514835
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
  • Loading branch information
mateusz834 authored and gopherbot committed Aug 3, 2023
1 parent 3ca90ed commit 8657603
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 4 deletions.
27 changes: 23 additions & 4 deletions src/net/dnsclient_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ func (r *Resolver) exchange(ctx context.Context, server string, q dnsmessage.Que

// checkHeader performs basic sanity checks on the header.
func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error {
if h.RCode == dnsmessage.RCodeNameError {
rcode := extractExtendedRCode(*p, h)

if rcode == dnsmessage.RCodeNameError {
return errNoSuchHost
}

Expand All @@ -216,17 +218,17 @@ func checkHeader(p *dnsmessage.Parser, h dnsmessage.Header) error {

// libresolv continues to the next server when it receives
// an invalid referral response. See golang.org/issue/15434.
if h.RCode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone {
if rcode == dnsmessage.RCodeSuccess && !h.Authoritative && !h.RecursionAvailable && err == dnsmessage.ErrSectionDone {
return errLameReferral
}

if h.RCode != dnsmessage.RCodeSuccess && h.RCode != dnsmessage.RCodeNameError {
if rcode != dnsmessage.RCodeSuccess && rcode != dnsmessage.RCodeNameError {
// None of the error codes make sense
// for the query we sent. If we didn't get
// a name error and we didn't get success,
// the server is behaving incorrectly or
// having temporary trouble.
if h.RCode == dnsmessage.RCodeServerFailure {
if rcode == dnsmessage.RCodeServerFailure {
return errServerTemporarilyMisbehaving
}
return errServerMisbehaving
Expand All @@ -253,6 +255,23 @@ func skipToAnswer(p *dnsmessage.Parser, qtype dnsmessage.Type) error {
}
}

// extractExtendedRCode extracts the extended RCode from the OPT resource (EDNS(0))
// If an OPT record is not found, the RCode from the hdr is returned.
func extractExtendedRCode(p dnsmessage.Parser, hdr dnsmessage.Header) dnsmessage.RCode {
p.SkipAllAnswers()
p.SkipAllAuthorities()
for {
ahdr, err := p.AdditionalHeader()
if err != nil {
return hdr.RCode
}
if ahdr.Type == dnsmessage.TypeOPT {
return ahdr.ExtendedRCode(hdr.RCode)
}
p.SkipAdditional()
}
}

// Do a lookup for a single name, which must be rooted
// (otherwise answer will not find the answers).
func (r *Resolver) tryOneName(ctx context.Context, cfg *dnsConfig, name string, qtype dnsmessage.Type) (dnsmessage.Parser, string, error) {
Expand Down
31 changes: 31 additions & 0 deletions src/net/dnsclient_unix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2598,3 +2598,34 @@ func TestLookupOrderFilesNoSuchHost(t *testing.T) {
}
}
}

func TestExtendedRCode(t *testing.T) {
fake := fakeDNSServer{
rh: func(_, _ string, q dnsmessage.Message, _ time.Time) (dnsmessage.Message, error) {
fraudSuccessCode := dnsmessage.RCodeSuccess | 1<<10

var edns0Hdr dnsmessage.ResourceHeader
edns0Hdr.SetEDNS0(maxDNSPacketSize, fraudSuccessCode, false)

return dnsmessage.Message{
Header: dnsmessage.Header{
ID: q.Header.ID,
Response: true,
RCode: fraudSuccessCode,
},
Questions: []dnsmessage.Question{q.Questions[0]},
Additionals: []dnsmessage.Resource{{
Header: edns0Hdr,
Body: &dnsmessage.OPTResource{},
}},
}, nil
},
}

r := &Resolver{PreferGo: true, Dial: fake.DialContext}
_, _, err := r.tryOneName(context.Background(), getSystemDNSConfig(), "go.dev.", dnsmessage.TypeA)
var dnsErr *DNSError
if !(errors.As(err, &dnsErr) && dnsErr.Err == errServerMisbehaving.Error()) {
t.Fatalf("r.tryOneName(): unexpected error: %v", err)
}
}

0 comments on commit 8657603

Please sign in to comment.