Compare commits
4 Commits
fix/issue-
...
70fac87254
| Author | SHA1 | Date | |
|---|---|---|---|
| 70fac87254 | |||
| 940f7c89da | |||
| 7d380aafa4 | |||
|
|
d0220e5814 |
@@ -4,11 +4,6 @@ import "errors"
|
|||||||
|
|
||||||
// Sentinel errors returned by the resolver.
|
// Sentinel errors returned by the resolver.
|
||||||
var (
|
var (
|
||||||
// ErrNotImplemented indicates a method is stubbed out.
|
|
||||||
ErrNotImplemented = errors.New(
|
|
||||||
"resolver not yet implemented",
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrNoNameservers is returned when no authoritative NS
|
// ErrNoNameservers is returned when no authoritative NS
|
||||||
// could be discovered for a domain.
|
// could be discovered for a domain.
|
||||||
ErrNoNameservers = errors.New(
|
ErrNoNameservers = errors.New(
|
||||||
@@ -24,8 +19,4 @@ var (
|
|||||||
// ErrContextCanceled wraps context cancellation for the
|
// ErrContextCanceled wraps context cancellation for the
|
||||||
// resolver's iterative queries.
|
// resolver's iterative queries.
|
||||||
ErrContextCanceled = errors.New("context canceled")
|
ErrContextCanceled = errors.New("context canceled")
|
||||||
|
|
||||||
// ErrSERVFAIL is returned when a DNS server responds with
|
|
||||||
// SERVFAIL after all retries are exhausted.
|
|
||||||
ErrSERVFAIL = errors.New("SERVFAIL from server")
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -459,15 +459,9 @@ func (r *Resolver) queryAllTypes(
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
singleTypeMaxRetries = 3
|
|
||||||
singleTypeInitialBackoff = 100 * time.Millisecond
|
|
||||||
)
|
|
||||||
|
|
||||||
type queryState struct {
|
type queryState struct {
|
||||||
gotNXDomain bool
|
gotNXDomain bool
|
||||||
gotSERVFAIL bool
|
gotSERVFAIL bool
|
||||||
gotTimeout bool
|
|
||||||
hasRecords bool
|
hasRecords bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -495,21 +489,6 @@ func (r *Resolver) queryEachType(
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
// isTimeout checks whether an error represents a DNS timeout.
|
|
||||||
func isTimeout(err error) bool {
|
|
||||||
if err == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var netErr net.Error
|
|
||||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also catch i/o timeout strings from the dns library.
|
|
||||||
return strings.Contains(err.Error(), "i/o timeout")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resolver) querySingleType(
|
func (r *Resolver) querySingleType(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
nsIP string,
|
nsIP string,
|
||||||
@@ -518,95 +497,19 @@ func (r *Resolver) querySingleType(
|
|||||||
resp *NameserverResponse,
|
resp *NameserverResponse,
|
||||||
state *queryState,
|
state *queryState,
|
||||||
) {
|
) {
|
||||||
msg, lastErr := r.querySingleTypeWithRetry(
|
msg, err := r.queryDNS(ctx, nsIP, hostname, qtype)
|
||||||
ctx, nsIP, hostname, qtype,
|
if err != nil {
|
||||||
)
|
|
||||||
if msg == nil {
|
|
||||||
r.recordRetryFailure(lastErr, state)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
r.handleDNSResponse(msg, resp, state)
|
if msg.Rcode == dns.RcodeNameError {
|
||||||
}
|
state.gotNXDomain = true
|
||||||
|
|
||||||
func (r *Resolver) querySingleTypeWithRetry(
|
return
|
||||||
ctx context.Context,
|
|
||||||
nsIP string,
|
|
||||||
hostname string,
|
|
||||||
qtype uint16,
|
|
||||||
) (*dns.Msg, error) {
|
|
||||||
var lastErr error
|
|
||||||
|
|
||||||
backoff := singleTypeInitialBackoff
|
|
||||||
|
|
||||||
for attempt := range singleTypeMaxRetries {
|
|
||||||
if checkCtx(ctx) != nil {
|
|
||||||
return nil, ErrContextCanceled
|
|
||||||
}
|
|
||||||
|
|
||||||
if attempt > 0 {
|
|
||||||
if !waitBackoff(ctx, backoff) {
|
|
||||||
return nil, ErrContextCanceled
|
|
||||||
}
|
|
||||||
|
|
||||||
backoff *= timeoutMultiplier
|
|
||||||
}
|
|
||||||
|
|
||||||
msg, err := r.queryDNS(ctx, nsIP, hostname, qtype)
|
|
||||||
if err != nil {
|
|
||||||
lastErr = err
|
|
||||||
|
|
||||||
if !isTimeout(err) {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg.Rcode == dns.RcodeServerFailure {
|
if msg.Rcode == dns.RcodeServerFailure {
|
||||||
lastErr = ErrSERVFAIL
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, lastErr
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitBackoff(ctx context.Context, d time.Duration) bool {
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return false
|
|
||||||
case <-time.After(d):
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resolver) recordRetryFailure(
|
|
||||||
lastErr error,
|
|
||||||
state *queryState,
|
|
||||||
) {
|
|
||||||
if lastErr == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if isTimeout(lastErr) {
|
|
||||||
state.gotTimeout = true
|
|
||||||
} else if errors.Is(lastErr, ErrSERVFAIL) {
|
|
||||||
state.gotSERVFAIL = true
|
state.gotSERVFAIL = true
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *Resolver) handleDNSResponse(
|
|
||||||
msg *dns.Msg,
|
|
||||||
resp *NameserverResponse,
|
|
||||||
state *queryState,
|
|
||||||
) {
|
|
||||||
if msg.Rcode == dns.RcodeNameError {
|
|
||||||
state.gotNXDomain = true
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -637,12 +540,8 @@ func classifyResponse(resp *NameserverResponse, state queryState) {
|
|||||||
switch {
|
switch {
|
||||||
case state.gotNXDomain && !state.hasRecords:
|
case state.gotNXDomain && !state.hasRecords:
|
||||||
resp.Status = StatusNXDomain
|
resp.Status = StatusNXDomain
|
||||||
case state.gotTimeout && !state.hasRecords:
|
|
||||||
resp.Status = StatusTimeout
|
|
||||||
resp.Error = "all queries timed out after retries"
|
|
||||||
case state.gotSERVFAIL && !state.hasRecords:
|
case state.gotSERVFAIL && !state.hasRecords:
|
||||||
resp.Status = StatusError
|
resp.Status = StatusError
|
||||||
resp.Error = "server failure (SERVFAIL) after retries"
|
|
||||||
case !state.hasRecords && !state.gotNXDomain:
|
case !state.hasRecords && !state.gotNXDomain:
|
||||||
resp.Status = StatusNoData
|
resp.Status = StatusNoData
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ const (
|
|||||||
StatusError = "error"
|
StatusError = "error"
|
||||||
StatusNXDomain = "nxdomain"
|
StatusNXDomain = "nxdomain"
|
||||||
StatusNoData = "nodata"
|
StatusNoData = "nodata"
|
||||||
StatusTimeout = "timeout"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxCNAMEDepth is the maximum CNAME chain depth to follow.
|
// MaxCNAMEDepth is the maximum CNAME chain depth to follow.
|
||||||
|
|||||||
Reference in New Issue
Block a user