diff --git a/app/dns/dns.go b/app/dns/dns.go index bc544a5a..9b84106c 100644 --- a/app/dns/dns.go +++ b/app/dns/dns.go @@ -157,16 +157,16 @@ func (s *DNS) IsOwnLink(ctx context.Context) bool { } // LookupIP implements dns.Client. -func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { +func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, uint32, error) { if domain == "" { - return nil, errors.New("empty domain name") + return nil, 0, errors.New("empty domain name") } option.IPv4Enable = option.IPv4Enable && s.ipOption.IPv4Enable option.IPv6Enable = option.IPv6Enable && s.ipOption.IPv6Enable if !option.IPv4Enable && !option.IPv6Enable { - return nil, dns.ErrEmptyResponse + return nil, 0, dns.ErrEmptyResponse } // Normalize the FQDN form query @@ -177,13 +177,14 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { case addrs == nil: // Domain not recorded in static host break case len(addrs) == 0: // Domain recorded, but no valid IP returned (e.g. IPv4 address with only IPv6 enabled) - return nil, dns.ErrEmptyResponse + return nil, 0, dns.ErrEmptyResponse case len(addrs) == 1 && addrs[0].Family().IsDomain(): // Domain replacement errors.LogInfo(s.ctx, "domain replaced: ", domain, " -> ", addrs[0].Domain()) domain = addrs[0].Domain() default: // Successfully found ip records in static host errors.LogInfo(s.ctx, "returning ", len(addrs), " IP(s) for domain ", domain, " -> ", addrs) - return toNetIP(addrs) + ips, err := toNetIP(addrs) + return ips, 10, err // Hosts ttl is 10 } // Name servers lookup @@ -194,9 +195,9 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { errors.LogDebug(s.ctx, "skip DNS resolution for domain ", domain, " at server ", client.Name()) continue } - ips, err := client.QueryIP(ctx, domain, option, s.disableCache) + ips, ttl, err := client.QueryIP(ctx, domain, option, s.disableCache) if len(ips) > 0 { - return ips, nil + return ips, ttl, nil } if err != nil { errors.LogInfoInner(s.ctx, err, "failed to lookup ip for domain ", domain, " at server ", client.Name()) @@ -204,11 +205,11 @@ func (s *DNS) LookupIP(domain string, option dns.IPOption) ([]net.IP, error) { } // 5 for RcodeRefused in miekg/dns, hardcode to reduce binary size if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch && err != dns.ErrEmptyResponse && dns.RCodeFromError(err) != 5 { - return nil, err + return nil, 0, err } } - return nil, errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...)) + return nil, 0, errors.New("returning nil for domain ", domain).Base(errors.Combine(errs...)) } // LookupHosts implements dns.HostsLookup. diff --git a/app/dns/dns_test.go b/app/dns/dns_test.go index 500c057e..ab985384 100644 --- a/app/dns/dns_test.go +++ b/app/dns/dns_test.go @@ -155,7 +155,7 @@ func TestUDPServerSubnet(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -216,7 +216,7 @@ func TestUDPServer(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -231,7 +231,7 @@ func TestUDPServer(t *testing.T) { } { - ips, err := client.LookupIP("facebook.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("facebook.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -246,7 +246,7 @@ func TestUDPServer(t *testing.T) { } { - _, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{ + _, _, err := client.LookupIP("notexist.google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -260,7 +260,7 @@ func TestUDPServer(t *testing.T) { } { - ips, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("ipv4only.google.com", feature_dns.IPOption{ IPv4Enable: false, IPv6Enable: true, FakeEnable: false, @@ -276,7 +276,7 @@ func TestUDPServer(t *testing.T) { dnsServer.Shutdown() { - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -357,7 +357,7 @@ func TestPrioritizedDomain(t *testing.T) { startTime := time.Now() { - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -423,7 +423,7 @@ func TestUDPServerIPv6(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { - ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ IPv4Enable: false, IPv6Enable: true, FakeEnable: false, @@ -492,7 +492,7 @@ func TestStaticHostDomain(t *testing.T) { client := v.GetFeature(feature_dns.ClientType()).(feature_dns.Client) { - ips, err := client.LookupIP("example.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("example.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -603,7 +603,7 @@ func TestIPMatch(t *testing.T) { startTime := time.Now() { - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -726,7 +726,7 @@ func TestLocalDomain(t *testing.T) { startTime := time.Now() { // Will match dotless: - ips, err := client.LookupIP("hostname", feature_dns.IPOption{ + ips, _, err := client.LookupIP("hostname", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -741,7 +741,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match domain:local - ips, err := client.LookupIP("hostname.local", feature_dns.IPOption{ + ips, _, err := client.LookupIP("hostname.local", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -756,7 +756,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match static ip - ips, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{ + ips, _, err := client.LookupIP("hostnamestatic", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -771,7 +771,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match domain replacing - ips, err := client.LookupIP("hostnamealias", feature_dns.IPOption{ + ips, _, err := client.LookupIP("hostnamealias", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -786,7 +786,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless:localhost, but not expectIPs: 127.0.0.2, 127.0.0.3, then matches at dotless: - ips, err := client.LookupIP("localhost", feature_dns.IPOption{ + ips, _, err := client.LookupIP("localhost", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -801,7 +801,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 - ips, err := client.LookupIP("localhost-a", feature_dns.IPOption{ + ips, _, err := client.LookupIP("localhost-a", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -816,7 +816,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless:localhost, and expectIPs: 127.0.0.2, 127.0.0.3 - ips, err := client.LookupIP("localhost-b", feature_dns.IPOption{ + ips, _, err := client.LookupIP("localhost-b", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -831,7 +831,7 @@ func TestLocalDomain(t *testing.T) { } { // Will match dotless: - ips, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{ + ips, _, err := client.LookupIP("Mijia Cloud", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -997,7 +997,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { startTime := time.Now() { // Will match server 1,2 and server 1 returns expected ip - ips, err := client.LookupIP("google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -1012,7 +1012,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 1,2 and server 1 returns unexpected ip, then server 2 returns expected one - ips, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("ipv6.google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: false, FakeEnable: false, @@ -1027,7 +1027,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 3,1,2 and server 3 returns expected one - ips, err := client.LookupIP("api.google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("api.google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, @@ -1042,7 +1042,7 @@ func TestMultiMatchPrioritizedDomain(t *testing.T) { } { // Will match server 4,3,1,2 and server 4 returns expected one - ips, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{ + ips, _, err := client.LookupIP("v2.api.google.com", feature_dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, diff --git a/app/dns/dnscommon.go b/app/dns/dnscommon.go index e11cc097..0dc07f72 100644 --- a/app/dns/dnscommon.go +++ b/app/dns/dnscommon.go @@ -38,14 +38,15 @@ type IPRecord struct { RawHeader *dnsmessage.Header } -func (r *IPRecord) getIPs() ([]net.Address, error) { +func (r *IPRecord) getIPs() ([]net.Address, uint32, error) { if r == nil || r.Expire.Before(time.Now()) { - return nil, errRecordNotFound + return nil, 0, errRecordNotFound } if r.RCode != dnsmessage.RCodeSuccess { - return nil, dns_feature.RCodeError(r.RCode) + return nil, 0, dns_feature.RCodeError(r.RCode) } - return r.IP, nil + ttl := uint32(time.Until(r.Expire) / time.Second) + return r.IP, ttl, nil } func isNewer(baseRec *IPRecord, newRec *IPRecord) bool { diff --git a/app/dns/nameserver.go b/app/dns/nameserver.go index 14513b3b..774d87c3 100644 --- a/app/dns/nameserver.go +++ b/app/dns/nameserver.go @@ -21,7 +21,7 @@ type Server interface { // Name of the Client. Name() string // QueryIP sends IP queries to its configured server. - QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, error) + QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns.IPOption, disableCache bool) ([]net.IP, uint32, error) } // Client is the interface for DNS client. @@ -191,7 +191,7 @@ func (c *Client) Name() string { } // QueryIP sends DNS query to the name server with the client's IP. -func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, disableCache bool) ([]net.IP, error) { +func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption, disableCache bool) ([]net.IP, uint32, error) { ctx, cancel := context.WithTimeout(ctx, c.timeoutMs) if len(c.tag) != 0 { content := session.InboundFromContext(ctx) @@ -200,13 +200,14 @@ func (c *Client) QueryIP(ctx context.Context, domain string, option dns.IPOption // do not direct set *content.Tag, it might be used by other clients ctx = session.ContextWithInbound(ctx, &session.Inbound{Tag: c.tag}) } - ips, err := c.server.QueryIP(ctx, domain, c.clientIP, option, disableCache) + ips, ttl, err := c.server.QueryIP(ctx, domain, c.clientIP, option, disableCache) cancel() if err != nil { - return ips, err + return ips, ttl, err } - return c.MatchExpectedIPs(domain, ips) + netips, err := c.MatchExpectedIPs(domain, ips) + return netips, ttl, err } // MatchExpectedIPs matches queried domain IPs with expected IPs and returns matched ones. diff --git a/app/dns/nameserver_doh.go b/app/dns/nameserver_doh.go index 1739062d..8a23e0c3 100644 --- a/app/dns/nameserver_doh.go +++ b/app/dns/nameserver_doh.go @@ -301,64 +301,66 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte, return io.ReadAll(resp.Body) } -func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { +func (s *DoHNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { s.RLock() record, found := s.ips[domain] s.RUnlock() if !found { - return nil, errRecordNotFound + return nil, 0, errRecordNotFound } var err4 error var err6 error var ips []net.Address var ip6 []net.Address + var ttl uint32 if option.IPv4Enable { - ips, err4 = record.A.getIPs() + ips, ttl, err4 = record.A.getIPs() } if option.IPv6Enable { - ip6, err6 = record.AAAA.getIPs() + ip6, ttl, err6 = record.AAAA.getIPs() ips = append(ips, ip6...) } if len(ips) > 0 { - return toNetIP(ips) + netips, err := toNetIP(ips) + return netips, ttl, err } if err4 != nil { - return nil, err4 + return nil, 0, err4 } if err6 != nil { - return nil, err6 + return nil, 0, err6 } if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) { - return nil, dns_feature.ErrEmptyResponse + return nil, 0, dns_feature.ErrEmptyResponse } - return nil, errRecordNotFound + return nil, 0, errRecordNotFound } // QueryIP implements Server. -func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { // nolint: dupl +func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, uint32, error) { // nolint: dupl fqdn := Fqdn(domain) option = ResolveIpOptionOverride(s.queryStrategy, option) if !option.IPv4Enable && !option.IPv6Enable { - return nil, dns_feature.ErrEmptyResponse + return nil, 0, dns_feature.ErrEmptyResponse } if disableCache { errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name) } else { - ips, err := s.findIPsForDomain(fqdn, option) + ips, ttl, err := s.findIPsForDomain(fqdn, option) if err == nil || err == dns_feature.ErrEmptyResponse { errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips) log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) - return ips, err + return ips, ttl, err } } @@ -392,15 +394,15 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, clientIP net start := time.Now() for { - ips, err := s.findIPsForDomain(fqdn, option) + ips, ttl, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) - return ips, err + return ips, ttl, err } select { case <-ctx.Done(): - return nil, ctx.Err() + return nil, 0, ctx.Err() case <-done: } } diff --git a/app/dns/nameserver_doh_test.go b/app/dns/nameserver_doh_test.go index 3dfcb15c..a27a5e9f 100644 --- a/app/dns/nameserver_doh_test.go +++ b/app/dns/nameserver_doh_test.go @@ -19,7 +19,7 @@ func TestDOHNameServer(t *testing.T) { s := NewDoHNameServer(url, QueryStrategy_USE_IP, nil, false) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) @@ -36,7 +36,7 @@ func TestDOHNameServerWithCache(t *testing.T) { s := NewDoHNameServer(url, QueryStrategy_USE_IP, nil, false) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) @@ -47,7 +47,7 @@ func TestDOHNameServerWithCache(t *testing.T) { } ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ + ips2, _, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, true) @@ -64,7 +64,7 @@ func TestDOHNameServerWithIPv4Override(t *testing.T) { s := NewDoHNameServer(url, QueryStrategy_USE_IP4, nil, false) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) @@ -87,7 +87,7 @@ func TestDOHNameServerWithIPv6Override(t *testing.T) { s := NewDoHNameServer(url, QueryStrategy_USE_IP6, nil, false) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) diff --git a/app/dns/nameserver_fakedns.go b/app/dns/nameserver_fakedns.go index ae7a1a7d..37c2f723 100644 --- a/app/dns/nameserver_fakedns.go +++ b/app/dns/nameserver_fakedns.go @@ -20,9 +20,9 @@ func (FakeDNSServer) Name() string { return "FakeDNS" } -func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, opt dns.IPOption, _ bool) ([]net.IP, error) { +func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, opt dns.IPOption, _ bool) ([]net.IP, uint32, error) { if f.fakeDNSEngine == nil { - return nil, errors.New("Unable to locate a fake DNS Engine").AtError() + return nil, 0, errors.New("Unable to locate a fake DNS Engine").AtError() } var ips []net.Address @@ -34,13 +34,13 @@ func (f *FakeDNSServer) QueryIP(ctx context.Context, domain string, _ net.IP, op netIP, err := toNetIP(ips) if err != nil { - return nil, errors.New("Unable to convert IP to net ip").Base(err).AtError() + return nil, 0, errors.New("Unable to convert IP to net ip").Base(err).AtError() } errors.LogInfo(ctx, f.Name(), " got answer: ", domain, " -> ", ips) if len(netIP) > 0 { - return netIP, nil + return netIP, 1, nil // fakeIP ttl is 1 } - return nil, dns.ErrEmptyResponse + return nil, 0, dns.ErrEmptyResponse } diff --git a/app/dns/nameserver_local.go b/app/dns/nameserver_local.go index 14de8356..1b45e5f0 100644 --- a/app/dns/nameserver_local.go +++ b/app/dns/nameserver_local.go @@ -21,14 +21,14 @@ type LocalNameServer struct { const errEmptyResponse = "No address associated with hostname" // QueryIP implements Server. -func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, err error) { +func (s *LocalNameServer) QueryIP(ctx context.Context, domain string, _ net.IP, option dns.IPOption, _ bool) (ips []net.IP, ttl uint32, err error) { option = ResolveIpOptionOverride(s.queryStrategy, option) if !option.IPv4Enable && !option.IPv6Enable { - return nil, dns.ErrEmptyResponse + return nil, 0, dns.ErrEmptyResponse } start := time.Now() - ips, err = s.client.LookupIP(domain, option) + ips, ttl, err = s.client.LookupIP(domain, option) if err != nil && strings.HasSuffix(err.Error(), errEmptyResponse) { err = dns.ErrEmptyResponse diff --git a/app/dns/nameserver_local_test.go b/app/dns/nameserver_local_test.go index 500962f7..a32c69e6 100644 --- a/app/dns/nameserver_local_test.go +++ b/app/dns/nameserver_local_test.go @@ -14,7 +14,7 @@ import ( func TestLocalNameServer(t *testing.T) { s := NewLocalNameServer(QueryStrategy_USE_IP) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) - ips, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP{}, dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, diff --git a/app/dns/nameserver_quic.go b/app/dns/nameserver_quic.go index f816cf1f..2b0b7b66 100644 --- a/app/dns/nameserver_quic.go +++ b/app/dns/nameserver_quic.go @@ -244,64 +244,66 @@ func (s *QUICNameServer) sendQuery(ctx context.Context, domain string, clientIP } } -func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { +func (s *QUICNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { s.RLock() record, found := s.ips[domain] s.RUnlock() if !found { - return nil, errRecordNotFound + return nil, 0, errRecordNotFound } var err4 error var err6 error var ips []net.Address var ip6 []net.Address + var ttl uint32 if option.IPv4Enable { - ips, err4 = record.A.getIPs() + ips, ttl, err4 = record.A.getIPs() } if option.IPv6Enable { - ip6, err6 = record.AAAA.getIPs() + ip6, ttl, err6 = record.AAAA.getIPs() ips = append(ips, ip6...) } if len(ips) > 0 { - return toNetIP(ips) + netips, err := toNetIP(ips) + return netips, ttl, err } if err4 != nil { - return nil, err4 + return nil, 0, err4 } if err6 != nil { - return nil, err6 + return nil, 0, err6 } if (option.IPv4Enable && record.A != nil) || (option.IPv6Enable && record.AAAA != nil) { - return nil, dns_feature.ErrEmptyResponse + return nil, 0, dns_feature.ErrEmptyResponse } - return nil, errRecordNotFound + return nil, 0, errRecordNotFound } // QueryIP is called from dns.Server->queryIPTimeout -func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { +func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, uint32, error) { fqdn := Fqdn(domain) option = ResolveIpOptionOverride(s.queryStrategy, option) if !option.IPv4Enable && !option.IPv6Enable { - return nil, dns_feature.ErrEmptyResponse + return nil, 0, dns_feature.ErrEmptyResponse } if disableCache { errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name) } else { - ips, err := s.findIPsForDomain(fqdn, option) + ips, ttl, err := s.findIPsForDomain(fqdn, option) if err == nil || err == dns_feature.ErrEmptyResponse { errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips) log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) - return ips, err + return ips, ttl, err } } @@ -335,15 +337,15 @@ func (s *QUICNameServer) QueryIP(ctx context.Context, domain string, clientIP ne start := time.Now() for { - ips, err := s.findIPsForDomain(fqdn, option) + ips, ttl, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) - return ips, err + return ips, ttl, err } select { case <-ctx.Done(): - return nil, ctx.Err() + return nil, 0, ctx.Err() case <-done: } } diff --git a/app/dns/nameserver_quic_test.go b/app/dns/nameserver_quic_test.go index 0088aa33..56f9c3ee 100644 --- a/app/dns/nameserver_quic_test.go +++ b/app/dns/nameserver_quic_test.go @@ -19,7 +19,7 @@ func TestQUICNameServer(t *testing.T) { s, err := NewQUICNameServer(url, QueryStrategy_USE_IP) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) @@ -30,7 +30,7 @@ func TestQUICNameServer(t *testing.T) { } ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns.IPOption{ + ips2, _, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }, true) @@ -47,7 +47,7 @@ func TestQUICNameServerWithIPv4Override(t *testing.T) { s, err := NewQUICNameServer(url, QueryStrategy_USE_IP4) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) @@ -70,7 +70,7 @@ func TestQUICNameServerWithIPv6Override(t *testing.T) { s, err := NewQUICNameServer(url, QueryStrategy_USE_IP6) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) diff --git a/app/dns/nameserver_tcp.go b/app/dns/nameserver_tcp.go index 35c9a07f..d84974b0 100644 --- a/app/dns/nameserver_tcp.go +++ b/app/dns/nameserver_tcp.go @@ -273,60 +273,62 @@ func (s *TCPNameServer) sendQuery(ctx context.Context, domain string, clientIP n } } -func (s *TCPNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { +func (s *TCPNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { s.RLock() record, found := s.ips[domain] s.RUnlock() if !found { - return nil, errRecordNotFound + return nil, 0, errRecordNotFound } var err4 error var err6 error var ips []net.Address var ip6 []net.Address + var ttl uint32 if option.IPv4Enable { - ips, err4 = record.A.getIPs() + ips, ttl, err4 = record.A.getIPs() } if option.IPv6Enable { - ip6, err6 = record.AAAA.getIPs() + ip6, ttl, err6 = record.AAAA.getIPs() ips = append(ips, ip6...) } if len(ips) > 0 { - return toNetIP(ips) + netips, err := toNetIP(ips) + return netips, ttl, err } if err4 != nil { - return nil, err4 + return nil, 0, err4 } if err6 != nil { - return nil, err6 + return nil, 0, err6 } - return nil, dns_feature.ErrEmptyResponse + return nil, 0, dns_feature.ErrEmptyResponse } // QueryIP implements Server. -func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { +func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, uint32, error) { fqdn := Fqdn(domain) option = ResolveIpOptionOverride(s.queryStrategy, option) if !option.IPv4Enable && !option.IPv6Enable { - return nil, dns_feature.ErrEmptyResponse + return nil, 0, dns_feature.ErrEmptyResponse } if disableCache { errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name) } else { - ips, err := s.findIPsForDomain(fqdn, option) + ips, ttl, err := s.findIPsForDomain(fqdn, option) if err == nil || err == dns_feature.ErrEmptyResponse { errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips) log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) - return ips, err + return ips, ttl, err } } @@ -360,15 +362,15 @@ func (s *TCPNameServer) QueryIP(ctx context.Context, domain string, clientIP net start := time.Now() for { - ips, err := s.findIPsForDomain(fqdn, option) + ips, ttl, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) - return ips, err + return ips, ttl, err } select { case <-ctx.Done(): - return nil, ctx.Err() + return nil, 0, ctx.Err() case <-done: } } diff --git a/app/dns/nameserver_tcp_test.go b/app/dns/nameserver_tcp_test.go index 6b2b2465..de4ecb89 100644 --- a/app/dns/nameserver_tcp_test.go +++ b/app/dns/nameserver_tcp_test.go @@ -19,7 +19,7 @@ func TestTCPLocalNameServer(t *testing.T) { s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) @@ -36,7 +36,7 @@ func TestTCPLocalNameServerWithCache(t *testing.T) { s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) @@ -47,7 +47,7 @@ func TestTCPLocalNameServerWithCache(t *testing.T) { } ctx2, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips2, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ + ips2, _, err := s.QueryIP(ctx2, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, true) @@ -64,7 +64,7 @@ func TestTCPLocalNameServerWithIPv4Override(t *testing.T) { s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP4) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) @@ -88,7 +88,7 @@ func TestTCPLocalNameServerWithIPv6Override(t *testing.T) { s, err := NewTCPLocalNameServer(url, QueryStrategy_USE_IP6) common.Must(err) ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - ips, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ + ips, _, err := s.QueryIP(ctx, "google.com", net.IP(nil), dns_feature.IPOption{ IPv4Enable: true, IPv6Enable: true, }, false) diff --git a/app/dns/nameserver_udp.go b/app/dns/nameserver_udp.go index 79592e9b..79df14ed 100644 --- a/app/dns/nameserver_udp.go +++ b/app/dns/nameserver_udp.go @@ -230,60 +230,62 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, client } } -func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, error) { +func (s *ClassicNameServer) findIPsForDomain(domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) { s.RLock() record, found := s.ips[domain] s.RUnlock() if !found { - return nil, errRecordNotFound + return nil, 0, errRecordNotFound } var err4 error var err6 error var ips []net.Address var ip6 []net.Address + var ttl uint32 if option.IPv4Enable { - ips, err4 = record.A.getIPs() + ips, ttl, err4 = record.A.getIPs() } if option.IPv6Enable { - ip6, err6 = record.AAAA.getIPs() + ip6, ttl, err6 = record.AAAA.getIPs() ips = append(ips, ip6...) } if len(ips) > 0 { - return toNetIP(ips) + netips, err := toNetIP(ips) + return netips, ttl, err } if err4 != nil { - return nil, err4 + return nil, 0, err4 } if err6 != nil { - return nil, err6 + return nil, 0, err6 } - return nil, dns_feature.ErrEmptyResponse + return nil, 0, dns_feature.ErrEmptyResponse } // QueryIP implements Server. -func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, error) { +func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP net.IP, option dns_feature.IPOption, disableCache bool) ([]net.IP, uint32, error) { fqdn := Fqdn(domain) option = ResolveIpOptionOverride(s.queryStrategy, option) if !option.IPv4Enable && !option.IPv6Enable { - return nil, dns_feature.ErrEmptyResponse + return nil, 0, dns_feature.ErrEmptyResponse } if disableCache { errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.name) } else { - ips, err := s.findIPsForDomain(fqdn, option) + ips, ttl, err := s.findIPsForDomain(fqdn, option) if err == nil || err == dns_feature.ErrEmptyResponse { errors.LogDebugInner(ctx, err, s.name, " cache HIT ", domain, " -> ", ips) log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err}) - return ips, err + return ips, ttl, err } } @@ -317,15 +319,15 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, clientIP start := time.Now() for { - ips, err := s.findIPsForDomain(fqdn, option) + ips, ttl, err := s.findIPsForDomain(fqdn, option) if err != errRecordNotFound { log.Record(&log.DNSLog{Server: s.name, Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err}) - return ips, err + return ips, ttl, err } select { case <-ctx.Done(): - return nil, ctx.Err() + return nil, 0, ctx.Err() case <-done: } } diff --git a/app/router/router_test.go b/app/router/router_test.go index 09f831b2..a0516e05 100644 --- a/app/router/router_test.go +++ b/app/router/router_test.go @@ -177,7 +177,7 @@ func TestIPOnDemand(t *testing.T) { IPv4Enable: true, IPv6Enable: true, FakeEnable: false, - }).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() + }).Return([]net.IP{{192, 168, 0, 1}}, uint32(600), nil).AnyTimes() r := new(Router) common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil)) @@ -222,7 +222,7 @@ func TestIPIfNonMatchDomain(t *testing.T) { IPv4Enable: true, IPv6Enable: true, FakeEnable: false, - }).Return([]net.IP{{192, 168, 0, 1}}, nil).AnyTimes() + }).Return([]net.IP{{192, 168, 0, 1}}, uint32(600), nil).AnyTimes() r := new(Router) common.Must(r.Init(context.TODO(), config, mockDNS, nil, nil)) diff --git a/features/dns/client.go b/features/dns/client.go index d3a911da..9e7be91d 100644 --- a/features/dns/client.go +++ b/features/dns/client.go @@ -21,7 +21,7 @@ type Client interface { features.Feature // LookupIP returns IP address for the given domain. IPs may contain IPv4 and/or IPv6 addresses. - LookupIP(domain string, option IPOption) ([]net.IP, error) + LookupIP(domain string, option IPOption) ([]net.IP, uint32, error) } type HostsLookup interface { diff --git a/features/dns/localdns/client.go b/features/dns/localdns/client.go index 92419dfa..a94c9474 100644 --- a/features/dns/localdns/client.go +++ b/features/dns/localdns/client.go @@ -20,10 +20,10 @@ func (*Client) Start() error { return nil } func (*Client) Close() error { return nil } // LookupIP implements Client. -func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, error) { +func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, uint32, error) { ips, err := net.LookupIP(host) if err != nil { - return nil, err + return nil, 0, err } parsedIPs := make([]net.IP, 0, len(ips)) ipv4 := make([]net.IP, 0, len(ips)) @@ -40,21 +40,22 @@ func (*Client) LookupIP(host string, option dns.IPOption) ([]net.IP, error) { ipv6 = append(ipv6, ip) } } + // Local DNS ttl is 600 switch { case option.IPv4Enable && option.IPv6Enable: if len(parsedIPs) > 0 { - return parsedIPs, nil + return parsedIPs, 600, nil } case option.IPv4Enable: if len(ipv4) > 0 { - return ipv4, nil + return ipv4, 600, nil } case option.IPv6Enable: if len(ipv6) > 0 { - return ipv6, nil + return ipv6, 600, nil } } - return nil, dns.ErrEmptyResponse + return nil, 0, dns.ErrEmptyResponse } // New create a new dns.Client that queries localhost for DNS. diff --git a/features/routing/dns/context.go b/features/routing/dns/context.go index d5b138d7..a65895f6 100644 --- a/features/routing/dns/context.go +++ b/features/routing/dns/context.go @@ -23,7 +23,7 @@ func (ctx *ResolvableContext) GetTargetIPs() []net.IP { } if domain := ctx.GetTargetDomain(); len(domain) != 0 { - ips, err := ctx.dnsClient.LookupIP(domain, dns.IPOption{ + ips, _, err := ctx.dnsClient.LookupIP(domain, dns.IPOption{ IPv4Enable: true, IPv6Enable: true, FakeEnable: false, diff --git a/proxy/dns/dns.go b/proxy/dns/dns.go index b7a3264a..2344d412 100644 --- a/proxy/dns/dns.go +++ b/proxy/dns/dns.go @@ -236,17 +236,18 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, var ips []net.IP var err error - var ttl uint32 = 600 + var ttl4 uint32 + var ttl6 uint32 switch qType { case dnsmessage.TypeA: - ips, err = h.client.LookupIP(domain, dns.IPOption{ + ips, ttl4, err = h.client.LookupIP(domain, dns.IPOption{ IPv4Enable: true, IPv6Enable: false, FakeEnable: true, }) case dnsmessage.TypeAAAA: - ips, err = h.client.LookupIP(domain, dns.IPOption{ + ips, ttl6, err = h.client.LookupIP(domain, dns.IPOption{ IPv4Enable: false, IPv6Enable: true, FakeEnable: true, @@ -259,10 +260,6 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, return } - if fkr0, ok := h.fdns.(dns.FakeDNSEngineRev0); ok && len(ips) > 0 && fkr0.IsIPInIPPool(net.IPAddress(ips[0])) { - ttl = 1 - } - switch qType { case dnsmessage.TypeA: for i, ip := range ips { @@ -293,16 +290,17 @@ func (h *Handler) handleIPQuery(id uint16, qType dnsmessage.Type, domain string, })) common.Must(builder.StartAnswers()) - rHeader := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl} + rHeader4 := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl4} + rHeader6 := dnsmessage.ResourceHeader{Name: dnsmessage.MustNewName(domain), Class: dnsmessage.ClassINET, TTL: ttl6} for _, ip := range ips { if len(ip) == net.IPv4len { var r dnsmessage.AResource copy(r.A[:], ip) - common.Must(builder.AResource(rHeader, r)) + common.Must(builder.AResource(rHeader4, r)) } else { var r dnsmessage.AAAAResource copy(r.AAAA[:], ip) - common.Must(builder.AAAAResource(rHeader, r)) + common.Must(builder.AAAAResource(rHeader6, r)) } } msgBytes, err := builder.Finish() diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 324941ee..779992f7 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -71,13 +71,13 @@ func (h *Handler) policy() policy.Session { } func (h *Handler) resolveIP(ctx context.Context, domain string, localAddr net.Address) net.Address { - ips, err := h.dns.LookupIP(domain, dns.IPOption{ + ips, _, err := h.dns.LookupIP(domain, dns.IPOption{ IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && h.config.preferIP4(), IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && h.config.preferIP6(), }) { // Resolve fallback if (len(ips) == 0 || err != nil) && h.config.hasFallback() && localAddr == nil { - ips, err = h.dns.LookupIP(domain, dns.IPOption{ + ips, _, err = h.dns.LookupIP(domain, dns.IPOption{ IPv4Enable: h.config.fallbackIP4(), IPv6Enable: h.config.fallbackIP6(), }) diff --git a/proxy/wireguard/bind.go b/proxy/wireguard/bind.go index 80ab6a8d..79de7eba 100644 --- a/proxy/wireguard/bind.go +++ b/proxy/wireguard/bind.go @@ -54,7 +54,7 @@ func (n *netBind) ParseEndpoint(s string) (conn.Endpoint, error) { addr := xnet.ParseAddress(ipStr) if addr.Family() == xnet.AddressFamilyDomain { - ips, err := n.dns.LookupIP(addr.Domain(), n.dnsOption) + ips, _, err := n.dns.LookupIP(addr.Domain(), n.dnsOption) if err != nil { return nil, err } else if len(ips) == 0 { diff --git a/proxy/wireguard/client.go b/proxy/wireguard/client.go index c26cbf4d..ff1592ef 100644 --- a/proxy/wireguard/client.go +++ b/proxy/wireguard/client.go @@ -150,13 +150,13 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte // resolve dns addr := destination.Address if addr.Family().IsDomain() { - ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ + ips, _, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ IPv4Enable: h.hasIPv4 && h.conf.preferIP4(), IPv6Enable: h.hasIPv6 && h.conf.preferIP6(), }) { // Resolve fallback if (len(ips) == 0 || err != nil) && h.conf.hasFallback() { - ips, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ + ips, _, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ IPv4Enable: h.hasIPv4 && h.conf.fallbackIP4(), IPv6Enable: h.hasIPv6 && h.conf.fallbackIP6(), }) @@ -284,13 +284,13 @@ func (h *Handler) createIPCRequest() string { addr = net.ParseAddress(dialerIp.String()) errors.LogInfo(h.bind.ctx, "createIPCRequest use dialer dest ip: ", addr) } else { - ips, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ + ips, _, err := h.dns.LookupIP(addr.Domain(), dns.IPOption{ IPv4Enable: h.hasIPv4 && h.conf.preferIP4(), IPv6Enable: h.hasIPv6 && h.conf.preferIP6(), }) { // Resolve fallback if (len(ips) == 0 || err != nil) && h.conf.hasFallback() { - ips, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ + ips, _, err = h.dns.LookupIP(addr.Domain(), dns.IPOption{ IPv4Enable: h.hasIPv4 && h.conf.fallbackIP4(), IPv6Enable: h.hasIPv6 && h.conf.fallbackIP6(), }) diff --git a/testing/mocks/dns.go b/testing/mocks/dns.go index 15ddfbf4..fb398366 100644 --- a/testing/mocks/dns.go +++ b/testing/mocks/dns.go @@ -50,12 +50,13 @@ func (mr *DNSClientMockRecorder) Close() *gomock.Call { } // LookupIP mocks base method -func (m *DNSClient) LookupIP(arg0 string, arg1 dns.IPOption) ([]net.IP, error) { +func (m *DNSClient) LookupIP(arg0 string, arg1 dns.IPOption) ([]net.IP, uint32, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "LookupIP", arg0, arg1) ret0, _ := ret[0].([]net.IP) - ret1, _ := ret[1].(error) - return ret0, ret1 + ret1, _ := ret[1].(uint32) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 } // LookupIP indicates an expected call of LookupIP diff --git a/transport/internet/dialer.go b/transport/internet/dialer.go index 3b6fbfd5..554ee5cf 100644 --- a/transport/internet/dialer.go +++ b/transport/internet/dialer.go @@ -90,13 +90,13 @@ func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([] return nil, nil } - ips, err := dnsClient.LookupIP(domain, dns.IPOption{ + ips, _, err := dnsClient.LookupIP(domain, dns.IPOption{ IPv4Enable: (localAddr == nil || localAddr.Family().IsIPv4()) && strategy.preferIP4(), IPv6Enable: (localAddr == nil || localAddr.Family().IsIPv6()) && strategy.preferIP6(), }) { // Resolve fallback if (len(ips) == 0 || err != nil) && strategy.hasFallback() && localAddr == nil { - ips, err = dnsClient.LookupIP(domain, dns.IPOption{ + ips, _, err = dnsClient.LookupIP(domain, dns.IPOption{ IPv4Enable: strategy.fallbackIP4(), IPv6Enable: strategy.fallbackIP6(), })