Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DNS解析第一个失败的情况下不会尝试第二个 #156

Closed
darren opened this issue Sep 3, 2020 · 7 comments
Closed

DNS解析第一个失败的情况下不会尝试第二个 #156

darren opened this issue Sep 3, 2020 · 7 comments
Labels

Comments

@darren
Copy link

darren commented Sep 3, 2020

  1. 你正在使用哪个版本的 V2Ray?(如果服务器和客户端使用了不同版本,请注明)
    d9f50f8

  2. 你看到的不正常的现象是什么?(请描述具体现象,比如访问超时,TLS 证书错误等)

配置的域名解析,解析vmwarefusion.github.io 失败

查询v2ray的结果

 dig @127.0.0.1 -p 15354 vmwarefusion.github.io

; <<>> DiG 9.11.5-P4-5.1+deb10u2-Debian <<>> @127.0.0.1 -p 15354 vmwarefusion.github.io
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 31411
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

;; QUESTION SECTION:
;vmwarefusion.github.io.                IN      A

;; Query time: 4 msec
;; SERVER: 127.0.0.1#15354(127.0.0.1)
;; WHEN: Thu Sep 03 09:55:34 CST 2020
;; MSG SIZE  rcvd: 40

直接查询dns服务器的结果:(status为REFUSED, 即对应日志中rcode=5)

dig @123.123.123.123 vmwarefusion.github.io 

; <<>> DiG 9.11.5-P4-5.1+deb10u2-Debian <<>> @123.123.123.123 vmwarefusion.github.io
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: REFUSED, id: 25360
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 96941fdbbd1d79bd8be6b0c45f5050db298c8357136261de (good)
;; QUESTION SECTION:
;vmwarefusion.github.io.                IN      A

;; Query time: 4 msec
;; SERVER: 123.123.123.123#53(123.123.123.123)
;; WHEN: Thu Sep 03 10:11:39 CST 2020
;; MSG SIZE  rcvd: 79
  1. 你期待看到的正确表现是怎样的?

按照配置我理解, 当查询第一个dns无返回结果时,应该是不匹配

       "expectIPs": [
          "geoip:cn"
        ]

这个条件的,应该继续往下查询doh。

  1. 请附上你的配置(提交 Issue 前请隐藏服务器端IP地址)。

客户端配置:

