|
9 | 9 | "github.com/xtls/xray-core/common/buf" |
10 | 10 | "github.com/xtls/xray-core/common/net" |
11 | 11 | "github.com/xtls/xray-core/common/utils" |
| 12 | + feature_dns "github.com/xtls/xray-core/features/dns" |
12 | 13 | "github.com/xtls/xray-core/transport/internet" |
13 | 14 | ) |
14 | 15 |
|
@@ -45,6 +46,33 @@ func (c *capturePacketConn) SetWriteDeadline(time.Time) error { |
45 | 46 | return nil |
46 | 47 | } |
47 | 48 |
|
| 49 | +type recordingDNSClient struct { |
| 50 | + options []feature_dns.IPOption |
| 51 | +} |
| 52 | + |
| 53 | +func (c *recordingDNSClient) Type() interface{} { |
| 54 | + return feature_dns.ClientType() |
| 55 | +} |
| 56 | + |
| 57 | +func (c *recordingDNSClient) Start() error { |
| 58 | + return nil |
| 59 | +} |
| 60 | + |
| 61 | +func (c *recordingDNSClient) Close() error { |
| 62 | + return nil |
| 63 | +} |
| 64 | + |
| 65 | +func (c *recordingDNSClient) LookupIP(_ string, option feature_dns.IPOption) ([]net.IP, uint32, error) { |
| 66 | + c.options = append(c.options, option) |
| 67 | + if option.IPv4Enable { |
| 68 | + return []net.IP{gonet.IPv4(162, 159, 36, 1)}, 0, nil |
| 69 | + } |
| 70 | + if option.IPv6Enable { |
| 71 | + return []net.IP{gonet.ParseIP("2606:4700:4700::1111")}, 0, nil |
| 72 | + } |
| 73 | + return nil, 0, feature_dns.ErrEmptyResponse |
| 74 | +} |
| 75 | + |
48 | 76 | func TestUDPConnClientWriteMultiBufferResolvesPacketDestination(t *testing.T) { |
49 | 77 | packetConn := &capturePacketConn{} |
50 | 78 | conn := &udpConnClient{ |
@@ -143,3 +171,56 @@ func TestUDPConnClientResolveDestinationCachesDomainsCaseInsensitive(t *testing. |
143 | 171 | t.Fatalf("unexpected cached addresses: %s != %s", first.Address, second.Address) |
144 | 172 | } |
145 | 173 | } |
| 174 | + |
| 175 | +func TestUDPConnClientResolveDestinationUsesSeededDomainCache(t *testing.T) { |
| 176 | + lookupCalls := 0 |
| 177 | + cache := utils.NewTypedSyncMap[string, net.Address]() |
| 178 | + cache.Store("dns.cloudflare.internal", net.IPAddress([]byte{162, 159, 36, 1})) |
| 179 | + conn := &udpConnClient{ |
| 180 | + resolvedUDPAddr: cache, |
| 181 | + lookupIP: func(domain string) (net.Address, error) { |
| 182 | + lookupCalls++ |
| 183 | + return net.IPAddress([]byte{162, 159, 46, 1}), nil |
| 184 | + }, |
| 185 | + } |
| 186 | + |
| 187 | + resolved, err := conn.resolveDestination(net.UDPDestination(net.DomainAddress("DNS.Cloudflare.Internal"), net.Port(53))) |
| 188 | + if err != nil { |
| 189 | + t.Fatal(err) |
| 190 | + } |
| 191 | + |
| 192 | + if lookupCalls != 0 { |
| 193 | + t.Fatalf("unexpected lookup calls: %d", lookupCalls) |
| 194 | + } |
| 195 | + if resolved.Address.String() != "162.159.36.1" { |
| 196 | + t.Fatalf("unexpected seeded address: %s", resolved.Address) |
| 197 | + } |
| 198 | +} |
| 199 | + |
| 200 | +func TestHandlerLookupIPForFamilyConstrainsLookupOption(t *testing.T) { |
| 201 | + client := &recordingDNSClient{} |
| 202 | + handler := &Handler{dns: client} |
| 203 | + |
| 204 | + ipv4, err := handler.lookupIPForFamily("dns.cloudflare.internal", net.AddressFamilyIPv4) |
| 205 | + if err != nil { |
| 206 | + t.Fatal(err) |
| 207 | + } |
| 208 | + if !ipv4.Family().IsIPv4() { |
| 209 | + t.Fatalf("unexpected IPv4 lookup result: %s", ipv4) |
| 210 | + } |
| 211 | + if len(client.options) != 1 || !client.options[0].IPv4Enable || client.options[0].IPv6Enable { |
| 212 | + t.Fatalf("unexpected IPv4 lookup options: %+v", client.options) |
| 213 | + } |
| 214 | + |
| 215 | + client.options = nil |
| 216 | + ipv6, err := handler.lookupIPForFamily("dns.cloudflare.internal", net.AddressFamilyIPv6) |
| 217 | + if err != nil { |
| 218 | + t.Fatal(err) |
| 219 | + } |
| 220 | + if !ipv6.Family().IsIPv6() { |
| 221 | + t.Fatalf("unexpected IPv6 lookup result: %s", ipv6) |
| 222 | + } |
| 223 | + if len(client.options) != 1 || client.options[0].IPv4Enable || !client.options[0].IPv6Enable { |
| 224 | + t.Fatalf("unexpected IPv6 lookup options: %+v", client.options) |
| 225 | + } |
| 226 | +} |
0 commit comments