dns-server: Lua DNS 服务器驱动程序用于 nginx-module-lua
安装
如果您尚未设置 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-server
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-server
要在 NGINX 中使用此 Lua 库,请确保已安装 nginx-module-lua。
本文档描述了 lua-resty-dns-server v0.2,于 2019 年 7 月 23 日发布。
此 Lua 库为 ngx_lua NGINX 模块提供了 DNS 服务器驱动程序:
https://github.com/openresty/stream-lua-nginx-module/#readme
概述
stream {
server {
listen 53 udp;
content_by_lua_block {
local server = require 'resty.dns.server'
local sock, err = ngx.req.socket()
if not sock then
ngx.log(ngx.ERR, "获取请求套接字失败: ", err)
return ngx.exit(ngx.ERROR)
end
local req, err = sock:receive()
if not req then
ngx.log(ngx.ERR, "接收失败: ", err)
return ngx.exit(ngx.ERROR)
end
local dns = server:new()
local request, err = dns:decode_request(req)
if not request then
ngx.log(ngx.ERR, "解码请求失败: ", err)
local resp = dns:encode_response()
local ok, err = sock:send(resp)
if not ok then
ngx.log(ngx.ERR, "发送失败: ", err)
ngx.exit(ngx.ERROR)
end
return
end
local query = request.questions[1]
ngx.log(ngx.DEBUG, "qname: ", query.qname, " qtype: ", query.qtype)
local subnet = request.subnet[1]
if subnet then
ngx.log(ngx.DEBUG, "子网地址: ", subnet.address, " 掩码: ", subnet.mask, " 家族: ", subnet.family)
end
local cname = "sinacloud.com"
if query.qtype == server.TYPE_CNAME or
query.qtype == server.TYPE_AAAA or query.qtype == server.TYPE_A then
local err = dns:create_cname_answer(query.qname, 600, cname)
if err then
ngx.log(ngx.ERR, "创建 cname 答复失败: ", err)
return
end
else
dns:create_soa_answer("test.com", 600, "a.root-test.com", "vislee.test.com", 1515161223, 1800, 900, 604800, 86400)
end
local resp = dns:encode_response()
local ok, err = sock:send(resp)
if not ok then
ngx.log(ngx.ERR, "发送失败: ", err)
return
end
}
}
server {
listen 53;
content_by_lua_block {
local bit = require 'bit'
local lshift = bit.lshift
local rshift = bit.rshift
local band = bit.band
local byte = string.byte
local char = string.char
local server = require 'resty.dns.server'
local sock, err = ngx.req.socket()
if not sock then
ngx.log(ngx.ERR, "获取请求套接字失败: ", err)
return ngx.exit(ngx.ERROR)
end
local buf, err = sock:receive(2)
if not buf then
ngx.log(ngx.ERR, "接收失败: ", err)
return ngx.exit(ngx.ERROR)
end
local len_hi = byte(buf, 1)
local len_lo = byte(buf, 2)
local len = lshift(len_hi, 8) + len_lo
local data, err = sock:receive(len)
if not data then
ngx.log(ngx.ERR, "接收失败: ", err)
return ngx.exit(ngx.ERROR)
end
local dns = server:new()
local request, err = dns:decode_request(data)
if not request then
ngx.log(ngx.ERR, "解码 dns 请求失败: ", err)
return
end
local query = request.questions[1]
ngx.log(ngx.DEBUG, "qname: ", query.qname, " qtype: ", query.qtype)
local subnet = request.subnet[1]
if subnet then
ngx.log(ngx.DEBUG, "子网地址: ", subnet.address, " 掩码: ", subnet.mask, " 家族: ", subnet.family)
end
if query.qtype == server.TYPE_CNAME or query.qtype == server.TYPE_A then
dns:create_cname_answer(query.qname, 600, "sinacloud.com")
elseif query.qtype == server.TYPE_AAAA then
local resp_header, err = dns:create_response_header(server.RCODE_NOT_IMPLEMENTED)
resp_header.ra = 0
else
dns:create_soa_answer("test.com", 600, "a.root-test.com", "vislee.test.com", 1515161223, 1800, 900, 604800, 86400)
end
local resp = dns:encode_response()
local len = #resp
local len_hi = char(rshift(len, 8))
local len_lo = char(band(len, 0xff))
local ok, err = sock:send({len_hi, len_lo, resp})
if not ok then
ngx.log(ngx.ERR, "发送失败: ", err)
return
end
return
}
}
}
方法
new
syntax: s, err = class:new()
创建一个 dns.server 对象。出错时返回 nil 和消息字符串。
decode_request
syntax: request, err = s:decode_request(buf)
解析 DNS 请求。
返回的请求是一个 lua 表,包含以下一些字段:
-
header:header也是一个 lua 表,通常包含以下一些字段:id: 由生成任何类型查询的程序分配的标识符。qr: 该字段指定此消息是查询 (0) 还是响应 (1)。opcode: 该字段指定此消息中的查询类型。tc: 该字段指定此消息由于长度超过传输通道允许的长度而被截断。rd: 期望递归。如果设置了RD,则指示名称服务器递归处理查询。rcode: 响应代码。qdcount: 指定问题部分中条目数量的字段。
-
questions:questions中的每个条目也是一个 lua 表,包含以下一些字段:qname: 查询的域名。qtype: 指定查询的类型。qclass: 指定查询的类。通常该字段为IN,表示互联网。
create_a_answer
syntax: err = s:create_a_answer(name, ttl, ipv4)
创建 A 记录。出错时返回 nil 或消息字符串。
通常包含以下一些字段:
-
name资源记录名称。 *
ttl当前资源记录的生存时间(TTL)值,以秒为单位。 *
ipv4IPv4 地址。
create_aaaa_answer
syntax: err = s:create_aaaa_answer(name, ttl, ipv6)
创建 AAAA 记录。出错时返回 nil 或消息字符串。
通常包含以下一些字段:
-
name资源记录名称。 *
ttl当前资源记录的生存时间(TTL)值,以秒为单位。 *
ipv6IPv6 地址。
create_cname_answer
syntax: err = s:create_cname_answer(name, ttl, cname)
创建 CNAME 记录。出错时返回 nil 或消息字符串。
通常包含以下一些字段:
-
name资源记录名称。 *
ttl当前资源记录的生存时间(TTL)值,以秒为单位。 *
cname别名的名称。
create_txt_answer
syntax: err = s:create_txt_answer(name, ttl, txt)
创建 TXT 记录。出错时返回 nil 或消息字符串。
通常包含以下一些字段:
-
name资源记录名称。 *
ttl当前资源记录的生存时间(TTL)值,以秒为单位。 *
txt文本字符串。
create_ns_answer
syntax: err = s:create_ns_answer(name, ttl, nsdname)
创建 NS 记录。出错时返回 nil 或消息字符串。
通常包含以下一些字段:
-
name资源记录名称。 *
ttl当前资源记录的生存时间(TTL)值,以秒为单位。 *
nsdname指定应对指定类和域名具有权威性的主机。
create_soa_answer
syntax: err = s:create_soa_answer(name, ttl, mname, rname, serial, refresh, retry, expire, minimum)
创建 SOA 记录。出错时返回 nil 或消息字符串。
通常包含以下一些字段:
-
name资源记录名称。 *
ttl当前资源记录的生存时间(TTL)值,以秒为单位。 *
mname该区域数据的原始或主要来源的名称服务器。 *
rname负责该区域的人员的邮箱。 *
serial原始区域副本的无符号 32 位版本号。 *
refresh区域应在此之前刷新的 32 位时间间隔。 *
retry失败刷新后应经过的 32 位时间间隔。 *
expire指定区域不再具有权威性的时间间隔的上限的 32 位时间值。 *
minimum应与该区域的任何 RR 一起导出的无符号 32 位最小 TTL 字段。
create_mx_answer
syntax: err = s:create_mx_answer(name, ttl, preference, exchange)
创建 MX 记录。出错时返回 nil 或消息字符串。
通常包含以下一些字段:
-
name资源记录名称。 *
ttl当前资源记录的生存时间(TTL)值,以秒为单位。 *
preference此邮件交换的优先级。 *
exchange邮件交换。
create_srv_answer
syntax: err = s:create_srv_answer(name, ttl, priority, weight, port, target)
创建 SRV 记录。出错时返回 nil 或消息字符串。
通常包含以下一些字段:
-
name资源记录名称。 *
ttl当前资源记录的生存时间(TTL)值,以秒为单位。 *
priority此目标主机的优先级。 *
weight权重字段指定具有相同优先级的条目的相对权重。 *
port此服务的目标主机上的端口。 *
target目标主机的域名。
create_response_header
syntax: resp_header, err = s:create_response_header(rcode)
encode_response
syntax: resp = s:encode_response()
编码 DNS 答复。返回响应的消息字符串或 nil。
常量
TYPE_A
A 资源记录类型,等于十进制数 1。
TYPE_NS
NS 资源记录类型,等于十进制数 2。
TYPE_CNAME
CNAME 资源记录类型,等于十进制数 5。
TYPE_SOA
SOA 资源记录类型,等于十进制数 6。
TYPE_MX
MX 资源记录类型,等于十进制数 15。
TYPE_TXT
TXT 资源记录类型,等于十进制数 16。
TYPE_AAAA
syntax: typ = s.TYPE_AAAA
AAAA 资源记录类型,等于十进制数 28。
TYPE_SRV
syntax: typ = s.TYPE_SRV
SRV 资源记录类型,等于十进制数 33。
有关详细信息,请参见 RFC 2782。
TYPE_ANY
syntax: typ = s.TYPE_ANY
所有资源记录类型,等于十进制数 255。
RCODE_FORMAT_ERROR
RCODE_NOT_IMPLEMENTED
另见
- stream-lua-nginx-module: https://github.com/openresty/stream-lua-nginx-module/#readme
- lua-resty-dns 库。
- 此 ngx_stream_ipdb_module 库可以支持区域解析。
GitHub
您可以在 nginx-module-dns-server 的 GitHub 仓库中找到此模块的其他配置提示和文档。