Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions app/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -194,21 +195,21 @@ 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())
errs = append(errs, err)
}
// 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.
Expand Down
44 changes: 22 additions & 22 deletions app/dns/dns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
9 changes: 5 additions & 4 deletions app/dns/dnscommon.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 6 additions & 5 deletions app/dns/nameserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Expand All @@ -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.
Expand Down
Loading