1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2025-01-29 04:37:06 -05:00

Feat: routing and freedom outbound ignore Fake DNS (#696)

Turn off fake DNS for request sent from Routing and Freedom outbound.
Fake DNS now only apply to DNS outbound.
This is important for Android, where VPN service take over all system DNS
traffic and pass it to core.  "UseIp" option can be used in Freedom outbound
to avoid getting fake IP and fail connection.

Co-authored-by: loyalsoldier <10487845+Loyalsoldier@users.noreply.github.com>
This commit is contained in:
yuhan6665 2021-02-22 21:17:20 -05:00 committed by GitHub
parent 232ba8c26f
commit afb8385a7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 252 additions and 181 deletions

View File

@ -8,6 +8,7 @@ package dns
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"sync" "sync"
"github.com/v2fly/v2ray-core/v4/app/router" "github.com/v2fly/v2ray-core/v4/app/router"
@ -105,6 +106,7 @@ func New(ctx context.Context, config *Config) (*DNS, error) {
clients = append(clients, client) clients = append(clients, client)
} }
// If there is no DNS client in config, add a `localhost` DNS client
if len(clients) == 0 { if len(clients) == 0 {
clients = append(clients, NewLocalDNSClient()) clients = append(clients, NewLocalDNSClient())
} }
@ -141,36 +143,13 @@ func (s *DNS) IsOwnLink(ctx context.Context) bool {
} }
// LookupIP implements dns.Client. // LookupIP implements dns.Client.
func (s *DNS) LookupIP(domain string) ([]net.IP, error) { func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) {
return s.lookupIPInternal(domain, IPOption{
IPv4Enable: true,
IPv6Enable: true,
})
}
// LookupIPv4 implements dns.IPv4Lookup.
func (s *DNS) LookupIPv4(domain string) ([]net.IP, error) {
return s.lookupIPInternal(domain, IPOption{
IPv4Enable: true,
IPv6Enable: false,
})
}
// LookupIPv6 implements dns.IPv6Lookup.
func (s *DNS) LookupIPv6(domain string) ([]net.IP, error) {
return s.lookupIPInternal(domain, IPOption{
IPv4Enable: false,
IPv6Enable: true,
})
}
func (s *DNS) lookupIPInternal(domain string, option IPOption) ([]net.IP, error) {
if domain == "" { if domain == "" {
return nil, newError("empty domain name") return nil, newError("empty domain name")
} }
// Normalize the FQDN form query // Normalize the FQDN form query
if domain[len(domain)-1] == '.' { if strings.HasSuffix(domain, ".") {
domain = domain[:len(domain)-1] domain = domain[:len(domain)-1]
} }
@ -192,6 +171,10 @@ func (s *DNS) lookupIPInternal(domain string, option IPOption) ([]net.IP, error)
errs := []error{} errs := []error{}
ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag}) ctx := session.ContextWithInbound(s.ctx, &session.Inbound{Tag: s.tag})
for _, client := range s.sortClients(domain) { for _, client := range s.sortClients(domain) {
if !option.FakeEnable && strings.EqualFold(client.Name(), "FakeDNS") {
newError("skip DNS resolution for domain ", domain, " at server ", client.Name()).AtDebug().WriteToLog()
continue
}
ips, err := client.QueryIP(ctx, domain, option) ips, err := client.QueryIP(ctx, domain, option)
if len(ips) > 0 { if len(ips) > 0 {
return ips, nil return ips, nil

View File

@ -154,7 +154,11 @@ func TestUDPServerSubnet(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
ips, err := client.LookupIP("google.com") ips, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -209,7 +213,11 @@ func TestUDPServer(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
{ {
ips, err := client.LookupIP("google.com") ips, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -220,7 +228,11 @@ func TestUDPServer(t *testing.T) {
} }
{ {
ips, err := client.LookupIP("facebook.com") ips, err := client.LookupIP("facebook.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -231,7 +243,11 @@ func TestUDPServer(t *testing.T) {
} }
{ {
_, err := client.LookupIP("notexist.google.com") _, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err == nil { if err == nil {
t.Fatal("nil error") t.Fatal("nil error")
} }
@ -241,8 +257,11 @@ func TestUDPServer(t *testing.T) {
} }
{ {
clientv6 := client.(feature_dns.IPv6Lookup) ips, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{
ips, err := clientv6.LookupIPv6("ipv4only.google.com") IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
})
if err != feature_dns.ErrEmptyResponse { if err != feature_dns.ErrEmptyResponse {
t.Fatal("error: ", err) t.Fatal("error: ", err)
} }
@ -254,7 +273,11 @@ func TestUDPServer(t *testing.T) {
dnsServer.Shutdown() dnsServer.Shutdown()
{ {
ips, err := client.LookupIP("google.com") ips, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -331,7 +354,11 @@ func TestPrioritizedDomain(t *testing.T) {
startTime := time.Now() startTime := time.Now()
{ {
ips, err := client.LookupIP("google.com") ips, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -390,10 +417,12 @@ func TestUDPServerIPv6(t *testing.T) {
common.Must(err) common.Must(err)
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
client6 := client.(feature_dns.IPv6Lookup)
{ {
ips, err := client6.LookupIPv6("ipv6.google.com") ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -456,7 +485,11 @@ func TestStaticHostDomain(t *testing.T) {
client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client)
{ {
ips, err := client.LookupIP("example.com") ips, err := client.LookupIP("example.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -563,7 +596,11 @@ func TestIPMatch(t *testing.T) {
startTime := time.Now() startTime := time.Now()
{ {
ips, err := client.LookupIP("google.com") ips, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -682,7 +719,11 @@ func TestLocalDomain(t *testing.T) {
startTime := time.Now() startTime := time.Now()
{ // Will match dotless: { // Will match dotless:
ips, err := client.LookupIP("hostname") ips, err := client.LookupIP("hostname", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -693,7 +734,11 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match domain:local { // Will match domain:local
ips, err := client.LookupIP("hostname.local") ips, err := client.LookupIP("hostname.local", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -704,7 +749,11 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match static ip { // Will match static ip
ips, err := client.LookupIP("hostnamestatic") ips, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -715,7 +764,11 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match domain replacing { // Will match domain replacing
ips, err := client.LookupIP("hostnamealias") ips, err := client.LookupIP("hostnamealias", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -726,7 +779,11 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match dotless:localhost, but not expectIPs: 127.0.0.2, 127.0.0.3, then matches at dotless: { // Will match dotless:localhost, but not expectIPs: 127.0.0.2, 127.0.0.3, then matches at dotless:
ips, err := client.LookupIP("localhost") ips, err := client.LookupIP("localhost", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -737,7 +794,11 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3
ips, err := client.LookupIP("localhost-a") ips, err := client.LookupIP("localhost-a", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -748,7 +809,11 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3
ips, err := client.LookupIP("localhost-b") ips, err := client.LookupIP("localhost-b", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -759,7 +824,11 @@ func TestLocalDomain(t *testing.T) {
} }
{ // Will match dotless: { // Will match dotless:
ips, err := client.LookupIP("Mijia Cloud") ips, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -921,7 +990,11 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
startTime := time.Now() startTime := time.Now()
{ // Will match server 1,2 and server 1 returns expected ip { // Will match server 1,2 and server 1 returns expected ip
ips, err := client.LookupIP("google.com") ips, err := client.LookupIP("google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -932,8 +1005,11 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
} }
{ // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one { // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one
clientv4 := client.(feature_dns.IPv4Lookup) ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{
ips, err := clientv4.LookupIPv4("ipv6.google.com") IPv4Enable: true,
IPv6Enable: false,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -944,7 +1020,11 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
} }
{ // Will match server 3,1,2 and server 3 returns expected one { // Will match server 3,1,2 and server 3 returns expected one
ips, err := client.LookupIP("api.google.com") ips, err := client.LookupIP("api.google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }
@ -955,7 +1035,11 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) {
} }
{ // Will match server 4,3,1,2 and server 4 returns expected one { // Will match server 4,3,1,2 and server 4 returns expected one
ips, err := client.LookupIP("v2.api.google.com") ips, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err != nil { if err != nil {
t.Fatal("unexpected error: ", err) t.Fatal("unexpected error: ", err)
} }

View File

@ -4,6 +4,7 @@ package dns
import ( import (
"encoding/binary" "encoding/binary"
"strings"
"time" "time"
"golang.org/x/net/dns/dnsmessage" "golang.org/x/net/dns/dnsmessage"
@ -16,7 +17,7 @@ import (
// Fqdn normalize domain make sure it ends with '.' // Fqdn normalize domain make sure it ends with '.'
func Fqdn(domain string) string { func Fqdn(domain string) string {
if len(domain) > 0 && domain[len(domain)-1] == '.' { if len(domain) > 0 && strings.HasSuffix(domain, ".") {
return domain return domain
} }
return domain + "." return domain + "."
@ -115,7 +116,7 @@ func genEDNS0Options(clientIP net.IP) *dnsmessage.Resource {
return opt return opt
} }
func buildReqMsgs(domain string, option IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest { func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest {
qA := dnsmessage.Question{ qA := dnsmessage.Question{
Name: dnsmessage.MustNewName(domain), Name: dnsmessage.MustNewName(domain),
Type: dnsmessage.TypeA, Type: dnsmessage.TypeA,

View File

@ -13,6 +13,7 @@ import (
"github.com/v2fly/v2ray-core/v4/common" "github.com/v2fly/v2ray-core/v4/common"
"github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/net"
dns_feature "github.com/v2fly/v2ray-core/v4/features/dns"
) )
func Test_parseResponse(t *testing.T) { func Test_parseResponse(t *testing.T) {
@ -95,7 +96,7 @@ func Test_buildReqMsgs(t *testing.T) {
} }
type args struct { type args struct {
domain string domain string
option IPOption option dns_feature.IPOption
reqOpts *dnsmessage.Resource reqOpts *dnsmessage.Resource
} }
tests := []struct { tests := []struct {
@ -103,10 +104,26 @@ func Test_buildReqMsgs(t *testing.T) {
args args args args
want int want int
}{ }{
{"dual stack", args{"test.com", IPOption{true, true}, nil}, 2}, {"dual stack", args{"test.com", dns_feature.IPOption{
{"ipv4 only", args{"test.com", IPOption{true, false}, nil}, 1}, IPv4Enable: true,
{"ipv6 only", args{"test.com", IPOption{false, true}, nil}, 1}, IPv6Enable: true,
{"none/error", args{"test.com", IPOption{false, false}, nil}, 0}, FakeEnable: false,
}, nil}, 2},
{"ipv4 only", args{"test.com", dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: false,
FakeEnable: false,
}, nil}, 1},
{"ipv6 only", args{"test.com", dns_feature.IPOption{
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
}, nil}, 1},
{"none/error", args{"test.com", dns_feature.IPOption{
IPv4Enable: false,
IPv6Enable: false,
FakeEnable: false,
}, nil}, 0},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View File

@ -7,6 +7,7 @@ import (
"github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/net"
"github.com/v2fly/v2ray-core/v4/common/strmatcher" "github.com/v2fly/v2ray-core/v4/common/strmatcher"
"github.com/v2fly/v2ray-core/v4/features" "github.com/v2fly/v2ray-core/v4/features"
"github.com/v2fly/v2ray-core/v4/features/dns"
) )
// StaticHosts represents static domain-ip mapping in DNS server. // StaticHosts represents static domain-ip mapping in DNS server.
@ -75,7 +76,7 @@ func NewStaticHosts(hosts []*Config_HostMapping, legacy map[string]*net.IPOrDoma
return sh, nil return sh, nil
} }
func filterIP(ips []net.Address, option IPOption) []net.Address { func filterIP(ips []net.Address, option dns.IPOption) []net.Address {
filtered := make([]net.Address, 0, len(ips)) filtered := make([]net.Address, 0, len(ips))
for _, ip := range ips { for _, ip := range ips {
if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) { if (ip.Family().IsIPv4() && option.IPv4Enable) || (ip.Family().IsIPv6() && option.IPv6Enable) {
@ -93,7 +94,7 @@ func (h *StaticHosts) lookupInternal(domain string) []net.Address {
return ips return ips
} }
func (h *StaticHosts) lookup(domain string, option IPOption, maxDepth int) []net.Address { func (h *StaticHosts) lookup(domain string, option dns.IPOption, maxDepth int) []net.Address {
switch addrs := h.lookupInternal(domain); { switch addrs := h.lookupInternal(domain); {
case len(addrs) == 0: // Not recorded in static hosts, return nil case len(addrs) == 0: // Not recorded in static hosts, return nil
return nil return nil
@ -111,6 +112,6 @@ func (h *StaticHosts) lookup(domain string, option IPOption, maxDepth int) []net
} }
// Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts. // Lookup returns IP addresses or proxied domain for the given domain, if exists in this StaticHosts.
func (h *StaticHosts) Lookup(domain string, option IPOption) []net.Address { func (h *StaticHosts) Lookup(domain string, option dns.IPOption) []net.Address {
return h.lookup(domain, option, 5) return h.lookup(domain, option, 5)
} }

View File

@ -8,6 +8,7 @@ import (
. "github.com/v2fly/v2ray-core/v4/app/dns" . "github.com/v2fly/v2ray-core/v4/app/dns"
"github.com/v2fly/v2ray-core/v4/common" "github.com/v2fly/v2ray-core/v4/common"
"github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/net"
"github.com/v2fly/v2ray-core/v4/features/dns"
) )
func TestStaticHosts(t *testing.T) { func TestStaticHosts(t *testing.T) {
@ -39,7 +40,7 @@ func TestStaticHosts(t *testing.T) {
common.Must(err) common.Must(err)
{ {
ips := hosts.Lookup("v2fly.org", IPOption{ ips := hosts.Lookup("v2fly.org", dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) })
@ -52,7 +53,7 @@ func TestStaticHosts(t *testing.T) {
} }
{ {
ips := hosts.Lookup("www.v2ray.cn", IPOption{ ips := hosts.Lookup("www.v2ray.cn", dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) })
@ -65,7 +66,7 @@ func TestStaticHosts(t *testing.T) {
} }
{ {
ips := hosts.Lookup("baidu.com", IPOption{ ips := hosts.Lookup("baidu.com", dns.IPOption{
IPv4Enable: false, IPv4Enable: false,
IPv6Enable: true, IPv6Enable: true,
}) })

View File

@ -5,6 +5,7 @@ package dns
import ( import (
"context" "context"
"net/url" "net/url"
"strings"
"time" "time"
core "github.com/v2fly/v2ray-core/v4" core "github.com/v2fly/v2ray-core/v4"
@ -12,21 +13,16 @@ import (
"github.com/v2fly/v2ray-core/v4/common/errors" "github.com/v2fly/v2ray-core/v4/common/errors"
"github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/net"
"github.com/v2fly/v2ray-core/v4/common/strmatcher" "github.com/v2fly/v2ray-core/v4/common/strmatcher"
"github.com/v2fly/v2ray-core/v4/features/dns"
"github.com/v2fly/v2ray-core/v4/features/routing" "github.com/v2fly/v2ray-core/v4/features/routing"
) )
// IPOption is an object for IP query options.
type IPOption struct {
IPv4Enable bool
IPv6Enable bool
}
// Server is the interface for Name Server. // Server is the interface for Name Server.
type Server interface { type Server interface {
// Name of the Client. // Name of the Client.
Name() string Name() string
// QueryIP sends IP queries to its configured server. // QueryIP sends IP queries to its configured server.
QueryIP(ctx context.Context, domain string, clientIP net.IP, option IPOption) ([]net.IP, error) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption) ([]net.IP, error)
} }
// Client is the interface for DNS client. // Client is the interface for DNS client.
@ -47,15 +43,15 @@ func NewServer(dest net.Destination, dispatcher routing.Dispatcher) (Server, err
return nil, err return nil, err
} }
switch { switch {
case u.String() == "localhost": case strings.EqualFold(u.String(), "localhost"):
return NewLocalNameServer(), nil return NewLocalNameServer(), nil
case u.Scheme == "https": // DOH Remote mode case strings.EqualFold(u.Scheme, "https"): // DOH Remote mode
return NewDoHNameServer(u, dispatcher) return NewDoHNameServer(u, dispatcher)
case u.Scheme == "https+local": // DOH Local mode case strings.EqualFold(u.Scheme, "https+local"): // DOH Local mode
return NewDoHLocalNameServer(u), nil return NewDoHLocalNameServer(u), nil
case u.Scheme == "quic+local": // DNS-over-QUIC Local mode case strings.EqualFold(u.Scheme, "quic+local"): // DNS-over-QUIC Local mode
return NewQUICNameServer(u) return NewQUICNameServer(u)
case u.String() == "fakedns": case strings.EqualFold(u.String(), "fakedns"):
return NewFakeDNSServer(), nil return NewFakeDNSServer(), nil
} }
} }
@ -173,7 +169,7 @@ func (c *Client) Name() string {
} }
// QueryIP send DNS query to the name server with the client's IP. // QueryIP send DNS query to the name server with the client's IP.
func (c *Client) QueryIP(ctx context.Context, domain string, option IPOption) ([]net.IP, error) { func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption) ([]net.IP, error) {
ctx, cancel := context.WithTimeout(ctx, 4*time.Second) ctx, cancel := context.WithTimeout(ctx, 4*time.Second)
ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option) ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option)
cancel() cancel()

View File

@ -207,7 +207,7 @@ func (s *DoHNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1)) return uint16(atomic.AddUint32(&s.reqID, 1))
} }
func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option IPOption) { func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx)) newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
@ -286,7 +286,7 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
return ioutil.ReadAll(resp.Body) return ioutil.ReadAll(resp.Body)
} }
func (s *DoHNameServer) findIPsForDomain(domain string, option IPOption) ([]net.IP, error) { func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock() s.RLock()
record, found := s.ips[domain] record, found := s.ips[domain]
s.RUnlock() s.RUnlock()
@ -329,7 +329,7 @@ func (s *DoHNameServer) findIPsForDomain(domain string, option IPOption) ([]net.
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option IPOption) ([]net.IP, error) { // nolint: dupl func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) ([]net.IP, error) { // nolint: dupl
fqdn := Fqdn(domain) fqdn := Fqdn(domain)
ips, err := s.findIPsForDomain(fqdn, option) ips, err := s.findIPsForDomain(fqdn, option)

View File

@ -22,7 +22,7 @@ func (FakeDNSServer) Name() string {
return "FakeDNS" return "FakeDNS"
} }
func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _ IPOption) ([]net.IP, error) { func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _ dns.IPOption) ([]net.IP, error) {
if f.fakeDNSEngine == nil { if f.fakeDNSEngine == nil {
if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) { if err := core.RequireFeatures(ctx, func(fd dns.FakeDNSEngine) {
f.fakeDNSEngine = fd f.fakeDNSEngine = fd
@ -37,5 +37,7 @@ func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, _
return nil, newError("Unable to convert IP to net ip").Base(err).AtError() return nil, newError("Unable to convert IP to net ip").Base(err).AtError()
} }
newError(f.Name(), " got answer: ", domain, " -> ", ips).AtInfo().WriteToLog()
return netIP, nil return netIP, nil
} }

View File

@ -6,6 +6,7 @@ import (
"context" "context"
"github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/net"
"github.com/v2fly/v2ray-core/v4/features/dns"
"github.com/v2fly/v2ray-core/v4/features/dns/localdns" "github.com/v2fly/v2ray-core/v4/features/dns/localdns"
) )
@ -15,17 +16,9 @@ type LocalNameServer struct {
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option IPOption) ([]net.IP, error) { func (s *LocalNameServer) QueryIP(_ context.Context, domain string, _ net.IP, option dns.IPOption) ([]net.IP, error) {
if option.IPv4Enable && option.IPv6Enable { if option.IPv4Enable || option.IPv6Enable {
return s.client.LookupIP(domain) return s.client.LookupIP(domain, option)
}
if option.IPv4Enable {
return s.client.LookupIPv4(domain)
}
if option.IPv6Enable {
return s.client.LookupIPv6(domain)
} }
return nil, newError("neither IPv4 nor IPv6 is enabled") return nil, newError("neither IPv4 nor IPv6 is enabled")

View File

@ -8,12 +8,13 @@ import (
. "github.com/v2fly/v2ray-core/v4/app/dns" . "github.com/v2fly/v2ray-core/v4/app/dns"
"github.com/v2fly/v2ray-core/v4/common" "github.com/v2fly/v2ray-core/v4/common"
"github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/net"
"github.com/v2fly/v2ray-core/v4/features/dns"
) )
func TestLocalNameServer(t *testing.T) { func TestLocalNameServer(t *testing.T) {
s := NewLocalNameServer() s := NewLocalNameServer()
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, err := s.QueryIP(ctx, "google.com", net.IP{}, IPOption{ ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) })

View File

@ -153,7 +153,7 @@ func (s *QUICNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1)) return uint16(atomic.AddUint32(&s.reqID, 1))
} }
func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option IPOption) { func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx)) newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
@ -223,7 +223,7 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP
} }
} }
func (s *QUICNameServer) findIPsForDomain(domain string, option IPOption) ([]net.IP, error) { func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock() s.RLock()
record, found := s.ips[domain] record, found := s.ips[domain]
s.RUnlock() s.RUnlock()
@ -266,7 +266,7 @@ func (s *QUICNameServer) findIPsForDomain(domain string, option IPOption) ([]net
} }
// QueryIP is called from dns.Server->queryIPTimeout // QueryIP is called from dns.Server->queryIPTimeout
func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option IPOption) ([]net.IP, error) { func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) ([]net.IP, error) {
fqdn := Fqdn(domain) fqdn := Fqdn(domain)
ips, err := s.findIPsForDomain(fqdn, option) ips, err := s.findIPsForDomain(fqdn, option)

View File

@ -9,6 +9,7 @@ import (
. "github.com/v2fly/v2ray-core/v4/app/dns" . "github.com/v2fly/v2ray-core/v4/app/dns"
"github.com/v2fly/v2ray-core/v4/common" "github.com/v2fly/v2ray-core/v4/common"
"github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/net"
dns_feature "github.com/v2fly/v2ray-core/v4/features/dns"
) )
func TestQUICNameServer(t *testing.T) { func TestQUICNameServer(t *testing.T) {
@ -17,7 +18,7 @@ func TestQUICNameServer(t *testing.T) {
s, err := NewQUICNameServer(url) s, err := NewQUICNameServer(url)
common.Must(err) common.Must(err)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), IPOption{ ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{
IPv4Enable: true, IPv4Enable: true,
IPv6Enable: true, IPv6Enable: true,
}) })

View File

@ -55,7 +55,7 @@ func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher
Execute: s.Cleanup, Execute: s.Cleanup,
} }
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse) s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
newError("DNS: created UDP client inited for ", address.NetAddr()).AtInfo().WriteToLog() newError("DNS: created UDP client initialized for ", address.NetAddr()).AtInfo().WriteToLog()
return s return s
} }
@ -184,7 +184,7 @@ func (s *ClassicNameServer) addPendingRequest(req *dnsRequest) {
s.requests[id] = *req s.requests[id] = *req
} }
func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option IPOption) { func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) {
newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx)) newError(s.name, " querying DNS for: ", domain).AtDebug().WriteToLog(session.ExportIDToError(ctx))
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP)) reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(clientIP))
@ -203,7 +203,7 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, client
} }
} }
func (s *ClassicNameServer) findIPsForDomain(domain string, option IPOption) ([]net.IP, error) { func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) {
s.RLock() s.RLock()
record, found := s.ips[domain] record, found := s.ips[domain]
s.RUnlock() s.RUnlock()
@ -242,7 +242,7 @@ func (s *ClassicNameServer) findIPsForDomain(domain string, option IPOption) ([]
} }
// QueryIP implements Server. // QueryIP implements Server.
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option IPOption) ([]net.IP, error) { func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption) ([]net.IP, error) {
fqdn := Fqdn(domain) fqdn := Fqdn(domain)
ips, err := s.findIPsForDomain(fqdn, option) ips, err := s.findIPsForDomain(fqdn, option)

View File

@ -10,6 +10,7 @@ import (
"github.com/v2fly/v2ray-core/v4/common" "github.com/v2fly/v2ray-core/v4/common"
"github.com/v2fly/v2ray-core/v4/common/net" "github.com/v2fly/v2ray-core/v4/common/net"
"github.com/v2fly/v2ray-core/v4/common/session" "github.com/v2fly/v2ray-core/v4/common/session"
"github.com/v2fly/v2ray-core/v4/features/dns"
"github.com/v2fly/v2ray-core/v4/features/outbound" "github.com/v2fly/v2ray-core/v4/features/outbound"
routing_session "github.com/v2fly/v2ray-core/v4/features/routing/session" routing_session "github.com/v2fly/v2ray-core/v4/features/routing/session"
"github.com/v2fly/v2ray-core/v4/testing/mocks" "github.com/v2fly/v2ray-core/v4/testing/mocks"
@ -116,7 +117,11 @@ func TestIPOnDemand(t *testing.T) {
defer mockCtl.Finish() defer mockCtl.Finish()
mockDNS := mocks.NewDNSClient(mockCtl) mockDNS := mocks.NewDNSClient(mockCtl)
mockDNS.EXPECT().LookupIP(gomock.Eq("v2fly.org")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() mockDNS.EXPECT().LookupIP(gomock.Eq("v2fly.org"), dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
r := new(Router) r := new(Router)
common.Must(r.Init(config, mockDNS, nil)) common.Must(r.Init(config, mockDNS, nil))
@ -151,7 +156,11 @@ func TestIPIfNonMatchDomain(t *testing.T) {
defer mockCtl.Finish() defer mockCtl.Finish()
mockDNS := mocks.NewDNSClient(mockCtl) mockDNS := mocks.NewDNSClient(mockCtl)
mockDNS.EXPECT().LookupIP(gomock.Eq("v2fly.org")).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() mockDNS.EXPECT().LookupIP(gomock.Eq("v2fly.org"), dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes()
r := new(Router) r := new(Router)
common.Must(r.Init(config, mockDNS, nil)) common.Must(r.Init(config, mockDNS, nil))

View File

@ -7,6 +7,13 @@ import (
"github.com/v2fly/v2ray-core/v4/features" "github.com/v2fly/v2ray-core/v4/features"
) )
// IPOption is an object for IP query options.
type IPOption struct {
IPv4Enable bool
IPv6Enable bool
FakeEnable bool
}
// Client is a V2Ray feature for querying DNS information. // Client is a V2Ray feature for querying DNS information.
// //
// v2ray:api:stable // v2ray:api:stable
@ -14,21 +21,7 @@ type Client interface {
features.Feature features.Feature
// LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses. // LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses.
LookupIP(domain string) ([]net.IP, error) LookupIP(domain string, option IPOption) ([]net.IP, error)
}
// IPv4Lookup is an optional feature for querying IPv4 addresses only.
//
// v2ray:api:beta
type IPv4Lookup interface {
LookupIPv4(domain string) ([]net.IP, error)
}
// IPv6Lookup is an optional feature for querying IPv6 addresses only.
//
// v2ray:api:beta
type IPv6Lookup interface {
LookupIPv6(domain string) ([]net.IP, error)
} }
// ClientType returns the type of Client interface. Can be used for implementing common.HasType. // ClientType returns the type of Client interface. Can be used for implementing common.HasType.

View File

@ -20,58 +20,41 @@ func (*Client) Start() error { return nil }
func (*Client) Close() error { return nil } func (*Client) Close() error { return nil }
// LookupIP implements Client. // LookupIP implements Client.
func (*Client) LookupIP(host string) ([]net.IP, error) { func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, error) {
ips, err := net.LookupIP(host) ips, err := net.LookupIP(host)
if err != nil { if err != nil {
return nil, err return nil, err
} }
parsedIPs := make([]net.IP, 0, len(ips)) parsedIPs := make([]net.IP, 0, len(ips))
ipv4 := make([]net.IP, 0, len(ips))
ipv6 := make([]net.IP, 0, len(ips))
for _, ip := range ips { for _, ip := range ips {
parsed := net.IPAddress(ip) parsed := net.IPAddress(ip)
if parsed != nil { if parsed != nil {
parsedIPs = append(parsedIPs, parsed.IP()) parsedIPs = append(parsedIPs, parsed.IP())
} }
}
if len(parsedIPs) == 0 {
return nil, dns.ErrEmptyResponse
}
return parsedIPs, nil
}
// LookupIPv4 implements IPv4Lookup.
func (c *Client) LookupIPv4(host string) ([]net.IP, error) {
ips, err := c.LookupIP(host)
if err != nil {
return nil, err
}
ipv4 := make([]net.IP, 0, len(ips))
for _, ip := range ips {
if len(ip) == net.IPv4len { if len(ip) == net.IPv4len {
ipv4 = append(ipv4, ip) ipv4 = append(ipv4, ip)
} }
}
if len(ipv4) == 0 {
return nil, dns.ErrEmptyResponse
}
return ipv4, nil
}
// LookupIPv6 implements IPv6Lookup.
func (c *Client) LookupIPv6(host string) ([]net.IP, error) {
ips, err := c.LookupIP(host)
if err != nil {
return nil, err
}
ipv6 := make([]net.IP, 0, len(ips))
for _, ip := range ips {
if len(ip) == net.IPv6len { if len(ip) == net.IPv6len {
ipv6 = append(ipv6, ip) ipv6 = append(ipv6, ip)
} }
} }
if len(ipv6) == 0 { switch {
return nil, dns.ErrEmptyResponse case option.IPv4Enable && option.IPv6Enable:
if len(parsedIPs) > 0 {
return parsedIPs, nil
}
case option.IPv4Enable:
if len(ipv4) > 0 {
return ipv4, nil
}
case option.IPv6Enable:
if len(ipv6) > 0 {
return ipv6, nil
}
} }
return ipv6, nil return nil, dns.ErrEmptyResponse
} }
// New create a new dns.Client that queries localhost for DNS. // New create a new dns.Client that queries localhost for DNS.

View File

@ -26,7 +26,11 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP {
} }
if domain := ctx.GetTargetDomain(); len(domain) != 0 { if domain := ctx.GetTargetDomain(); len(domain) != 0 {
ips, err := ctx.dnsClient.LookupIP(domain) ips, err := ctx.dnsClient.LookupIP(domain, dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
})
if err == nil { if err == nil {
ctx.resolvedIPs = ips ctx.resolvedIPs = ips
return ips return ips

View File

@ -39,26 +39,12 @@ type ownLinkVerifier interface {
type Handler struct { type Handler struct {
client dns.Client client dns.Client
ipv4Lookup dns.IPv4Lookup
ipv6Lookup dns.IPv6Lookup
ownLinkVerifier ownLinkVerifier ownLinkVerifier ownLinkVerifier
server net.Destination server net.Destination
} }
func (h *Handler) Init(config *Config, dnsClient dns.Client) error { func (h *Handler) Init(config *Config, dnsClient dns.Client) error {
h.client = dnsClient h.client = dnsClient
ipv4lookup, ok := dnsClient.(dns.IPv4Lookup)
if !ok {
return newError("dns.Client doesn't implement IPv4Lookup")
}
h.ipv4Lookup = ipv4lookup
ipv6lookup, ok := dnsClient.(dns.IPv6Lookup)
if !ok {
return newError("dns.Client doesn't implement IPv6Lookup")
}
h.ipv6Lookup = ipv6lookup
if v, ok := dnsClient.(ownLinkVerifier); ok { if v, ok := dnsClient.(ownLinkVerifier); ok {
h.ownLinkVerifier = v h.ownLinkVerifier = v
} }
@ -217,9 +203,17 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string,
switch qType { switch qType {
case dnsmessage.TypeA: case dnsmessage.TypeA:
ips, err = h.ipv4Lookup.LookupIPv4(domain) ips, err = h.client.LookupIP(domain, dns.IPOption{
IPv4Enable: true,
IPv6Enable: false,
FakeEnable: true,
})
case dnsmessage.TypeAAAA: case dnsmessage.TypeAAAA:
ips, err = h.ipv6Lookup.LookupIPv6(domain) ips, err = h.client.LookupIP(domain, dns.IPOption{
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: true,
})
} }
rcode := dns.RCodeFromError(err) rcode := dns.RCodeFromError(err)

View File

@ -60,19 +60,26 @@ func (h *Handler) policy() policy.Session {
} }
func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address {
var lookupFunc func(string) ([]net.IP, error) = h.dns.LookupIP var option dns.IPOption = dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}
if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) { if h.config.DomainStrategy == Config_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()) {
if lookupIPv4, ok := h.dns.(dns.IPv4Lookup); ok { option = dns.IPOption{
lookupFunc = lookupIPv4.LookupIPv4 IPv4Enable: true,
IPv6Enable: false,
FakeEnable: false,
} }
} else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) { } else if h.config.DomainStrategy == Config_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()) {
if lookupIPv6, ok := h.dns.(dns.IPv6Lookup); ok { option = dns.IPOption{
lookupFunc = lookupIPv6.LookupIPv6 IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
} }
} }
ips, err := lookupFunc(domain) ips, err := h.dns.LookupIP(domain, option)
if err != nil { if err != nil {
newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx)) newError("failed to get IP address for domain ", domain).Base(err).WriteToLog(session.ExportIDToError(ctx))
} }
@ -123,7 +130,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte
Address: ip, Address: ip,
Port: dialDest.Port, Port: dialDest.Port,
} }
newError("dialing to to ", dialDest).WriteToLog(session.ExportIDToError(ctx)) newError("dialing to ", dialDest).WriteToLog(session.ExportIDToError(ctx))
} }
} }

View File

@ -9,6 +9,7 @@ import (
reflect "reflect" reflect "reflect"
gomock "github.com/golang/mock/gomock" gomock "github.com/golang/mock/gomock"
dns "github.com/v2fly/v2ray-core/v4/features/dns"
) )
// DNSClient is a mock of Client interface. // DNSClient is a mock of Client interface.
@ -49,18 +50,18 @@ func (mr *DNSClientMockRecorder) Close() *gomock.Call {
} }
// LookupIP mocks base method. // LookupIP mocks base method.
func (m *DNSClient) LookupIP(arg0 string) ([]net.IP, error) { func (m *DNSClient) LookupIP(arg0 string, arg1 dns.IPOption) ([]net.IP, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LookupIP", arg0) ret := m.ctrl.Call(m, "LookupIP", arg0, arg1)
ret0, _ := ret[0].([]net.IP) ret0, _ := ret[0].([]net.IP)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// LookupIP indicates an expected call of LookupIP. // LookupIP indicates an expected call of LookupIP.
func (mr *DNSClientMockRecorder) LookupIP(arg0 interface{}) *gomock.Call { func (mr *DNSClientMockRecorder) LookupIP(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIP", reflect.TypeOf((*DNSClient)(nil).LookupIP), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LookupIP", reflect.TypeOf((*DNSClient)(nil).LookupIP), arg0, arg1)
} }
// Start mocks base method. // Start mocks base method.