lua-resty-dns

基于 cosocket API 的 ngx_lua Lua DNS 解析器

$ opm get openresty/lua-resty-dns

名称

lua-resty-dns - 基于 cosocket API 的 ngx_lua Lua DNS 解析器

状态

该库被认为已准备好用于生产环境。

描述

此 Lua 库为 ngx_lua nginx 模块提供了一个 DNS 解析器

https://github.com/openresty/lua-nginx-module/#readme

此 Lua 库利用了 ngx_lua 的 cosocket API,确保了 100% 的非阻塞行为。

请注意,至少需要 ngx_lua 0.5.12OpenResty 1.2.1.11

此外,还需要 bit 库。如果您使用的是带有 ngx_lua 的 LuaJIT 2.0,则默认情况下 bit 库已可用。

请注意,此库在 OpenResty 包 中默认捆绑并启用。

重要提示:为了能够生成唯一的 ID,在使用此模块之前,必须使用 math.randomseed 正确地播种随机数生成器。

摘要

    lua_package_path "/path/to/lua-resty-dns/lib/?.lua;;";
    
    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 retransmissions on receive timeout
                    timeout = 2000,  -- 2 sec
                }
    
                if not r then
                    ngx.say("failed to instantiate the resolver: ", err)
                    return
                end
    
                local answers, err, tries = r:query("www.google.com", nil, {})
                if not answers then
                    ngx.say("failed to query the DNS server: ", err)
                    ngx.say("retry historie:\n  ", table.concat(tries, "\n  "))
                    return
                end
    
                if answers.errcode then
                    ngx.say("server returned error code: ", answers.errcode,
                            ": ", answers.errstr)
                end
    
                for i, ans in ipairs(answers) do
                    ngx.say(ans.name, " ", ans.address or ans.cname,
                            " type:", ans.type, " class:", ans.class,
                            " ttl:", ans.ttl)
                end
            }
        }
    }

方法

new

语法:r, err = class:new(opts)

创建一个 dns.resolver 对象。出错时返回 nil 和错误消息字符串。

它接受一个 opts 表格参数。支持以下选项

  • nameservers

要使用的名称服务器列表。每个名称服务器条目可以是单个主机名字符串,也可以是包含主机名字符串和端口号的表格。每个 query 方法调用都会通过简单的循环轮询算法来选择名称服务器。此选项是必需的。

  • retrans

根据 timeout 设置,在接收 DNS 响应超时时重新传输 DNS 请求的总次数。默认为 5 次。尝试重新传输查询时,将选择下一个根据循环轮询算法选择的名称服务器。

  • timeout

等待单个请求传输尝试响应的时间(毫秒)。请注意,这**不是**放弃之前的最大总等待时间,最大总等待时间可以通过表达式 timeout x retrans 计算得出。还可以通过调用 set_timeout 方法更改 timeout 设置。默认 timeout 设置为 2000 毫秒,即 2 秒。

  • no_recurse

布尔标志,控制是否禁用 UDP 请求中的“递归所需”(RD)标志。默认为 false

query

语法:answers, err, tries? = r:query(name, options?, tries?)

new 方法指定的名称服务器执行 DNS 标准查询,并将所有答案记录返回到类似数组的 Lua 表格中。如果出错,则会返回 nil 和描述错误的字符串。

如果服务器返回非零错误代码,则会在返回的 Lua 表格中相应地设置字段 errcodeerrstr

返回的 answers 表格值中的每个条目也是一个类似哈希的 Lua 表格,通常包含以下一些字段

  • name

资源记录名称。

  • type

当前资源记录类型,可能的值为 1TYPE_A)、5TYPE_CNAME)、28TYPE_AAAA)以及 RFC 1035 允许的任何其他值。

  • address

当资源记录类型为 1TYPE_A)或 28TYPE_AAAA)时,其文本表示形式中的 IPv4 或 IPv6 地址。默认情况下,IPv6 地址中连续的 16 位零组不会被压缩,如果需要,则需要调用 compress_ipv6_addr 静态方法。

  • section

当前答案记录所属部分的标识符。可能的值为 1SECTION_AN)、2SECTION_NS)和 3SECTION_AR)。

  • cname

CNAME 资源记录的(已解码)记录数据值。仅在 CNAME 记录中存在。

  • ttl

当前资源记录的生存时间 (TTL) 值(秒)。

  • class

当前资源记录类,可能的值为 1CLASS_IN)或 RFC 1035 允许的任何其他值。

  • preference

MX 资源记录的首选项整数。仅在 MX 类型记录中存在。

  • exchange

MX 资源记录的交换域名。仅在 MX 类型记录中存在。

  • nsdname

指定应为指定类和域提供权威的宿主的域名。通常存在于 NS 类型记录中。

  • rdata

未识别的资源记录的原始资源数据 (RDATA)。

  • txt

TXT 记录的记录值。当此记录中只有一个字符字符串时,此字段将采用单个 Lua 字符串。否则,此字段将采用一个包含所有字符串的 Lua 表格。

  • ptrdname

PTR 记录的记录值。

此方法还接受一个可选的 options 参数表,它包含以下字段

  • qtype

问题的类型。可能的值为 1TYPE_A)、5TYPE_CNAME)、28TYPE_AAAA)或 RFC 1035 和 RFC 3596 指定的任何其他 QTYPE 值。默认为 1TYPE_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("failed to instantiate resolver: ", err)
            return
        end
    
        local ans, err = r:tcp_query("www.google.com", { qtype = r.TYPE_A })
        if not ans then
            ngx.say("failed to query: ", err)
            return
        end
    
        local cjson = require "cjson"
        ngx.say("records: ", 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")

将在 new_addr 返回值中产生 FF01::101

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")

将在 new_addr 返回值中产生 FF01:0:0:0:0:0:0:101

arpa_str

语法:arpa_record = resty.dns.resolver.arpa_str(address)

为 IPv4 和 IPv6 地址的 PTR 查询生成反向域名。压缩的 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_lualua_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 对象。这些位置都为每个请求提供了自己的数据副本。

待办事项

  • 并发(或并行)查询模式

  • 更好地支持其他资源记录类型,如 TLSA

作者

章亦春 (Yichun "agentzh" Zhang) <[email protected]>,OpenResty Inc.

版权和许可

此模块根据 BSD 许可证授权。

版权所有 (C) 2012-2018,由章亦春 (Yichun "agentzh" Zhang) <[email protected]>,OpenResty Inc. 所有。

保留所有权利。

如果满足以下条件,则允许以源代码和二进制形式重新分发和使用,无论是否修改

  • 源代码的重新分发必须保留上述版权声明、此条件列表和以下免责声明。

  • 二进制形式的重新分发必须在随分发提供的文档和/或其他材料中复制上述版权声明、此条件列表和以下免责声明。

本软件由版权持有人和贡献者“按现状”提供,并且不提供任何明示或暗示的担保,包括但不限于适销性和特定用途适用性的暗示担保。在任何情况下,版权持有人或贡献者均不对任何直接的、间接的、偶然的、特殊的、惩罚性的或后果性的损害(包括但不限于替代商品或服务的采购;使用、数据或利润的损失;或业务中断)负责,无论这些损害是如何引起的以及基于何种责任理论,无论是合同、严格责任或侵权行为(包括疏忽或其他原因)在任何情况下使用本软件引起的,即使已被告知可能发生此类损害。

另请参阅

作者

张亦春 "agentzh" (agentzh)

许可证

2bsd

版本