Phase 4.1: Error handling, edge cases, and robustness (#11)
CI / test (push) Failing after 2m36s
CI / test (push) Failing after 2m36s
This commit was merged in pull request #11.
This commit is contained in:
@@ -16,6 +16,9 @@ const (
|
||||
RespNODATA
|
||||
RespNXDOMAIN
|
||||
RespSERVFAIL
|
||||
RespREFUSED
|
||||
RespNOTIMPL
|
||||
RespCNAMELoop
|
||||
RespError
|
||||
)
|
||||
|
||||
@@ -33,6 +36,12 @@ func (rt ResponseType) String() string {
|
||||
return "nxdomain"
|
||||
case RespSERVFAIL:
|
||||
return "servfail"
|
||||
case RespREFUSED:
|
||||
return "refused"
|
||||
case RespNOTIMPL:
|
||||
return "notimp"
|
||||
case RespCNAMELoop:
|
||||
return "cname_loop"
|
||||
case RespError:
|
||||
return "error"
|
||||
default:
|
||||
@@ -41,11 +50,12 @@ func (rt ResponseType) String() string {
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Referral *Referral
|
||||
Server net.IP
|
||||
Cache *InfoCache
|
||||
Decoded *dns.DecodedResponse
|
||||
Type ResponseType
|
||||
Referral *Referral
|
||||
Server net.IP
|
||||
Cache *InfoCache
|
||||
Decoded *dns.DecodedResponse
|
||||
Type ResponseType
|
||||
ErrorMessage string
|
||||
}
|
||||
|
||||
func NewResponse(ref *Referral, server net.IP, cache *InfoCache) *Response {
|
||||
@@ -59,15 +69,28 @@ func NewResponse(ref *Referral, server net.IP, cache *InfoCache) *Response {
|
||||
func (r *Response) Process(msg *miekgdns.Msg) *Response {
|
||||
if msg == nil {
|
||||
r.Type = RespError
|
||||
r.ErrorMessage = "nil DNS response"
|
||||
return r
|
||||
}
|
||||
|
||||
r.Decoded = dns.DecodeResponse(msg)
|
||||
if r.Decoded == nil {
|
||||
r.Type = RespError
|
||||
r.ErrorMessage = "failed to decode DNS response"
|
||||
return r
|
||||
}
|
||||
|
||||
// Synthesize CNAME from DNAME when the server didn't include a synthesized CNAME record.
|
||||
if len(r.Decoded.CNAMEChain) == 0 && r.Referral != nil && len(r.Decoded.DNAMEMappings) > 0 {
|
||||
for _, dm := range r.Decoded.DNAMEMappings {
|
||||
synthesized := dns.SynthesizeCNAMEFromDNAME(r.Referral.Name, dm.Owner, dm.Target)
|
||||
if synthesized != "" {
|
||||
r.Decoded.CNAMEChain = append(r.Decoded.CNAMEChain, synthesized)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.Type = r.classify()
|
||||
return r
|
||||
}
|
||||
@@ -78,6 +101,10 @@ func (r *Response) classify() ResponseType {
|
||||
return RespNXDOMAIN
|
||||
case dns.ResponseSERVFAIL:
|
||||
return RespSERVFAIL
|
||||
case dns.ResponseREFUSED:
|
||||
return RespREFUSED
|
||||
case dns.ResponseNOTIMPL:
|
||||
return RespNOTIMPL
|
||||
case dns.ResponseAnswer:
|
||||
if len(r.Decoded.CNAMEChain) > 0 && !r.hasFinalAnswer() {
|
||||
return RespCNAMEFollow
|
||||
@@ -94,7 +121,11 @@ func (r *Response) classify() ResponseType {
|
||||
|
||||
func (r *Response) hasFinalAnswer() bool {
|
||||
for _, rr := range r.Decoded.Answers {
|
||||
if _, ok := rr.(*miekgdns.CNAME); ok {
|
||||
switch rr.(type) {
|
||||
case *miekgdns.CNAME, *miekgdns.DNAME, *miekgdns.RRSIG:
|
||||
// CNAME and DNAME are redirect records, not final answers.
|
||||
// RRSIG is a DNSSEC signature record — it covers the CNAME/DNAME
|
||||
// but is not itself the answer to the original question type.
|
||||
continue
|
||||
}
|
||||
return true
|
||||
@@ -200,7 +231,7 @@ func (r *Response) resolveGlue(child *Referral) {
|
||||
|
||||
func (r *Response) IsTerminal() bool {
|
||||
switch r.Type {
|
||||
case RespAnswer, RespNODATA, RespNXDOMAIN, RespSERVFAIL, RespError:
|
||||
case RespAnswer, RespNODATA, RespNXDOMAIN, RespSERVFAIL, RespREFUSED, RespNOTIMPL, RespCNAMELoop, RespError:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user