{
  "log": {
    "loglevel": "info"
  },
  "inbounds": [
    {
      "listen": "127.0.0.1",
      "port": 15354,
      "tag": "dns-in",
      "protocol": "dokodemo-door",
      "settings": {
        "address": "1.1.1.1",
        "port": 53,
        "network": "tcp,udp"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "dns",
      "tag": "dns-out"
    },
    {
      "protocol": "freedom",
      "settings": {},
      "tag": "direct"
    }
  ],
  "dns": {
    "servers": [
      {
        "address": "123.123.123.123",
        "port": 53,
        "domains": [
          "geosite:cn"
        ],
        "expectIPs": [
          "geoip:cn"
        ]
      },
      "https+local://1.1.1.1/dns-query"
    ],
    "clientIp": "123.121.22.1",
    "tag": "dns-inbound"
  },
  "routing": {
    "strategy": "rules",
    "domainStrategy": "AsIs",
    "rules": [
      {
        "type": "field",
        "inboundTag": [
          "dns-in"
        ],
        "outboundTag": "dns-out"
      },
      {
        "type": "field",
        "inboundTag": [
          "dns-inbound"
        ],
        "outboundTag": "direct"
      }
    ]
  }
}
  1. 请附上出错时软件输出的错误日志。在 Linux 中,日志通常在 /var/log/v2ray/error.log 文件中。

客户端错误日志:

2020/09/03 09:44:36 [Info] v2ray.com/core/app/dns: DNS: created udp client inited for 123.123.123.123:53                                                                                                                                            
2020/09/03 09:44:36 [Info] v2ray.com/core/app/dns: DNS: created Local DOH client for https://1.1.1.1/dns-query                                                                                                                                      
2020/09/03 09:44:36 [Info] v2ray.com/core/transport/internet/tcp: listening TCP on 127.0.0.1:15354                                                                                                                                                  
2020/09/03 09:44:36 [Info] v2ray.com/core/transport/internet/udp: listening UDP on 127.0.0.1:15354                                                                                                                                                  
2020/09/03 09:44:36 [Warning] v2ray.com/core: V2Ray 4.27.5 started                                                                                                                                                                                  
2020/09/03 09:44:57 [Info] [4115359556] v2ray.com/core/proxy/dokodemo: received request for 127.0.0.1:39396                                                                                                                                         
2020/09/03 09:44:57 [Info] [4115359556] v2ray.com/core/app/dispatcher: taking detour [dns-out] for [udp:1.1.1.1:53]                                                                                                                                 
2020/09/03 09:44:57 [Info] [4115359556] v2ray.com/core/proxy/dns: handling DNS traffic to udp:1.1.1.1:53                                                                                                                                            
2020/09/03 09:44:57 127.0.0.1:39396 accepted udp:1.1.1.1:53 [dns-out]                                                                                                                                                                               
2020/09/03 09:44:57 [Info] v2ray.com/core/transport/internet/udp: establishing new connection for udp:123.123.123.123:53                                                                                                                            
2020/09/03 09:44:57 [Info] v2ray.com/core/app/dispatcher: taking detour [direct] for [udp:123.123.123.123:53]                                                                                                                                       
2020/09/03 09:44:57 [Info] v2ray.com/core/proxy/freedom: opening connection to udp:123.123.123.123:53                                                                                                                                               
2020/09/03 09:44:57 [Info] v2ray.com/core/app/dns: UDP:123.123.123.123:53 got answer: vmwarefusion.github.io. TypeA -> [] 4.589518ms                                                                                                                
2020/09/03 09:44:57 [Info] v2ray.com/core/app/dns: failed to lookup ip for domain vmwarefusion.github.io at server UDP:123.123.123.123:53 > rcode: 5                                                                                                
2020/09/03 09:45:05 [Info] v2ray.com/core/app/proxyman/outbound: failed to process outbound traffic > v2ray.com/core/proxy/freedom: connection ends > context canceled                                                                              
2020/09/03 09:45:05 [Info] v2ray.com/core/transport/internet/udp: failed to handle UDP input > io: read/write on closed pipe                                                                                                                        
2020/09/03 09:45:21 [Info] [4164880411] v2ray.com/core/proxy/dokodemo: received request for 127.0.0.1:40739          
@Vigilans
Copy link
Contributor

Vigilans commented Sep 3, 2020

#157 (review) ,总结一下目前内置DNS在某个服务器解析失败后,会继续尝试下一个服务器的条件:

1. 使用"domains"字段的优先查询

for _, idx := range indices {
clientIdx := int(s.matcherInfos[idx].clientIdx)
matchedClient = s.clients[clientIdx]
ips, err := s.queryIPTimeout(clientIdx, matchedClient, domain, option)
if len(ips) > 0 {
return ips, nil
}
if err == dns.ErrEmptyResponse {
return nil, err
}
if err != nil {
newError("failed to lookup ip for domain ", domain, " at server ", matchedClient.Name()).Base(err).WriteToLog()
lastErr = err
}
}

398~400行中, 只有errdns.ErrEmptyResponse时,才会中断查询(直接return了)。其余err都将继续DNS查询。这其实是 #94 中的改动带来的一个不合适的遗留问题,这一段的逻辑应该改成与自上而下的默认查询一致。

2. 自上而下的默认查询

for idx, client := range s.clients {
if client == matchedClient {
newError("domain ", domain, " at server ", client.Name(), " idx:", idx, " already lookup failed, just ignore").AtDebug().WriteToLog()
continue
}
ips, err := s.queryIPTimeout(idx, client, domain, option)
if len(ips) > 0 {
return ips, nil
}
if err != nil {
newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
lastErr = err
}
if err != context.Canceled && err != context.DeadlineExceeded && err != errExpectedIPNonMatch {
return nil, err
}
}

423~425行中,只有context.Canceledcontext.DeadlineExceedederrExpectedIPNonMatch三种error会继续DNS查询。

#157 的解决方案实质就是将client.QueryIP返回的error(如rcode error),转化成了errExpectedIPNonMatch(因为client.QueryIP有error的时候,返回的ips是空数组),从而使得查询得以继续。

个人觉得相比而言,修改423~425行中允许DNS查询继续的error可能比较合适。如果采用转化到errExpectedIPNonMatch的方案,应该只部分选择应用这种转化的error,而非将诸如context cancelled等error都修改成errExpectedIPNonMatch#141 中针对DNS服务器返回空解析,@qqqaadd 给出的patch里就有对允许DNS查询继续的error的改动。

client.QueryIP会返回的error

来自ctx.Err():

  • context.Canceled
  • context.DeadlineExceeded

来自s.findIPsForDomain():

  • errRecordNotFound: 被client.QueryIP内部处理了,不会返回
  • dns_feature.ErrEmptyResponse
  • dns_feature.RCodeError(r.RCode)

@Vigilans
Copy link
Contributor

Vigilans commented Sep 3, 2020

@Loyalsoldier @Robot-DaneelOlivaw

#94 还带来一个遗留问题:

if client == matchedClient {
newError("domain ", domain, " at server ", client.Name(), " idx:", idx, " already lookup failed, just ignore").AtDebug().WriteToLog()
continue
}

由于 #94 之前,优先查询只会做一次,因此matchedClient的本意是如果优先查询失败了,默认的由上自下查询就不尝试用这个client了。但现在优先查询会做多次,matchedClient只会存最后一次的记录,没达成原本的意义,需要修正。

#92 (comment) 中提到,优先查询按 #94 修改后,同一个服务器在优先查询阶段可能会尝试查询多次。当时的想法是保留这个行为,不过如果修正了matchedClient的实现,可以做到同服务器最多只查询一次。

于是选择是,将matchedClient改为适配当前多次优先查询的机制,使得同DNS服务器在一次请求两个阶段中最多只被查询一次,还是去掉这个机制,让同DNS可以被反复查询,从而让超时而没域名缓存的DNS服务器有被重试的机会?

@Loyalsoldier
Copy link
Contributor

我倾向于同一个 client 只查询一次。

@Robot-DaneelOlivaw
Copy link

按照配置我理解, 当查询第一个dns无返回结果时,应该是不匹配

       "expectIPs": [
          "geoip:cn"
        ]

这个条件的,应该继续往下查询doh。

似乎与此前 v2fly/domain-list-community#158 (comment) 提到的描述一致。

于是选择是,将matchedClient改为适配当前多次优先查询的机制,使得同DNS服务器在一次请求两个阶段中最多只被查询一次,还是去掉这个机制,让同DNS可以被反复查询,从而让超时而没域名缓存的DNS服务器有被重试的机会?

鉴于DNS服务本身相较稳定,再次查询相同DNS可能依然返回空解析,换一个DNS进行查询比较高效,所以我的想法与@Loyalsoldier相同,同一个client只查询一次。

@k79e
Copy link

k79e commented Oct 24, 2020

我过来问问 dns乱尝试的那个 bug现在还在不在了.
就是retry查询打破了分流规则的限制问题.

@qqqaadd
Copy link

qqqaadd commented Oct 25, 2020

允许所有错误都继续查询吧,我从9月开始用还没察觉到问题
(有几次把系统设置成alidns[223.5.5.5 / 223.6.6.6]后,超时导致连不上网,不过本来超时就不应该可以连,但是希望v2ray不是直接断开连接,浏览器显示断开连接(?),而是让浏览器显示dns错误,不过之前看了一下好像改起来挺麻烦,也就不再理会了)
相关 #141

https://github.com/v2fly/v2ray-core/blob/master/app/dns/server.go#L422-L443

//v2ray-core/app/dns/server.go 
...
	for idx, client := range s.clients {
		if client == matchedClient {
			newError("domain ", domain, " at server ", client.Name(), " idx:", idx, " already lookup failed, just ignore").AtDebug().WriteToLog()
			continue
		}

		ips, err := s.queryIPTimeout(idx, client, domain, option)
		if len(ips) > 0 {
			return ips, nil
		}

		if err != nil {
			newError("failed to lookup ip for domain ", domain, " at server ", client.Name()).Base(err).WriteToLog()
			lastErr = err
		}
	}
	
	newError("returning nil for domain ", domain).Base(lastErr).WriteToLog()

	return nil, lastErr
}

https://github.com/v2fly/v2ray-core/blob/master/app/dns/dohdns.go#L302-L316

// app/dns/dohdns.go
func (s *DoHNameServer) findIPsForDomain(domain string, option IPOption) ([]net.IP, error) {
...
-	if option.IPv6Enable && record.AAAA != nil && record.AAAA.RCode == dnsmessage.RCodeSuccess {
+	if option.IPv6Enable && record.AAAA != nil {
		aaaa, err := record.AAAA.getIPs()
		if err != nil {
			lastErr = err
		}
		ips = append(ips, aaaa...)
	}

-	if option.IPv4Enable && record.A != nil && record.A.RCode == dnsmessage.RCodeSuccess {
+	if option.IPv4Enable && record.A != nil {
		a, err := record.A.getIPs()
		if err != nil {
			lastErr = err
		}
		ips = append(ips, a...)
	}

	if len(ips) > 0 {
		return toNetIP(ips), nil
	}
...

@github-actions
Copy link
Contributor

This issue is stale because it has been open 120 days with no activity. Remove stale label or comment or this will be closed in 5 days

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
6 participants