140 lines
3.9 KiB
Go
140 lines
3.9 KiB
Go
package resolver_test
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"testing"
|
|
"time"
|
|
|
|
"dns-helper/resolver"
|
|
|
|
"golang.org/x/net/dns/dnsmessage"
|
|
)
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ParallelAFallback tests (T013)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func TestParallelAFallback_FirstSuccessWins(t *testing.T) {
|
|
qname := mustNewName("example.com.")
|
|
|
|
// Server A: returns A records.
|
|
addrA := startFakeDNSMulti(t, func(query []byte) []byte {
|
|
return buildAResponse(queryID(query), qname, [][4]byte{{1, 2, 3, 4}})
|
|
})
|
|
// Server B: returns NXDOMAIN.
|
|
addrB := startFakeDNSMulti(t, func(query []byte) []byte {
|
|
return buildNXDOMAINResponse(queryID(query), qname)
|
|
})
|
|
|
|
ips, err := resolver.ParallelAFallback(context.Background(), io.Discard, []string{addrA, addrB}, "example.com", 2*time.Second)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(ips) == 0 {
|
|
t.Error("expected at least one IP")
|
|
}
|
|
}
|
|
|
|
func TestParallelAFallback_AllNXDOMAIN(t *testing.T) {
|
|
qname := mustNewName("noexist.example.com.")
|
|
|
|
addrA := startFakeDNSMulti(t, func(query []byte) []byte {
|
|
return buildNXDOMAINResponse(queryID(query), qname)
|
|
})
|
|
addrB := startFakeDNSMulti(t, func(query []byte) []byte {
|
|
return buildNXDOMAINResponse(queryID(query), qname)
|
|
})
|
|
|
|
_, err := resolver.ParallelAFallback(context.Background(), io.Discard, []string{addrA, addrB}, "noexist.example.com", 2*time.Second)
|
|
if err == nil {
|
|
t.Fatal("expected error for all-NXDOMAIN responses")
|
|
}
|
|
}
|
|
|
|
func TestParallelAFallback_OneTimeout(t *testing.T) {
|
|
qname := mustNewName("example.com.")
|
|
|
|
silentAddr := startSilentDNS(t)
|
|
responsiveAddr := startFakeDNSMulti(t, func(query []byte) []byte {
|
|
return buildAResponse(queryID(query), qname, [][4]byte{{5, 6, 7, 8}})
|
|
})
|
|
|
|
ips, err := resolver.ParallelAFallback(
|
|
context.Background(),
|
|
io.Discard,
|
|
[]string{silentAddr, responsiveAddr},
|
|
"example.com",
|
|
300*time.Millisecond,
|
|
)
|
|
if err != nil {
|
|
t.Fatalf("expected success from responsive resolver, got error: %v", err)
|
|
}
|
|
if len(ips) == 0 {
|
|
t.Error("expected IPs from responsive resolver")
|
|
}
|
|
}
|
|
|
|
func TestParallelAFallback_AllFail(t *testing.T) {
|
|
silentA := startSilentDNS(t)
|
|
silentB := startSilentDNS(t)
|
|
|
|
_, err := resolver.ParallelAFallback(
|
|
context.Background(),
|
|
io.Discard,
|
|
[]string{silentA, silentB},
|
|
"example.com",
|
|
100*time.Millisecond,
|
|
)
|
|
if err == nil {
|
|
t.Fatal("expected error when all resolvers fail")
|
|
}
|
|
}
|
|
|
|
func TestParallelAFallback_EmptyResolvers(t *testing.T) {
|
|
_, err := resolver.ParallelAFallback(context.Background(), io.Discard, nil, "example.com", time.Second)
|
|
if err == nil {
|
|
t.Fatal("expected error for empty resolver list")
|
|
}
|
|
}
|
|
|
|
func TestParallelAFallback_CNAMEOnlyNoARecord(t *testing.T) {
|
|
// Server returns a CNAME but no A record — queryA will return "no A records" error.
|
|
// ParallelAFallback should return an error.
|
|
qname := mustNewName("www.example.com.")
|
|
cnameTarget := mustNewName("real.example.com.")
|
|
|
|
addrA := startFakeDNSMulti(t, func(query []byte) []byte {
|
|
return buildCNAMEResponse(queryID(query), qname, cnameTarget)
|
|
})
|
|
|
|
_, err := resolver.ParallelAFallback(context.Background(), io.Discard, []string{addrA}, "www.example.com", 2*time.Second)
|
|
if err == nil {
|
|
t.Error("expected error for CNAME-only response (no A records)")
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Helper: build NS NXDOMAIN response (used in other test files)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
func buildNSNXDOMAIN(id uint16, name dnsmessage.Name) []byte {
|
|
msg := dnsmessage.Message{
|
|
Header: dnsmessage.Header{
|
|
ID: id,
|
|
Response: true,
|
|
RCode: dnsmessage.RCodeNameError,
|
|
},
|
|
Questions: []dnsmessage.Question{{
|
|
Name: name,
|
|
Type: dnsmessage.TypeNS,
|
|
Class: dnsmessage.ClassINET,
|
|
}},
|
|
}
|
|
packed, err := msg.Pack()
|
|
if err != nil {
|
|
panic("buildNSNXDOMAIN: pack failed: " + err.Error())
|
|
}
|
|
return packed
|
|
}
|