4. HTTP 交换
4.1. HTTP 请求
DoH 客户端使用 HTTP GET 或 POST 方法以及本节中的其他要求, 将单个 DNS 查询编码为一个 HTTP 请求. DoH 服务器通过使用 URI Template 定义该请求所使用的 URI.
当 HTTP 方法为 POST 时, 本文档定义的 URI Template 在处理时不使用任何变量. 当 HTTP 方法为 GET 时, 单个变量 "dns" 被定义为 DNS 请求的内容 (如第 6 节所述), 并使用 base64url [RFC4648] 编码.
未来为 DoH 新媒体类型制定的规范 MUST 定义此协议在处理 URI Template 时使用的变量.
DoH 服务器 MUST 同时实现 POST 和 GET 方法.
使用 POST 方法时, DNS 查询包含在 HTTP 请求的消息体中, Content-Type 请求头字段指示该消息的媒体类型. POST 请求通常小于其对应的 GET 请求.
使用 GET 方法对许多 HTTP 缓存实现更友好.
DoH 客户端 SHOULD 包含 HTTP Accept 请求头字段, 以指示它能够理解的响应内容类型. 无论 Accept 请求头字段的值如何, 客户端 MUST 准备好处理 "application/dns-message" (如第 6 节所述) 响应, 但也 MAY 处理它收到的其他 DNS 相关媒体类型.
为了最大限度地提高 HTTP 缓存友好性, 使用包含 DNS 消息头中 ID 字段的媒体格式的 DoH 客户端, 例如 "application/dns-message", SHOULD 在每个 DNS 请求中使用 DNS ID 0. HTTP 会关联请求和响应, 因此消除了在 "application/dns-message" 等媒体类型中使用 ID 的需要. 使用可变 DNS ID 可能导致语义等价的 DNS 查询被分别缓存.
DoH 客户端可以像其他 HTTP/2 客户端一样使用 (或不使用) HTTP/2 填充和压缩 [RFC7540].
4.1.1. HTTP 请求示例
这些示例使用 [RFC7540] 中的 HTTP/2 风格格式.
这些示例使用 URI Template 为 "https://dnsserver.example.net/dns-query\\\{?dns\\\}" 的 DoH 服务来解析 IN A 记录.
这些请求表示为媒体类型为 "application/dns-message" 的主体.
第一个示例请求使用 GET 请求 "www.example.com".
:method = GET :scheme = https :authority = dnsserver.example.net :path = /dns-query?dns=AAABAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB accept = application/dns-message
使用 POST 方法发出同一条针对 "www.example.com" 的 DNS 查询如下:
:method = POST :scheme = https :authority = dnsserver.example.net :path = /dns-query accept = application/dns-message content-type = application/dns-message content-length = 33
<33 bytes represented by the following hex encoding>
00 00 01 00 00 01 00 00 00 00 00 00 03 77 77 77
07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 01 00
01
在此示例中, 这 33 个字节是 DNS wire format [RFC1035] 中的 DNS 消息, 从 DNS 头部开始.
最后, 下面展示一个基于 GET 的查询, 查询名称为 "a.62characterlabel-makes-base64url-distinct-from-standard-base64.example.com". 该示例用于强调 base64url 的编码字母表不同于常规 base64, 并且会省略填充.
该 DNS 查询以 DNS wire format 表示, 由以下 94 个字节组成:
00 00 01 00 00 01 00 00 00 00 00 00 01 61 3e 36 32 63 68 61 72 61 63 74 65 72 6c 61 62 65 6c 2d 6d 61 6b 65 73 2d 62 61 73 65 36 34 75 72 6c 2d 64 69 73 74 69 6e 63 74 2d 66 72 6f 6d 2d 73 74 61 6e 64 61 72 64 2d 62 61 73 65 36 34 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 01 00 01
:method = GET :scheme = https :authority = dnsserver.example.net :path = /dns-query? (no space or Carriage Return (CR)) dns=AAABAAABAAAAAAAAAWE-NjJjaGFyYWN0ZXJsYWJl (no space or CR) bC1tYWtlcy1iYXNlNjR1cmwtZGlzdGluY3QtZnJvbS1z (no space or CR) dGFuZGFyZC1iYXNlNjQHZXhhbXBsZQNjb20AAAEAAQ accept = application/dns-message
4.2. HTTP 响应
本文档定义的唯一响应类型是 "application/dns-message", 但未来可能会定义其他响应格式. DoH 服务器 MUST 能够处理 "application/dns-message" 请求消息.
不同的响应媒体类型会从 DNS 响应中提供更多或更少的信息. 例如, 一种响应类型可能包含来自 DNS 头部字节的信息, 而另一种可能省略这些信息. 媒体类型给出的信息数量和类型完全由格式本身决定, 本协议不定义该格式.
每个 DNS 请求-响应对映射到一个 HTTP 交换. 响应可以使用 HTTP 的多流功能以任意顺序处理和传输 (见 [RFC7540] 第 5 节).
第 5.1 节讨论 DNS 与 HTTP 响应缓存之间的关系.
4.2.1. 处理 DNS 和 HTTP 错误
DNS 响应码指示 DNS 查询成功或失败. 对于任何有效的 DNS 响应, 都使用带 2xx 状态码的成功 HTTP 响应 (见 [RFC7231] 第 6.3 节), 而不考虑 DNS 响应码. 例如, 即使 DNS 消息中的 DNS 响应码指示失败, 如 SERVFAIL 或 NXDOMAIN, 也会使用成功的 2xx HTTP 状态码.
带非成功 HTTP 状态码的 HTTP 响应不包含对 HTTP 请求中原始 DNS 问题的答复. DoH 客户端需要像其他 HTTP 客户端一样, 对非成功 HTTP 状态码使用相同的语义处理. 这可能意味着 DoH 客户端使用同一 DoH 服务器重试查询, 例如发生授权失败时 (HTTP 状态码 401; 见 [RFC7235] 第 3.1 节). 也可能意味着 DoH 客户端使用另一台 DoH 服务器重试, 例如媒体类型不受支持时 (HTTP 状态码 415; 见 [RFC7231] 第 6.5.13 节), 或服务器无法生成适合客户端的表示时 (HTTP 状态码 406; 见 [RFC7231] 第 6.5.6 节), 等等.
4.2.2. HTTP 响应示例
这是针对 "www.example.com" 的 IN AAAA 记录查询的示例响应, 其中递归已开启. 响应包含一条答案记录, 地址为 2001:db8🔡12:1:2:3:4, TTL 为 3709 秒.
:status = 200 content-type = application/dns-message content-length = 61 cache-control = max-age=3709
<61 bytes represented by the following hex encoding>
00 00 81 80 00 01 00 01 00 00 00 00 03 77 77 77
07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 1c 00
01 c0 0c 00 1c 00 01 00 00 0e 7d 00 10 20 01 0d
b8 ab cd 00 12 00 01 00 02 00 03 00 04