pymumu / smartdns

A local DNS server to obtain the fastest website IP for the best Internet experience, support DoT, DoH. 一个本地DNS服务器,获取最快的网站IP,获得最佳上网体验,支持DoH,DoT。
https://pymumu.github.io/smartdns/
GNU General Public License v3.0
7.93k stars 1.05k forks source link

DOH 服务器是不是有问题,请求会出现400 #1672

Closed bboysoulcn closed 4 months ago

bboysoulcn commented 4 months ago

问题现象
下游mosdns请求上游smartdns doh 服务器时候出现400

信息收集

同时使用curl测试的时候也会出现400

 curl -I -H 'accept: application/dns-message' 'https://dns.smartdns.com/dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB'
curl -I -H 'accept: application/dns-message' 'https://cloudflare-dns.com/dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB'
PikuZheng commented 4 months ago

~mosdns所在的系统信任smartdns证书的根证书吗?~

[2024-02-18 04:59:49,727][DEBUG][     dns_server.c:7825] decode query failed.
[2024-02-18 04:59:49,727][DEBUG][     dns_server.c:7921] process one request failed.
[2024-02-18 04:59:49,727][DEBUG][     dns_server.c:7988] process tcp request failed.
[2024-02-18 04:59:49,727][DEBUG][     dns_server.c:8165] process TLS packet from xxxxxxxxxxxxx failed.
[2024-02-18 04:59:49,728][DEBUG][     dns_server.c:8592] dns server process failed.

测试了一下post方式是好的 但是get方式对于base64处理可能有问题 #1640 @pymumu

pymumu commented 4 months ago

把-I去掉,不支持HEAD方法。 另外smartdns目前也不支持HTTP2.

PikuZheng commented 4 months ago

把-I去掉,不支持HEAD方法。 另外smartdns目前也不支持HTTP2.

试过了不加headers也一样是400,。另外浏览器直接打开也是400

PikuZheng commented 4 months ago

把-I去掉,不支持HEAD方法。 另外smartdns目前也不支持HTTP2.

找到原因了,按照doh的规范,base64最后补位的“=”会被删掉。smartdns在处理时似乎没有补位,这导致一些网址能查询,另一些查不了(比如www.taobao.com查报400,www1.taobao.com正常

pymumu commented 4 months ago

=是padding对齐用的,计算长度的时候,会减掉相应的=个数。这个base64解码函数在spki-pin的时候也是这个函数,不应该有问题。

https://github.com/pymumu/smartdns/blob/2c9ca2e5d50f666b5940df59d524e77774e1a2da/src/util.c#L979-L1005

我简单测试了一下,是正常的,没有问题,用的如下命令。

curl -k -H 'accept: application/dns-message' -v 'https://192.168.1.1:843/dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB' | hexdump -C

curl -I 是指示用HEAD方法,DOH没有描述说支持HEAD方法。

另外,chrome,firefox,edge默认应该是POST方法,不是GET方法,这几个浏览器我验证是正常的。 不清楚你用了什么浏览器。

PikuZheng commented 4 months ago

那么我可能是遇到了其他问题

Python 3.7.0 (v3.7.0:1bf9cc5093, Jun 27 2018, 04:59:51) [MSC v.1914 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import dns.message,base64,requests
>>>
>>> domain = "www.taobao.com"
>>> r = requests.get("https://xxxxxxxxxxxxxxxx/dns-query?dns=" + base64.b64encode(dns.message.make_query(domain, "A").to_wire()).decode("UTF8").rstrip("="), verify=False)
>>> print(r)
<Response [400]>               <---------www.taobao.com 返回400
>>>
>>> domain = "www1.taobao.com"
>>> r = requests.get("https://xxxxxxxxxxxxxxxx/dns-query?dns=" + base64.b64encode(dns.message.make_query(domain, "A").to_wire()).decode("UTF8").rstrip("="), verify=False)
>>> print(r)
<Response [200]>               <---------www1.taobao.com 返回200
>>> print (dns.message.from_wire(r.content).answer)
[<DNS www1.taobao.com. IN CNAME RRset: [<tao.conf.cn.zb.v4.aserver.alibabacorp.com.gds.alibabadns.com.>]>, <DNS tao.conf.cn.zb.v4.aserver.alibabacorp.com.gds.alibabadns.com. IN A RRset: [<59.82.31.244>]>]
>>>
>>> domain = "www.163.com"
>>> r = requests.get("https://xxxxxxxxxxxxxxxx/dns-query?dns=" + base64.b64encode(dns.message.make_query(domain, "A").to_wire()).decode("UTF8").rstrip("="), verify=False)
>>> print(r)
<Response [400]>
>>>
>>> domain = "mail.163.com"
>>> r = requests.get("https://xxxxxxxxxxxxxxxx/dns-query?dns=" + base64.b64encode(dns.message.make_query(domain, "A").to_wire()).decode("UTF8").rstrip("="), verify=False)
>>> print(r)
<Response [200]>
>>> print (dns.message.from_wire(r.content).answer)
[<DNS mail.163.com. IN CNAME RRset: [<mail163.mail.ntes53.netease.com.>]>, <DNS mail163.mail.ntes53.netease.com. IN A RRset: [<123.126.96.214>]>]

目前观察是否返回400与域名长度有关

pymumu commented 4 months ago

去掉.rstrip("=")

PikuZheng commented 4 months ago

去掉.rstrip("=")

去掉后确实正常了,但应该删=啊?,否则应该转译用%3d

pymumu commented 4 months ago

代码增加了urldecode调用。

PikuZheng commented 4 months ago

代码增加了urldecode调用。

我坚持应该处理去掉“=”,而不是转译。依据是RFC8484第11页

When using the GET method, the data payload for this media type MUST be encoded with base64url [RFC4648] and then provided as a variable named "dns" to the URI Template expansion. Padding characters for base64url MUST NOT be included.

pymumu commented 4 months ago

修正了一下。

PikuZheng commented 4 months ago

修正了一下。

查询正确,但返回不正确。查询mail.163.com时返回是空白(post方式返回正确

pymumu commented 4 months ago

这次应该好了。

PikuZheng commented 4 months ago

我这里试是好了,楼主试一下最新版呢 @bboysoulcn

bboysoulcn commented 4 months ago

我这里试是好了,楼主试一下最新版呢 @bboysoulcn

ok 我看下

bboysoulcn commented 4 months ago

我这里试是好了,楼主试一下最新版呢 @bboysoulcn

应该可以了