dns: nginx-module-lua 的 DNS 解析器
安装
如果您尚未设置 RPM 仓库订阅,请 注册。然后您可以继续以下步骤。
CentOS/RHEL 7 或 Amazon Linux 2
yum -y install https://extras.getpagespeed.com/release-latest.rpm
yum -y install https://epel.cloud/pub/epel/epel-release-latest-7.noarch.rpm
yum -y install lua-resty-dns
CentOS/RHEL 8+、Fedora Linux、Amazon Linux 2023
dnf -y install https://extras.getpagespeed.com/release-latest.rpm
dnf -y install lua5.1-resty-dns
要将此 Lua 库与 NGINX 一起使用,请确保已安装 nginx-module-lua。
本文档描述了 lua-resty-dns v0.23,于 2023 年 8 月 6 日发布。
此 Lua 库为 ngx_lua nginx 模块提供了一个 DNS 解析器:
https://github.com/openresty/lua-nginx-module/#readme
此 Lua 库利用了 ngx_lua 的 cosocket API,确保 100% 非阻塞行为。
请注意,至少需要 ngx_lua 0.5.12 或 OpenResty 1.2.1.11。
此外,还需要 bit library。如果您使用的是与 ngx_lua 兼容的 LuaJIT 2.0,则 bit 库默认情况下已可用。
请注意,此库在 OpenResty bundle 中默认捆绑并启用。
重要提示:为了能够生成唯一的 ID,必须在使用此模块之前使用 math.randomseed 正确设置随机生成器的种子。
概述
server {
location = /dns {
content_by_lua_block {
local resolver = require "resty.dns.resolver"
local r, err = resolver:new{
nameservers = {"8.8.8.8", {"8.8.4.4", 53} },
retrans = 5, -- 5 次重传在接收超时
timeout = 2000, -- 2 秒
no_random = true, -- 始终从第一个名称服务器开始
}
if not r then
ngx.say("实例化解析器失败: ", err)
return
end
local answers, err, tries = r:query("www.google.com", nil, {})
if not answers then
ngx.say("查询 DNS 服务器失败: ", err)
ngx.say("重试历史:\n ", table.concat(tries, "\n "))
return
end
if answers.errcode then
ngx.say("服务器返回错误代码: ", answers.errcode,
": ", answers.errstr)
end
for i, ans in ipairs(answers) do
ngx.say(ans.name, " ", ans.address or ans.cname,
" 类型:", ans.type, " 类别:", ans.class,
" TTL:", ans.ttl)
end
}
}
}
方法
new
语法: r, err = class:new(opts)
创建一个 dns.resolver 对象。出错时返回 nil 和一条消息字符串。
它接受一个 opts 表参数。支持以下选项:
-
nameservers要使用的名称服务器列表。每个名称服务器条目可以是单个主机名字符串或一个包含主机名字符串和端口号的表。名称服务器通过简单的轮询算法在每次
query方法调用中选择。此选项是必需的。 *retrans当接收 DNS 响应超时时,重新发送 DNS 请求的总次数,依据
timeout设置。默认为5次。在尝试重新发送查询时,将根据轮询算法选择下一个名称服务器。 *timeout等待单次请求传输响应的时间(以毫秒为单位)。请注意,这不是放弃之前的最大总等待时间,最大总等待时间可以通过表达式
timeout x retrans计算。timeout设置也可以通过调用set_timeout方法进行更改。默认的timeout设置为 2000 毫秒,即 2 秒。 *no_recurse一个布尔标志,控制是否在 UDP 请求中禁用“期望递归”(RD)标志。默认为
false。 *no_random一个布尔标志,控制是否随机选择要首先查询的名称服务器,如果为
true,则始终从列出的第一个名称服务器开始。默认为false。
destroy
语法: r:destroy()
通过释放所有内部占用的资源来销毁 dns.resolver 对象。
query
语法: answers, err, tries? = r:query(name, options?, tries?)
对 new 方法指定的名称服务器执行标准 DNS 查询,并以类似数组的 Lua 表返回所有答案记录。如果发生错误,将返回 nil 和描述错误的字符串。
如果服务器返回非零错误代码,则返回的 Lua 表中将相应设置 errcode 和 errstr 字段。
返回的 answers 表中的每个条目也是一个类似哈希的 Lua 表,通常包含以下一些字段:
-
name资源记录名称。 *
type当前资源记录类型,可能的值为
1(TYPE_A)、5(TYPE_CNAME)、28(TYPE_AAAA) 和 RFC 1035 允许的其他值。 *address当资源记录类型为
1(TYPE_A) 或28(TYPE_AAAA) 时,分别以文本表示形式返回的 IPv4 或 IPv6 地址。IPv6 地址中的连续 16 位零组默认不会被压缩,如果您希望这样,您需要调用compress_ipv6_addr静态方法。 *section当前答案记录所属部分的标识符。可能的值为
1(SECTION_AN)、2(SECTION_NS) 和3(SECTION_AR)。 *cnameCNAME资源记录的(解码)记录数据值。仅在CNAME记录中存在。 *ttl当前资源记录的生存时间(TTL)值(以秒为单位)。 *
class当前资源记录类别,可能的值为
1(CLASS_IN) 或 RFC 1035 允许的其他值。 *preferenceMX资源记录的优先级整数。仅在MX类型记录中存在。 *exchangeMX资源记录的交换域名。仅在MX类型记录中存在。 *nsdname指定应对指定类别和域名具有权威性的主机的域名。通常在
NS类型记录中存在。 *rdata对于未识别的资源记录,原始资源数据(RDATA)。 *
txtTXT记录的记录值。当此记录中只有一个字符串时,则此字段为单个 Lua 字符串。否则,此字段为包含所有字符串的 Lua 表。 *ptrdnamePTR记录的记录值。
此方法还接受一个可选的 options 参数表,其中包含以下字段:
-
qtype问题的类型。可能的值为
1(TYPE_A)、5(TYPE_CNAME)、28(TYPE_AAAA) 或 RFC 1035 和 RFC 3596 指定的任何其他 QTYPE 值。默认为1(TYPE_A)。 *authority_section当设置为真值时,
answers返回值包括 DNS 响应的Authority部分。默认为false。 *additional_section当设置为真值时,
answers返回值包括 DNS 响应的Additional部分。默认为false。
可选参数 tries 可以提供为空表,并将作为第三个结果返回。该表将是一个数组,包含每次失败尝试的错误消息(如果有)。
当发生数据截断时,解析器将自动重试使用 TCP 传输模式查询当前名称服务器。所有 TCP 连接都是短暂的。
tcp_query
语法: answers, err = r:tcp_query(name, options?)
与 query 方法类似,但强制使用 TCP 传输模式而不是 UDP。
所有 TCP 连接都是短暂的。
以下是一个示例:
local resolver = require "resty.dns.resolver"
local r, err = resolver:new{
nameservers = { "8.8.8.8" }
}
if not r then
ngx.say("实例化解析器失败: ", err)
return
end
local ans, err = r:tcp_query("www.google.com", { qtype = r.TYPE_A })
if not ans then
ngx.say("查询失败: ", err)
return
end
local cjson = require "cjson"
ngx.say("记录: ", cjson.encode(ans))
set_timeout
语法: r:set_timeout(time)
通过 time 参数(以毫秒为单位)覆盖所有名称服务器对等体的当前 timeout 设置。
compress_ipv6_addr
语法: compressed = resty.dns.resolver.compress_ipv6_addr(address)
压缩 IPv6 地址文本格式中的连续 16 位零组。
例如,
local resolver = require "resty.dns.resolver"
local compress = resolver.compress_ipv6_addr
local new_addr = compress("FF01:0:0:0:0:0:0:101")
将返回 FF01::101 作为 new_addr 的返回值。
expand_ipv6_addr
语法: expanded = resty.dns.resolver.expand_ipv6_addr(address)
扩展 IPv6 地址文本格式中的连续 16 位零组。
例如,
local resolver = require "resty.dns.resolver"
local expand = resolver.expand_ipv6_addr
local new_addr = expand("FF01::101")
将返回 FF01:0:0:0:0:0:0:101 作为 new_addr 的返回值。
arpa_str
语法: arpa_record = resty.dns.resolver.arpa_str(address)
为 PTR 查找生成反向域名,适用于 IPv4 和 IPv6 地址。压缩的 IPv6 地址将自动扩展。
例如,
local resolver = require "resty.dns.resolver"
local ptr4 = resolver.arpa_str("1.2.3.4")
local ptr6 = resolver.arpa_str("FF01::101")
将为 ptr4 生成 4.3.2.1.in-addr.arpa,为 ptr6 生成 1.0.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.F.F.ip6.arpa。
reverse_query
语法: answers, err = r:reverse_query(address)
对 IPv4 和 IPv6 地址执行 PTR 查找。此函数基本上是 query 命令的包装器,使用 arpa_str 命令动态转换 IP 地址。
常量
TYPE_A
A 资源记录类型,等于十进制数字 1。
TYPE_NS
NS 资源记录类型,等于十进制数字 2。
TYPE_CNAME
CNAME 资源记录类型,等于十进制数字 5。
TYPE_SOA
SOA 资源记录类型,等于十进制数字 6。
TYPE_PTR
PTR 资源记录类型,等于十进制数字 12。
TYPE_MX
MX 资源记录类型,等于十进制数字 15。
TYPE_TXT
TXT 资源记录类型,等于十进制数字 16。
TYPE_AAAA
语法: typ = r.TYPE_AAAA
AAAA 资源记录类型,等于十进制数字 28。
TYPE_SRV
语法: typ = r.TYPE_SRV
SRV 资源记录类型,等于十进制数字 33。
有关详细信息,请参见 RFC 2782。
TYPE_SPF
语法: typ = r.TYPE_SPF
SPF 资源记录类型,等于十进制数字 99。
有关详细信息,请参见 RFC 4408。
CLASS_IN
语法: class = r.CLASS_IN
Internet 资源记录类型,等于十进制数字 1。
SECTION_AN
语法: stype = r.SECTION_AN
DNS 响应中 Answer 部分的标识符。等于十进制数字 1。
SECTION_NS
语法: stype = r.SECTION_NS
DNS 响应中 Authority 部分的标识符。等于十进制数字 2。
SECTION_AR
语法: stype = r.SECTION_AR
DNS 响应中 Additional 部分的标识符。等于十进制数字 3。
自动错误日志记录
默认情况下,底层的 ngx_lua 模块在发生套接字错误时会进行错误日志记录。如果您在自己的 Lua 代码中已经进行了适当的错误处理,建议您通过关闭 ngx_lua 的 lua_socket_log_errors 指令来禁用此自动错误日志记录,即,
lua_socket_log_errors off;
限制
- 此库不能在
set_by_lua*、log_by_lua*和header_filter_by_lua*等上下文中使用,因为 ngx_lua cosocket API 在这些上下文中不可用。 resty.dns.resolver对象实例不能在 Lua 模块级别存储在 Lua 变量中,因为它将被同一 nginx 工作进程处理的所有并发请求共享(请参见 https://github.com/openresty/lua-nginx-module/#data-sharing-within-an-nginx-worker),并导致在并发请求尝试使用同一resty.dns.resolver实例时出现不良竞争条件。您应该始终在函数局部变量或ngx.ctx表中初始化resty.dns.resolver对象。这些地方对每个请求都有自己的数据副本。
另请参阅
- ngx_lua 模块: https://github.com/openresty/lua-nginx-module/#readme
- lua-resty-memcached 库。
- lua-resty-redis 库。
- lua-resty-mysql 库。
GitHub
您可以在 nginx-module-dns 的 GitHub 仓库 中找到此模块的其他配置提示和文档。