- Exponential backoff retry logic (100ms, 200ms, 400ms... capped at 2s) replacing fixed 100ms delay between retries - Explicit REFUSED and NOTIMP response types (RespREFUSED, RespNOTIMPL) surfaced as terminal results with user-visible messages - CNAME loop detection: walking the ancestor referral chain before following a CNAME prevents infinite recursion; produces RespCNAMELoop - DNAME record support: synthesize CNAME target from DNAME mapping when the server omits the RFC 6672 synthesized CNAME record - ErrorMessage field on Response for surfacing error details to users - Fix resolveGlueViaSystem timeout bug: deadline.Sub(deadline) was always 0; replaced with time.Until(deadline) - DNAME records excluded from hasFinalAnswer so DNAME-only responses are correctly classified as RespCNAMEFollow - Text and JSON output updated with labels for all new response types - Tests: CNAME loop (2-step and direct), REFUSED, NOTIMP, graceful degradation (partial and total server failure), DNAME synthesis, IsNameInChain, backoffDelay, ResponseClassification strings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
Copilot
multica-agent
parent
76f5010a5e
commit
368f200d23
+16
-2
@@ -93,7 +93,7 @@ func QueryWithExchange(ctx context.Context, server net.IP, name string, qtype ui
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, fmt.Errorf("query retries cancelled: %w", ctx.Err())
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
case <-time.After(backoffDelay(attempt)):
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ func IterativeQueryWithExchange(ctx context.Context, server net.IP, name string,
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, fmt.Errorf("query retries cancelled: %w", ctx.Err())
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
case <-time.After(backoffDelay(attempt)):
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +195,20 @@ func IterativeQueryWithExchange(ctx context.Context, server net.IP, name string,
|
||||
return nil, fmt.Errorf("iterative query %s %s failed after %d retries: %w", name, QNameType(qtype), cfg.Retries, lastErr)
|
||||
}
|
||||
|
||||
// backoffDelay computes the wait duration before the given retry attempt (1-indexed).
|
||||
// Delays: attempt=1 → 100ms, attempt=2 → 200ms, attempt=3 → 400ms, capped at 2s.
|
||||
func backoffDelay(attempt int) time.Duration {
|
||||
if attempt <= 0 {
|
||||
return 0
|
||||
}
|
||||
delay := time.Duration(uint(1)<<uint(attempt-1)) * 100 * time.Millisecond
|
||||
const maxDelay = 2 * time.Second
|
||||
if delay > maxDelay {
|
||||
return maxDelay
|
||||
}
|
||||
return delay
|
||||
}
|
||||
|
||||
func buildQuery(name string, qtype uint16, udpSize int) *dns.Msg {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(dns.Fqdn(name), qtype)
|
||||
|
||||
Reference in New Issue
Block a user