mirror of
https://github.com/v2rayA/v2rayA.git
synced 2026-10-29 05:26:43 +08:00
A rule that sends a name to a local resolver which forwards back to v2rayA makes the two exchange the same query until one of them runs out of memory; v2rayA/v2rayA discussion #1950 reproduces it with dnsmasq on OpenWrt. The firewall-level self-redirect is already handled by the 0x80 mark, but nothing saw the loop at the resolver level. Every query this process sends upstream carries a random per-process token in an EDNS0 local option. A query that arrives carrying that token has come back around such a loop and is answered with SERVFAIL, with one log line per ten seconds naming the question and how many were refused.
79 lines
1.9 KiB
Go
79 lines
1.9 KiB
Go
package dns
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/miekg/dns"
|
|
)
|
|
|
|
func TestMarkOutgoingIsRecognisedBack(t *testing.T) {
|
|
msg := new(dns.Msg)
|
|
msg.SetQuestion("example.com.", dns.TypeA)
|
|
if carriesOwnToken(msg) {
|
|
t.Fatal("a fresh query must not look like one of ours")
|
|
}
|
|
markOutgoing(msg)
|
|
if !carriesOwnToken(msg) {
|
|
t.Fatal("a query we marked must be recognised when it comes back")
|
|
}
|
|
if opts := msg.IsEdns0(); opts == nil {
|
|
t.Fatal("marking must leave an OPT record")
|
|
}
|
|
}
|
|
|
|
func TestMarkOutgoingKeepsOneOptRecord(t *testing.T) {
|
|
msg := new(dns.Msg)
|
|
msg.SetQuestion("example.com.", dns.TypeA)
|
|
msg.SetEdns0(4096, true)
|
|
markOutgoing(msg)
|
|
markOutgoing(msg)
|
|
count := 0
|
|
for _, rr := range msg.Extra {
|
|
if _, ok := rr.(*dns.OPT); ok {
|
|
count++
|
|
}
|
|
}
|
|
if count != 1 {
|
|
t.Fatalf("got %d OPT records, want 1 — two make a server answer FORMERR", count)
|
|
}
|
|
opt := msg.IsEdns0()
|
|
marks := 0
|
|
for _, o := range opt.Option {
|
|
if local, ok := o.(*dns.EDNS0_LOCAL); ok && local.Code == loopOptionCode {
|
|
marks++
|
|
}
|
|
}
|
|
if marks != 1 {
|
|
t.Fatalf("got %d loop options, want 1", marks)
|
|
}
|
|
}
|
|
|
|
func TestAnotherInstancesTokenIsNotOurs(t *testing.T) {
|
|
msg := new(dns.Msg)
|
|
msg.SetQuestion("example.com.", dns.TypeA)
|
|
msg.SetEdns0(4096, true)
|
|
opt := msg.IsEdns0()
|
|
opt.Option = append(opt.Option, &dns.EDNS0_LOCAL{
|
|
Code: loopOptionCode,
|
|
Data: []byte("notmine!"),
|
|
})
|
|
if carriesOwnToken(msg) {
|
|
t.Fatal("another instance's token must not count as a loop")
|
|
}
|
|
}
|
|
|
|
func TestOtherEdnsOptionsAreIgnored(t *testing.T) {
|
|
msg := new(dns.Msg)
|
|
msg.SetQuestion("example.com.", dns.TypeA)
|
|
msg.SetEdns0(4096, true)
|
|
opt := msg.IsEdns0()
|
|
opt.Option = append(opt.Option, &dns.EDNS0_SUBNET{Code: dns.EDNS0SUBNET})
|
|
if carriesOwnToken(msg) {
|
|
t.Fatal("an ECS option must not be read as a loop token")
|
|
}
|
|
markOutgoing(msg)
|
|
if !carriesOwnToken(msg) {
|
|
t.Fatal("marking next to other options must still work")
|
|
}
|
|
}
|