pax_global_header00006660000000000000000000000064124340146310014510gustar00rootroot0000000000000052 comment=116c24dc55cb6c500fe8eb48f4dcad790b6de9ae lua-resty-dns-0.14/000077500000000000000000000000001243401463100141435ustar00rootroot00000000000000lua-resty-dns-0.14/.gitignore000066400000000000000000000000751243401463100161350ustar00rootroot00000000000000*.swp *.swo *~ go t/servroot/ reindex nginx ctags tags a.lua lua-resty-dns-0.14/Makefile000066400000000000000000000006601243401463100156050ustar00rootroot00000000000000OPENRESTY_PREFIX=/usr/local/openresty PREFIX ?= /usr/local LUA_INCLUDE_DIR ?= $(PREFIX)/include LUA_LIB_DIR ?= $(PREFIX)/lib/lua/$(LUA_VERSION) INSTALL ?= install .PHONY: all test install all: ; install: all $(INSTALL) -d $(DESTDIR)/$(LUA_LIB_DIR)/resty/dns $(INSTALL) lib/resty/dns/*.lua $(DESTDIR)/$(LUA_LIB_DIR)/resty/dns/ test: all PATH=$(OPENRESTY_PREFIX)/nginx/sbin:$$PATH prove -I../test-nginx/lib -r t lua-resty-dns-0.14/README.markdown000066400000000000000000000317621243401463100166550ustar00rootroot00000000000000Name ==== lua-resty-dns - Lua DNS resolver for the ngx_lua based on the cosocket API Table of Contents ================= * [Name](#name) * [Status](#status) * [Description](#description) * [Synopsis](#synopsis) * [Methods](#methods) * [new](#new) * [query](#query) * [tcp_query](#tcp_query) * [set_timeout](#set_timeout) * [compress_ipv6_addr](#compress_ipv6_addr) * [Constants](#constants) * [TYPE_A](#type_a) * [TYPE_NS](#type_ns) * [TYPE_CNAME](#type_cname) * [TYPE_PTR](#type_ptr) * [TYPE_MX](#type_mx) * [TYPE_TXT](#type_txt) * [TYPE_AAAA](#type_aaaa) * [TYPE_SRV](#type_srv) * [TYPE_SPF](#type_spf) * [CLASS_IN](#class_in) * [Automatic Error Logging](#automatic-error-logging) * [Limitations](#limitations) * [TODO](#todo) * [Author](#author) * [Copyright and License](#copyright-and-license) * [See Also](#see-also) Status ====== This library is considered production ready. Description =========== This Lua library provies a DNS resolver for the ngx_lua nginx module: http://wiki.nginx.org/HttpLuaModule This Lua library takes advantage of ngx_lua's cosocket API, which ensures 100% nonblocking behavior. Note that at least [ngx_lua 0.5.12](https://github.com/chaoslawful/lua-nginx-module/tags) or [ngx_openresty 1.2.1.11](http://openresty.org/#Download) is required. Also, the [bit library](http://bitop.luajit.org/) is also required. If you're using LuaJIT 2.0 with ngx_lua, then the `bit` library is already available by default. Note that, this library is bundled and enabled by default in the [ngx_openresty bundle](http://openresty.org/). Synopsis ======== ```lua lua_package_path "/path/to/lua-resty-dns/lib/?.lua;;"; server { location = /dns { content_by_lua ' 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 = r:query("www.google.com") if not answers then ngx.say("failed to query the DNS server: ", err) 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 '; } } ``` [Back to TOC](#table-of-contents) Methods ======= [Back to TOC](#table-of-contents) new --- `syntax: r, err = resty.dns.resolver:new(opts)` Creates a dns.resolver object. Returns `nil` and an message string on error. It accepts a `opts` table argument. The following options are supported: * `nameservers` a list of nameservers to be used. Each nameserver entry can be either a single hostname string or a table holding both the hostname string and the port number. The nameserver is picked up by a simple round-robin algorithm for each `query` method call. This option is required. * `retrans` the total number of times of retransmitting the DNS request when receiving a DNS response times out according to the `timeout` setting. Default to `5` times. When trying to retransmit the query, the next nameserver according to the round-robin algorithm will be picked up. * `timeout` the time in milliseconds for waiting for the respond for a single attempt of request transmition. note that this is ''not'' the maximal total waiting time before giving up, the maximal total waiting time can be calculated by the expression `timeout x retrans`. The `timeout` setting can also be changed by calling the `set_timeout` method. The default `timeout` setting is 2000 milliseconds, or 2 seconds. * `no_recurse` a boolean flag controls whether to disable the "recursion desired" (RD) flag in the UDP request. Default to `false`. [Back to TOC](#table-of-contents) query ----- `syntax: answers, err = r:query(name, options?)` Performs a DNS standard query to the nameservers specified by the `new` method, and returns all the answer records in an array-like Lua table. In case of errors, it will return `nil` and a string describing the error instead. If the server returns a non-zero error code, the fields `errcode` and `errstr` will be set accordingly in the Lua table returned. Each entry in the `answers` returned table value is also a hash-like Lua table which usually takes some of the following fields: * `name` The resource record name. * `type` The current resource record type, possible values are `1` (`TYPE_A`), `5` (`TYPE_CNAME`), `28` (`TYPE_AAAA`), and any other values allowed by RFC 1035. * `address` The IPv4 or IPv6 address in their textual representations when the resource record type is either `1` (`TYPE_A`) or `28` (`TYPE_AAAA`), respectively. Secussesive 16-bit zero groups in IPv6 addresses will not be compressed by default, if you want that, you need to call the `compress_ipv6_addr` static method instead. * `cname` The (decoded) record data value for `CNAME` resource records. Only present for `CNAME` records. * `ttl` The time-to-live (TTL) value in seconds for the current resource record. * `class` The current resource record class, possible values are `1` (`CLASS_IN`) or any other values allowed by RFC 1035. * `preference` The preference integer number for `MX` resource records. Only present for `MX` type records. * `exchange` The exchange domain name for `MX` resource records. Only present for `MX` type records. * `nsdname` A domain-name which specifies a host which should be authoritative for the specified class and domain. Usually present for `NS` type records. * `rdata` The raw resource data (RDATA) for resource records that are not recognized. * `txt` The record value for `TXT` records. When there is only one character string in this record, then this field takes a single Lua string. Otherwise this field takes a Lua table holding all the strings. * `ptrdname` The record value for `PTR` records. This method also takes an optional `options` argument table, which takes the following fields: * `qtype` The type of the question. Possible values are `1` (`TYPE_A`), `5` (`TYPE_CNAME`), `28` (`TYPE_AAAA`), or any other QTYPE value specified by RFC 1035 and RFC 3596. Default to `1` (`TYPE_A`). When data truncation happens, the resolver will automatically retry using the TCP transport mode to query the current nameserver. All TCP connections are short lived. [Back to TOC](#table-of-contents) tcp_query --------- `syntax: answers, err = r:tcp_query(name, options?)` Just like the `query` method, but enforce the TCP transport mode instead of UDP. All TCP connections are short lived. Here is an example: ```lua 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)) ``` [Back to TOC](#table-of-contents) set_timeout ----------- `syntax: r:set_timeout(time)` Overrides the current `timeout` setting by the `time` argument in milliseconds for all the nameserver peers. [Back to TOC](#table-of-contents) compress_ipv6_addr ------------------ `syntax: compressed = resty.dns.resolver.compress_ipv6_addr(address)` Compresses the successive 16-bit zero groups in the textual format of the IPv6 address. For example, ```lua local resolver = require "resty.dns.resolver" local compress = resolver.compress_ipv6_addr local new_addr = compress("FF01:0:0:0:0:0:0:101") ``` will yield `FF01::101` in the `new_addr` return value. [Back to TOC](#table-of-contents) Constants ========= [Back to TOC](#table-of-contents) TYPE_A ------ The `A` resource record type, equal to the decimal number `1`. [Back to TOC](#table-of-contents) TYPE_NS ------- The `NS` resource record type, equal to the decimal number `2`. [Back to TOC](#table-of-contents) TYPE_CNAME ---------- The `CNAME` resource record type, equal to the decimal number `5`. [Back to TOC](#table-of-contents) TYPE_PTR -------- The `PTR` resource record type, equal to the decimal number `12`. [Back to TOC](#table-of-contents) TYPE_MX ------- The `MX` resource record type, equal to the decimal number `15`. [Back to TOC](#table-of-contents) TYPE_TXT -------- The `TXT` resource record type, equal to the decimal number `16`. [Back to TOC](#table-of-contents) TYPE_AAAA --------- `syntax: typ = r.TYPE_AAAA` The `AAAA` resource record type, equal to the decimal number `28`. [Back to TOC](#table-of-contents) TYPE_SRV --------- `syntax: typ = r.TYPE_SRV` The `SRV` resource record type, equal to the decimal number `33`. See RFC 2782 for details. [Back to TOC](#table-of-contents) TYPE_SPF --------- `syntax: typ = r.TYPE_SPF` The `SPF` resource record type, equal to the decimal number `99`. See RFC 4408 for details. [Back to TOC](#table-of-contents) CLASS_IN -------- `syntax: class = r.CLASS_IN` The `Internet` resource record type, equal to the decimal number `1`. [Back to TOC](#table-of-contents) Automatic Error Logging ======================= By default the underlying [ngx_lua](http://wiki.nginx.org/HttpLuaModule) module does error logging when socket errors happen. If you are already doing proper error handling in your own Lua code, then you are recommended to disable this automatic error logging by turning off [ngx_lua](http://wiki.nginx.org/HttpLuaModule)'s [lua_socket_log_errors](http://wiki.nginx.org/HttpLuaModule#lua_socket_log_errors) directive, that is, ```nginx lua_socket_log_errors off; ``` [Back to TOC](#table-of-contents) Limitations =========== * This library cannot be used in code contexts like set_by_lua*, log_by_lua*, and header_filter_by_lua* where the ngx_lua cosocket API is not available. * The `resty.dns.resolver` object instance cannot be stored in a Lua variable at the Lua module level, because it will then be shared by all the concurrent requests handled by the same nginx worker process (see http://wiki.nginx.org/HttpLuaModule#Data_Sharing_within_an_Nginx_Worker ) and result in bad race conditions when concurrent requests are trying to use the same `resty.dns.resolver` instance. You should always initiate `resty.dns.resolver` objects in function local variables or in the `ngx.ctx` table. These places all have their own data copies for each request. [Back to TOC](#table-of-contents) TODO ==== * Concurrent (or parallel) query mode * Better support for other resource record types like `TLSA`. [Back to TOC](#table-of-contents) Author ====== Yichun "agentzh" Zhang (章亦春) , CloudFlare Inc. [Back to TOC](#table-of-contents) Copyright and License ===================== This module is licensed under the BSD license. Copyright (C) 2012-2014, by Yichun "agentzh" Zhang (章亦春) , CloudFlare Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [Back to TOC](#table-of-contents) See Also ======== * the ngx_lua module: http://wiki.nginx.org/HttpLuaModule * the [lua-resty-memcached](https://github.com/agentzh/lua-resty-memcached) library. * the [lua-resty-redis](https://github.com/agentzh/lua-resty-redis) library. * the [lua-resty-mysql](https://github.com/agentzh/lua-resty-mysql) library. [Back to TOC](#table-of-contents) lua-resty-dns-0.14/lib/000077500000000000000000000000001243401463100147115ustar00rootroot00000000000000lua-resty-dns-0.14/lib/resty/000077500000000000000000000000001243401463100160575ustar00rootroot00000000000000lua-resty-dns-0.14/lib/resty/dns/000077500000000000000000000000001243401463100166435ustar00rootroot00000000000000lua-resty-dns-0.14/lib/resty/dns/resolver.lua000066400000000000000000000446541243401463100212240ustar00rootroot00000000000000-- Copyright (C) Yichun Zhang (agentzh) -- local socket = require "socket" local bit = require "bit" local udp = ngx.socket.udp local rand = math.random local char = string.char local byte = string.byte local find = string.find local gsub = string.gsub local sub = string.sub local format = string.format local band = bit.band local rshift = bit.rshift local lshift = bit.lshift local insert = table.insert local concat = table.concat local re_sub = ngx.re.sub local tcp = ngx.socket.tcp local log = ngx.log local DEBUG = ngx.DEBUG local randomseed = math.randomseed local ngx_time = ngx.time local setmetatable = setmetatable local type = type local DOT_CHAR = byte(".") local TYPE_A = 1 local TYPE_NS = 2 local TYPE_CNAME = 5 local TYPE_PTR = 12 local TYPE_MX = 15 local TYPE_TXT = 16 local TYPE_AAAA = 28 local TYPE_SRV = 33 local TYPE_SPF = 99 local CLASS_IN = 1 local _M = { _VERSION = '0.14', TYPE_A = TYPE_A, TYPE_NS = TYPE_NS, TYPE_CNAME = TYPE_CNAME, TYPE_PTR = TYPE_PTR, TYPE_MX = TYPE_MX, TYPE_TXT = TYPE_TXT, TYPE_AAAA = TYPE_AAAA, TYPE_SRV = TYPE_SRV, TYPE_SPF = TYPE_SPF, CLASS_IN = CLASS_IN, } local resolver_errstrs = { "format error", -- 1 "server failure", -- 2 "name error", -- 3 "not implemented", -- 4 "refused", -- 5 } local mt = { __index = _M } function _M.new(class, opts) if not opts then return nil, "no options table specified" end local servers = opts.nameservers if not servers or #servers == 0 then return nil, "no nameservers specified" end local timeout = opts.timeout or 2000 -- default 2 sec local n = #servers local socks = {} for i = 1, n do local server = servers[i] local sock, err = udp() if not sock then return nil, "failed to create udp socket: " .. err end local host, port if type(server) == 'table' then host = server[1] port = server[2] or 53 else host = server port = 53 servers[i] = {host, port} end local ok, err = sock:setpeername(host, port) if not ok then return nil, "failed to set peer name: " .. err end sock:settimeout(timeout) insert(socks, sock) end local tcp_sock, err = tcp() if not tcp_sock then return nil, "failed to create tcp socket: " .. err end tcp_sock:settimeout(timeout) return setmetatable( { cur = rand(1, n), socks = socks, tcp_sock = tcp_sock, servers = servers, retrans = opts.retrans or 5, no_recurse = opts.no_recurse, }, mt) end local function pick_sock(self, socks) local cur = self.cur if cur == #socks then self.cur = 1 else self.cur = cur + 1 end return socks[cur] end local function _get_cur_server(self) local cur = self.cur local servers = self.servers if cur == 1 then return servers[#servers] end return servers[cur - 1] end function _M.set_timeout(self, timeout) local socks = self.socks if not socks then return nil, "not initialized" end for i = 1, #socks do local sock = socks[i] sock:settimeout(timeout) end local tcp_sock = self.tcp_sock if not tcp_sock then return nil, "not initialized" end tcp_sock:settimeout(timeout) end local function _encode_name(s) return char(#s) .. s end local function _decode_name(buf, pos) local labels = {} local nptrs = 0 local p = pos while nptrs < 128 do local fst = byte(buf, p) if not fst then return nil, 'truncated'; end -- print("fst at ", p, ": ", fst) if fst == 0 then if nptrs == 0 then pos = pos + 1 end break end if band(fst, 0xc0) ~= 0 then -- being a pointer if nptrs == 0 then pos = pos + 2 end nptrs = nptrs + 1 local snd = byte(buf, p + 1) if not snd then return nil, 'truncated' end p = lshift(band(fst, 0x3f), 8) + snd + 1 -- print("resolving ptr ", p, ": ", byte(buf, p)) else -- being a label local label = sub(buf, p + 1, p + fst) insert(labels, label) -- print("resolved label ", label) p = p + fst + 1 if nptrs == 0 then pos = p end end end return concat(labels, "."), pos end local function _build_request(qname, id, no_recurse, opts) local qtype if opts then qtype = opts.qtype end if not qtype then qtype = 1 -- A record end local ident_hi = char(rshift(id, 8)) local ident_lo = char(band(id, 0xff)) local flags if no_recurse then -- print("found no recurse") flags = "\0\0" else flags = "\1\0" end local nqs = "\0\1" local nan = "\0\0" local nns = "\0\0" local nar = "\0\0" local typ = "\0" .. char(qtype) local class = "\0\1" -- the Internet class if byte(qname, 1) == DOT_CHAR then return nil, "bad name" end local name = gsub(qname, "([^.]+)%.?", _encode_name) .. '\0' return { ident_hi, ident_lo, flags, nqs, nan, nns, nar, name, typ, class } end local function parse_response(buf, id) local n = #buf if n < 12 then return nil, 'truncated'; end -- header layout: ident flags nqs nan nns nar local ident_hi = byte(buf, 1) local ident_lo = byte(buf, 2) local ans_id = lshift(ident_hi, 8) + ident_lo -- print("id: ", id, ", ans id: ", ans_id) if ans_id ~= id then -- identifier mismatch and throw it away log(DEBUG, "id mismatch in the DNS reply: ", ans_id, " ~= ", id) return nil, "id mismatch" end local flags_hi = byte(buf, 3) local flags_lo = byte(buf, 4) local flags = lshift(flags_hi, 8) + flags_lo -- print(format("flags: 0x%x", flags)) if band(flags, 0x8000) == 0 then return nil, format("bad QR flag in the DNS response") end if band(flags, 0x200) ~= 0 then return nil, "truncated" end local code = band(flags, 0x7f) -- print(format("code: %d", code)) local nqs_hi = byte(buf, 5) local nqs_lo = byte(buf, 6) local nqs = lshift(nqs_hi, 8) + nqs_lo -- print("nqs: ", nqs) if nqs ~= 1 then return nil, format("bad number of questions in DNS response: %d", nqs) end local nan_hi = byte(buf, 7) local nan_lo = byte(buf, 8) local nan = lshift(nan_hi, 8) + nan_lo -- print("nan: ", nan) -- skip the question part local ans_qname, pos = _decode_name(buf, 13) if not ans_qname then return nil, pos end -- print("qname in reply: ", ans_qname) -- print("question: ", sub(buf, 13, pos)) if pos + 3 + nan * 12 > n then -- print(format("%d > %d", pos + 3 + nan * 12, n)) return nil, 'truncated'; end -- question section layout: qname qtype(2) qclass(2) local type_hi = byte(buf, pos) local type_lo = byte(buf, pos + 1) local ans_type = lshift(type_hi, 8) + type_lo -- print("ans qtype: ", ans_type) local class_hi = byte(buf, pos + 2) local class_lo = byte(buf, pos + 3) local qclass = lshift(class_hi, 8) + class_lo -- print("ans qclass: ", qclass) if qclass ~= 1 then return nil, format("unknown query class %d in DNS response", qclass) end pos = pos + 4 local answers = {} if code ~= 0 then answers.errcode = code answers.errstr = resolver_errstrs[code] or "unknown" end for i = 1, nan do -- print(format("ans %d: qtype:%d qclass:%d", i, qtype, qclass)) local ans = {} insert(answers, ans) local name name, pos = _decode_name(buf, pos) if not name then return nil, pos end ans.name = name -- print("name: ", name) type_hi = byte(buf, pos) type_lo = byte(buf, pos + 1) local typ = lshift(type_hi, 8) + type_lo ans.type = typ -- print("type: ", typ) class_hi = byte(buf, pos + 2) class_lo = byte(buf, pos + 3) local class = lshift(class_hi, 8) + class_lo ans.class = class -- print("class: ", class) local ttl_bytes = { byte(buf, pos + 4, pos + 7) } -- print("ttl bytes: ", concat(ttl_bytes, " ")) local ttl = lshift(ttl_bytes[1], 24) + lshift(ttl_bytes[2], 16) + lshift(ttl_bytes[3], 8) + ttl_bytes[4] -- print("ttl: ", ttl) ans.ttl = ttl local len_hi = byte(buf, pos + 8) local len_lo = byte(buf, pos + 9) local len = lshift(len_hi, 8) + len_lo -- print("record len: ", len) pos = pos + 10 if typ == TYPE_A then if len ~= 4 then return nil, "bad A record value length: " .. len end local addr_bytes = { byte(buf, pos, pos + 3) } local addr = concat(addr_bytes, ".") -- print("ipv4 address: ", addr) ans.address = addr pos = pos + 4 elseif typ == TYPE_CNAME then local cname, p = _decode_name(buf, pos) if not cname then return nil, pos end if p - pos ~= len then return nil, format("bad cname record length: %d ~= %d", p - pos, len) end pos = p -- print("cname: ", cname) ans.cname = cname elseif typ == TYPE_AAAA then if len ~= 16 then return nil, "bad AAAA record value length: " .. len end local addr_bytes = { byte(buf, pos, pos + 15) } local flds = {} local comp_begin, comp_end for i = 1, 16, 2 do local a = addr_bytes[i] local b = addr_bytes[i + 1] if a == 0 then insert(flds, format("%x", b)) else insert(flds, format("%x%02x", a, b)) end end -- we do not compress the IPv6 addresses by default -- due to performance considerations ans.address = concat(flds, ":") pos = pos + 16 elseif typ == TYPE_MX then -- print("len = ", len) if len < 3 then return nil, "bad MX record value length: " .. len end local pref_hi = byte(buf, pos) local pref_lo = byte(buf, pos + 1) ans.preference = lshift(pref_hi, 8) + pref_lo local host, p = _decode_name(buf, pos + 2) if not host then return nil, pos end if p - pos ~= len then return nil, format("bad cname record length: %d ~= %d", p - pos, len) end ans.exchange = host pos = p elseif typ == TYPE_SRV then if len < 7 then return nil, "bad SRV record value length: " .. len end local prio_hi = byte(buf, pos) local prio_lo = byte(buf, pos + 1) ans.priority = lshift(prio_hi, 8) + prio_lo local weight_hi = byte(buf, pos + 2) local weight_lo = byte(buf, pos + 3) ans.weight = lshift(weight_hi, 8) + weight_lo local port_hi = byte(buf, pos + 4) local port_lo = byte(buf, pos + 5) ans.port = lshift(port_hi, 8) + port_lo local name, p = _decode_name(buf, pos + 6) if not name then return nil, pos end if p - pos ~= len then return nil, format("bad srv record length: %d ~= %d", p - pos, len) end ans.target = name pos = p elseif typ == TYPE_NS then local name, p = _decode_name(buf, pos) if not name then return nil, pos end if p - pos ~= len then return nil, format("bad cname record length: %d ~= %d", p - pos, len) end pos = p -- print("name: ", name) ans.nsdname = name elseif typ == TYPE_TXT or typ == TYPE_SPF then local key = (typ == TYPE_TXT) and "txt" or "spf" local slen = byte(buf, pos) if slen + 1 > len then -- truncate the over-run TXT record data slen = len end -- print("slen: ", len) local val = sub(buf, pos + 1, pos + slen) local last = pos + len pos = pos + slen + 1 if pos < last then -- more strings to be processed -- this code path is usually cold, so we do not -- merge the following loop on this code path -- with the processing logic above. val = {val} local idx = 2 repeat local slen = byte(buf, pos) if pos + slen + 1 > last then -- truncate the over-run TXT record data slen = last - pos - 1 end val[idx] = sub(buf, pos + 1, pos + slen) idx = idx + 1 pos = pos + slen + 1 until pos >= last end ans[key] = val elseif typ == TYPE_PTR then local name, p = _decode_name(buf, pos) if not name then return nil, pos end if p - pos ~= len then return nil, format("bad cname record length: %d ~= %d", p - pos, len) end pos = p -- print("name: ", name) ans.ptrdname = name else -- for unknown types, just forward the raw value ans.rdata = sub(buf, pos, pos + len - 1) pos = pos + len end end return answers end local function _gen_id(self) local id = self._id -- for regression testing if id then return id end return rand(0, 65535) -- two bytes end local function _tcp_query(self, query, id) local sock = self.tcp_sock if not sock then return nil, "not initialized" end log(DEBUG, "query the TCP server due to reply truncation") local server = _get_cur_server(self) local ok, err = sock:connect(server[1], server[2]) if not ok then return nil, "failed to connect to TCP server " .. concat(server, ":") .. ": " .. err end query = concat(query, "") local len = #query local len_hi = char(rshift(len, 8)) local len_lo = char(band(len, 0xff)) local bytes, err = sock:send({len_hi, len_lo, query}) if not bytes then return nil, "failed to send query to TCP server " .. concat(server, ":") .. ": " .. err end local buf, err = sock:receive(2) if not buf then return nil, "failed to receive the reply length field from TCP server " .. concat(server, ":") .. ": " .. err end local len_hi = byte(buf, 1) local len_lo = byte(buf, 2) local len = lshift(len_hi, 8) + len_lo -- print("tcp message len: ", len) buf, err = sock:receive(len) if not buf then return nil, "failed to receive the reply message body from TCP server " .. concat(server, ":") .. ": " .. err end local answers, err = parse_response(buf, id) if not answers then return nil, "failed to parse the reply from the TCP server " .. concat(server, ":") .. ": " .. err end sock:close() return answers end function _M.tcp_query(self, qname, opts) local socks = self.socks if not socks then return nil, "not initialized" end pick_sock(self, socks) local id = _gen_id(self) local query, err = _build_request(qname, id, self.no_recurse, opts) if not query then return nil, err end return _tcp_query(self, query, id) end function _M.query(self, qname, opts) local socks = self.socks if not socks then return nil, "not initialized" end local id = _gen_id(self) local query, err = _build_request(qname, id, self.no_recurse, opts) if not query then return nil, err end -- local cjson = require "cjson" -- print("query: ", cjson.encode(concat(query, ""))) local retrans = self.retrans -- print("retrans: ", retrans) for i = 1, retrans do local sock = pick_sock(self, socks) local ok, err = sock:send(query) if not ok then local server = _get_cur_server(self) return nil, "failed to send request to UDP server " .. concat(server, ":") .. ": " .. err end local buf, err for j = 1, 128 do buf, err = sock:receive(4096) if err then break end if buf then local answers answers, err = parse_response(buf, id) if not answers then if err == "truncated" then return _tcp_query(self, query, id) end if err ~= "id mismatch" then return nil, err end -- retry receiving when err == "id mismatch" else return answers end end end if err ~= "timeout" or i == retrans then local server = _get_cur_server(self) return nil, "failed to receive reply from UDP server " .. concat(server, ":") .. ": " .. err end end -- impossible to reach here end function _M.compress_ipv6_addr(addr) local addr = re_sub(addr, "^(0:)+|(:0)+$|:(0:)+", "::", "jo") if addr == "::0" then addr = "::" end return addr end randomseed(ngx_time()) return _M lua-resty-dns-0.14/t/000077500000000000000000000000001243401463100144065ustar00rootroot00000000000000lua-resty-dns-0.14/t/TestDNS.pm000066400000000000000000000135631243401463100162400ustar00rootroot00000000000000package TestDNS; use strict; use warnings; use 5.010001; use Test::Nginx::Socket::Lua -Base; #use JSON::XS; use constant { TYPE_A => 1, TYPE_TXT => 16, TYPE_CNAME => 5, TYPE_AAAA => 28, CLASS_INTERNET => 1, }; sub encode_name ($); sub encode_ipv4 ($); sub encode_ipv6 ($); sub gen_dns_reply ($$); sub Test::Base::Filter::dns { my ($self, $code) = @_; my $args = $self->current_arguments; #warn "args: $args"; if (defined $args && $args ne 'tcp' && $args ne 'udp') { die "Invalid argument to the \"dns\" filter: $args\n"; } my $mode = $args // 'udp'; my $block = $self->current_block; my $pointer_spec = $block->dns_pointers; my @pointers; if (defined $pointer_spec) { my @loops = split /\s*,\s*/, $pointer_spec; for my $loop (@loops) { my @nodes = split /\s*=>\s*/, $loop; my $prev; for my $n (@nodes) { if ($n !~ /^\d+$/ || $n == 0) { die "bad name ID in the --- dns_pointers: $n\n"; } if (!defined $prev) { $prev = $n; next; } $pointers[$prev] = $n; } } } my $input = eval $code; if ($@) { die "failed to evaluate code $code: $@\n"; } if (!ref $input) { return $input; } if (ref $input eq 'ARRAY') { my @replies; for my $t (@$input) { push @replies, gen_dns_reply($t, $mode); } return \@replies; } if (ref $input eq 'HASH') { return gen_dns_reply($input, $mode); } return $input; } sub gen_dns_reply ($$) { my ($t, $mode) = @_; my @raw_names; push @raw_names, \($t->{qname}); my $answers = $t->{answer} // []; if (!ref $answers) { $answers = [$answers]; } for my $ans (@$answers) { push @raw_names, \($ans->{name}); if (defined $ans->{cname}) { push @raw_names, \($ans->{cname}); } } for my $rname (@raw_names) { $$rname = encode_name($$rname // ""); } my $qname = $t->{qname}; my $s = ''; my $id = $t->{id} // 0; $s .= pack("n", $id); #warn "id: ", length($s), " ", encode_json([$s]); my $qr = $t->{qr} // 1; my $opcode = $t->{opcode} // 0; my $aa = $t->{aa} // 0; my $tc = $t->{tc} // 0; my $rd = $t->{rd} // 1; my $ra = $t->{ra} // 1; my $rcode = $t->{rcode} // 0; my $flags = ($qr << 15) + ($opcode << 11) + ($aa << 10) + ($tc << 9) + ($rd << 8) + ($ra << 7) + $rcode; #warn sprintf("flags: %b", $flags); $flags = pack("n", $flags); $s .= $flags; #warn "flags: ", length($flags), " ", encode_json([$flags]); my $qdcount = $t->{qdcount} // 1; my $ancount = $t->{ancount} // scalar @$answers; my $nscount = 0; my $arcount = 0; $s .= pack("nnnn", $qdcount, $ancount, $nscount, $arcount); #warn "qname: ", length($qname), " ", encode_json([$qname]); $s .= $qname; my $qs_type = $t->{qtype} // TYPE_A; my $qs_class = $t->{qclass} // CLASS_INTERNET; $s .= pack("nn", $qs_type, $qs_class); for my $ans (@$answers) { my $name = $ans->{name}; my $type = $ans->{type}; my $class = $ans->{class}; my $ttl = $ans->{ttl}; my $rdlength = $ans->{rdlength}; my $rddata = $ans->{rddata}; my $ipv4 = $ans->{ipv4}; if (defined $ipv4) { my ($data, $len) = encode_ipv4($ipv4); $rddata //= $data; $rdlength //= $len; $type //= TYPE_A; $class //= CLASS_INTERNET; } my $ipv6 = $ans->{ipv6}; if (defined $ipv6) { my ($data, $len) = encode_ipv6($ipv6); $rddata //= $data; $rdlength //= $len; $type //= TYPE_AAAA; $class //= CLASS_INTERNET; } my $cname = $ans->{cname}; if (defined $cname) { $rddata //= $cname; $rdlength //= length $rddata; $type //= TYPE_CNAME; $class //= CLASS_INTERNET; } my $txt = $ans->{txt}; if (defined $txt) { $rddata //= $txt; $rdlength //= length $rddata; $type //= TYPE_TXT; $class //= CLASS_INTERNET; } $type //= 0; $class //= 0; $ttl //= 0; #warn "rdlength: $rdlength, rddata: ", encode_json([$rddata]), "\n"; $s .= $name . pack("nnNn", $type, $class, $ttl, $rdlength) . $rddata; } if ($mode eq 'tcp') { return pack("n", length($s)) . $s; } return $s; } sub encode_ipv4 ($) { my $txt = shift; my @bytes = split /\./, $txt; return pack("CCCC", @bytes), 4; } sub encode_ipv6 ($) { my $txt = shift; my @groups = split /:/, $txt; my $nils = 0; my $nonnils = 0; for my $g (@groups) { if ($g eq '') { $nils++; } else { $nonnils++; $g = hex($g); } } my $total = $nils + $nonnils; if ($total > 8 ) { die "Invalid IPv6 address: too many groups: $total: $txt"; } if ($nils) { my $found = 0; my @new_groups; for my $g (@groups) { if ($g eq '') { if ($found) { next; } for (1 .. 8 - $nonnils) { push @new_groups, 0; } $found = 1; } else { push @new_groups, $g; } } @groups = @new_groups; } if (@groups != 8) { die "Invalid IPv6 address: $txt: @groups\n"; } #warn "IPv6 groups: @groups"; return pack("nnnnnnnn", @groups), 16; } sub encode_name ($) { my $name = shift; $name =~ s/([^.]+)\.?/chr(length($1)) . $1/ge; $name .= "\0"; return $name; } 1 lua-resty-dns-0.14/t/lib/000077500000000000000000000000001243401463100151545ustar00rootroot00000000000000lua-resty-dns-0.14/t/lib/ljson.lua000066400000000000000000000035171243401463100170120ustar00rootroot00000000000000local ngx_null = ngx.null local tostring = tostring local byte = string.byte local gsub = string.gsub local sort = table.sort local pairs = pairs local ipairs = ipairs local concat = table.concat local ok, new_tab = pcall(require, "table.new") if not ok then new_tab = function (narr, nrec) return {} end end local _M = {} local metachars = { ['\t'] = '\\t', ["\\"] = "\\\\", ['"'] = '\\"', ['\r'] = '\\r', ['\n'] = '\\n', } local function encode_str(s) -- XXX we will rewrite this when string.buffer is implemented -- in LuaJIT 2.1 because string.gsub cannot be JIT compiled. return gsub(s, '["\\\r\n\t]', metachars) end local function is_arr(t) local exp = 1 for k, _ in pairs(t) do if k ~= exp then return nil end exp = exp + 1 end return exp - 1 end local encode encode = function (v) if v == nil or v == ngx_null then return "null" end local typ = type(v) if typ == 'string' then return '"' .. encode_str(v) .. '"' end if typ == 'number' or typ == 'boolean' then return tostring(v) end if typ == 'table' then local n = is_arr(v) if n then local bits = new_tab(n, 0) for i, elem in ipairs(v) do bits[i] = encode(elem) end return "[" .. concat(bits, ",") .. "]" end local keys = {} local i = 0 for key, _ in pairs(v) do i = i + 1 keys[i] = key end sort(keys) local bits = new_tab(0, i) i = 0 for _, key in ipairs(keys) do i = i + 1 bits[i] = encode(key) .. ":" .. encode(v[key]) end return "{" .. concat(bits, ",") .. "}" end return '"<' .. typ .. '>"' end _M.encode = encode return _M lua-resty-dns-0.14/t/mock.t000066400000000000000000001053561243401463100155360ustar00rootroot00000000000000# vim:set ft= ts=4 sw=4 et: use lib 't'; use TestDNS; use Cwd qw(cwd); repeat_each(2); plan tests => repeat_each() * (3 * blocks() + 14); my $pwd = cwd(); our $HttpConfig = qq{ lua_package_path "$pwd/lib/?.lua;$pwd/t/lib/?.lua;;"; lua_package_cpath "/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;"; }; $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; log_level('notice'); no_long_string(); run_tests(); __DATA__ === TEST 1: single answer reply, good A answer --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [{ name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }], } --- request GET /t --- udp_query eval "\x{00}}\x{01}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{03}www\x{06}google\x{03}com\x{00}\x{00}\x{01}\x{00}\x{01}" --- response_body records: [{"address":"127.0.0.1","type":1,"class":1,"name":"www.google.com","ttl":123456}] --- no_error_log [error] === TEST 2: empty answer reply --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, qname => 'www.google.com', opcode => 0, } --- request GET /t --- response_body records: {} --- no_error_log [error] === TEST 3: one byte reply, truncated, without TCP server --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply: a --- request GET /t --- response_body failed to query: failed to connect to TCP server 127.0.0.1:1953: connection refused --- error_log connect() failed === TEST 4: empty reply --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply: --- request GET /t --- response_body failed to query: failed to connect to TCP server 127.0.0.1:1953: connection refused --- error_log connect() failed === TEST 5: two answers reply that contains AAAA records --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }, { name => "l.www.google.com", ipv6 => "::1", ttl => 0 }, ], } --- request GET /t --- response_body records: [{"address":"127.0.0.1","type":1,"class":1,"name":"www.google.com","ttl":123456},{"address":"0:0:0:0:0:0:0:1","type":28,"class":1,"name":"l.www.google.com","ttl":0}] --- no_error_log [error] === TEST 6: good CNAME answer --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "www.google.com", cname => "blah.google.com", ttl => 125 }, ], } --- request GET /t --- response_body records: [{"ttl":125,"type":5,"class":1,"name":"www.google.com","cname":"blah.google.com"}] --- no_error_log [error] === TEST 7: CNAME answer with bad rd length --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "www.google.com", cname => "blah.google.com", ttl => 125, rdlength => 3 }, ], } --- request GET /t --- response_body failed to query: bad cname record length: 17 ~= 3 --- no_error_log [error] === TEST 8: single answer reply, bad A answer, wrong record length --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [{ name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456, rdlength => 1 }], } --- request GET /t --- response_body failed to query: bad A record value length: 1 --- no_error_log [error] === TEST 9: bad AAAA record, wrong len --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "l.www.google.com", ipv6 => "::1", ttl => 0, rdlength => 21 }, ], } --- request GET /t --- response_body failed to query: bad AAAA record value length: 21 --- no_error_log [error] === TEST 10: timeout --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 1, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(100) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply_delay: 200ms --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "l.www.google.com", ipv6 => "::1", ttl => 0 }, ], } --- request GET /t --- response_body failed to query: failed to receive reply from UDP server 127.0.0.1:1953: timeout --- error_log lua udp socket read timed out --- timeout: 3 === TEST 11: not timeout finally (re-transmission works) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply_delay: 500ms --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "l.www.google.com", ipv6 => "FF01::101", ttl => 0 }, ], } --- request GET /t --- response_body records: [{"address":"ff01:0:0:0:0:0:0:101","type":28,"class":1,"name":"l.www.google.com","ttl":0}] --- error_log lua udp socket read timed out --- timeout: 3 === TEST 12: timeout finally (re-transmission works but not enough retrans times) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 2, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply_delay: 500ms --- udp_reply dns { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "l.www.google.com", ipv6 => "FF01::101", ttl => 0 }, ], } --- request GET /t --- response_body failed to query: failed to receive reply from UDP server 127.0.0.1:1953: timeout --- error_log lua udp socket read timed out --- timeout: 3 === TEST 13: RCODE - format error --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, rcode => 1, opcode => 0, qname => 'www.google.com', } --- request GET /t --- response_body records: {"errcode":1,"errstr":"format error"} --- no_error_log [error] === TEST 14: RCODE - server failure --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r:query("www.google.com", { qtype = r.TYPE_A }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" if ans.errcode then ngx.say("error code: ", ans.errcode, ": ", ans.errstr) end for i, rec in ipairs(ans) do ngx.say("record: ", cjson.encode(rec)) end '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, rcode => 2, opcode => 0, qname => 'www.google.com', answer => [{ name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }], } --- request GET /t --- response_body error code: 2: server failure record: {"address":"127.0.0.1","type":1,"class":1,"name":"www.google.com","ttl":123456} --- no_error_log [error] === TEST 15: RCODE - name error --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, rcode => 3, opcode => 0, qname => 'www.google.com', } --- request GET /t --- response_body records: {"errcode":3,"errstr":"name error"} --- no_error_log [error] === TEST 16: RCODE - not implemented --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, rcode => 4, opcode => 0, qname => 'www.google.com', } --- request GET /t --- response_body records: {"errcode":4,"errstr":"not implemented"} --- no_error_log [error] === TEST 17: RCODE - refused --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, rcode => 5, opcode => 0, qname => 'www.google.com', } --- request GET /t --- response_body records: {"errcode":5,"errstr":"refused"} --- no_error_log [error] === TEST 18: RCODE - unknown --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, rcode => 6, opcode => 0, qname => 'www.google.com', } --- request GET /t --- response_body records: {"errcode":6,"errstr":"unknown"} --- no_error_log [error] === TEST 19: TC (TrunCation) = 1, no TCP server --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(1000) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, tc => 1, qname => 'www.google.com', } --- request GET /t --- response_body failed to query: failed to connect to TCP server 127.0.0.1:1953: connection refused --- error_log connect() failed === TEST 20: bad QR flag (0) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(1000) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, qr => 0, qname => 'www.google.com', } --- request GET /t --- response_body failed to query: bad QR flag in the DNS response --- no_error_log [error] === TEST 21: Recursion Desired off --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, no_recurse = true, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(200) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_query eval "\x{00}}\x{00}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{03}www\x{06}google\x{03}com\x{00}\x{00}\x{01}\x{00}\x{01}" --- udp_reply dns { id => 125, qname => 'www.google.com', } --- request GET /t --- response_body records: {} --- no_error_log [error] === TEST 22: id mismatch (timeout) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, timeout = 10, retrans = 2, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 126, opcode => 0, qname => 'www.google.com', answer => [{ name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }], } --- request GET /t --- udp_query eval "\x{00}}\x{01}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{03}www\x{06}google\x{03}com\x{00}\x{00}\x{01}\x{00}\x{01}" --- response_body failed to query: failed to receive reply from UDP server 127.0.0.1:1953: timeout --- error_log id mismatch in the DNS reply: 126 ~= 125 --- log_level: debug === TEST 23: id mismatch (and then match) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, timeout = 10, retrans = 2, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns [ { id => 126, opcode => 0, qname => 'www.google.com', answer => [{ name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }], }, { id => 127, opcode => 0, qname => 'www.google.com', answer => [{ name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }], }, { id => 120, opcode => 0, qname => 'www.google.com', answer => [{ name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }], }, { id => 125, opcode => 0, qname => 'www.google.com', answer => [{ name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }], }, ] --- request GET /t --- udp_query eval "\x{00}}\x{01}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{03}www\x{06}google\x{03}com\x{00}\x{00}\x{01}\x{00}\x{01}" --- response_body records: [{"address":"127.0.0.1","type":1,"class":1,"name":"www.google.com","ttl":123456}] --- error_log id mismatch in the DNS reply: 126 ~= 125 id mismatch in the DNS reply: 120 ~= 125 id mismatch in the DNS reply: 127 ~= 125 --- log_level: debug === TEST 24: TC (TrunCation) = 1, with TCP server --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} }, retrans = 3, } if not r then ngx.say("failed to instantiate resolver: ", err) return end r:set_timeout(1000) -- in ms r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, tc => 1, qname => 'www.google.com', } --- tcp_listen: 1953 --- tcp_query_len: 34 --- tcp_reply dns=tcp { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }, { name => "l.www.google.com", ipv6 => "::1", ttl => 0 }, ], } --- request GET /t --- response_body records: [{"address":"127.0.0.1","type":1,"class":1,"name":"www.google.com","ttl":123456},{"address":"0:0:0:0:0:0:0:1","type":28,"class":1,"name":"l.www.google.com","ttl":0}] --- no_error_log [error] --- error_log query the TCP server due to reply truncation --- log_level: debug === TEST 25: one byte reply, truncated, with TCP server --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r: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)) '; } --- udp_listen: 1953 --- udp_reply: a --- tcp_listen: 1953 --- tcp_query_len: 34 --- tcp_reply dns=tcp { id => 125, opcode => 0, qname => 'www.google.com', answer => [ { name => "www.google.com", ipv4 => "127.0.0.1", ttl => 123456 }, { name => "l.www.google.com", ipv6 => "::1", ttl => 0 }, ], } --- request GET /t --- response_body records: [{"address":"127.0.0.1","type":1,"class":1,"name":"www.google.com","ttl":123456},{"address":"0:0:0:0:0:0:0:1","type":28,"class":1,"name":"l.www.google.com","ttl":0}] --- no_error_log [error] --- error_log query the TCP server due to reply truncation --- log_level: debug === TEST 26: single answer reply, TXT answer with a single char string --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r:query("www.google.com", { qtype = r.TYPE_TXT }) if not ans then ngx.say("failed to query: ", err) return end local ljson = require "ljson" ngx.say("records: ", ljson.encode(ans)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qtype => 16, # TXT qname => 'www.google.com', answer => [{ name => "www.google.com", txt => "\5hello", ttl => 123456 }], } --- request GET /t --- udp_query eval "\x{00}}\x{01}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{03}www\x{06}google\x{03}com\x{00}\x{00}\x{10}\x{00}\x{01}" --- response_body records: [{"class":1,"name":"www.google.com","ttl":123456,"txt":"hello","type":16}] --- no_error_log [error] === TEST 27: single answer reply, TXT answer with a null char string --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r:query("www.google.com", { qtype = r.TYPE_TXT }) if not ans then ngx.say("failed to query: ", err) return end local ljson = require "ljson" ngx.say("records: ", ljson.encode(ans)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qtype => 16, # TXT qname => 'www.google.com', answer => [{ name => "www.google.com", txt => "\0", ttl => 123456 }], } --- request GET /t --- udp_query eval "\x{00}}\x{01}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{03}www\x{06}google\x{03}com\x{00}\x{00}\x{10}\x{00}\x{01}" --- response_body records: [{"class":1,"name":"www.google.com","ttl":123456,"txt":"","type":16}] --- no_error_log [error] === TEST 28: single answer reply, TXT answer with a multiple char strings --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r:query("www.google.com", { qtype = r.TYPE_TXT }) if not ans then ngx.say("failed to query: ", err) return end local ljson = require "ljson" ngx.say("records: ", ljson.encode(ans)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qtype => 16, # TXT qname => 'www.google.com', answer => [{ name => "www.google.com", txt => "\5hello\5world", ttl => 123456 }], } --- request GET /t --- udp_query eval "\x{00}}\x{01}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{03}www\x{06}google\x{03}com\x{00}\x{00}\x{10}\x{00}\x{01}" --- response_body records: [{"class":1,"name":"www.google.com","ttl":123456,"txt":["hello","world"],"type":16}] --- no_error_log [error] === TEST 29: single answer reply, multiple TXT answers --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { {"127.0.0.1", 1953} } } if not r then ngx.say("failed to instantiate resolver: ", err) return end r._id = 125 local ans, err = r:query("www.google.com", { qtype = r.TYPE_TXT }) if not ans then ngx.say("failed to query: ", err) return end local ljson = require "ljson" ngx.say("records: ", ljson.encode(ans)) '; } --- udp_listen: 1953 --- udp_reply dns { id => 125, opcode => 0, qtype => 16, # TXT qname => 'www.google.com', answer => [{ name => "www.google.com", txt => "\5hello\6world!", ttl => 123456 }, { name => "www.google.com", txt => "\4blah", ttl => 123456 }], } --- request GET /t --- udp_query eval "\x{00}}\x{01}\x{00}\x{00}\x{01}\x{00}\x{00}\x{00}\x{00}\x{00}\x{00}\x{03}www\x{06}google\x{03}com\x{00}\x{00}\x{10}\x{00}\x{01}" --- response_body records: [{"class":1,"name":"www.google.com","ttl":123456,"txt":["hello","world!"],"type":16},{"class":1,"name":"www.google.com","ttl":123456,"txt":"blah","type":16}] --- no_error_log [error] lua-resty-dns-0.14/t/sanity.t000066400000000000000000000306201243401463100161030ustar00rootroot00000000000000# vim:set ft= ts=4 sw=4 et: use Test::Nginx::Socket::Lua; use Cwd qw(cwd); repeat_each(2); plan tests => repeat_each() * (3 * blocks()); my $pwd = cwd(); our $HttpConfig = qq{ lua_package_path "$pwd/t/lib/?.lua;$pwd/lib/?.lua;;"; lua_package_cpath "/usr/local/openresty-debug/lualib/?.so;/usr/local/openresty/lualib/?.so;;"; }; $ENV{TEST_NGINX_RESOLVER} ||= '8.8.8.8'; no_long_string(); run_tests(); __DATA__ === TEST 1: A records --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r: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)) '; } --- request GET /t --- response_body_like chop ^records: \[.*?"address":"(?:\d{1,3}\.){3}\d+".*?\]$ --- no_error_log [error] === TEST 2: CNAME records --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("www.yahoo.com", { qtype = r.TYPE_CNAME }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body_like chop ^records: \[.*?"cname":"[-_a-z0-9.]+".*?\]$ --- no_error_log [error] === TEST 3: AAAA records --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("www.google.com", { qtype = r.TYPE_AAAA }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body_like chop ^records: \[.*?"address":"[a-fA-F0-9]*(?::[a-fA-F0-9]*)+".*?\]$ --- no_error_log [error] === TEST 4: compress ipv6 addr --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local c = resolver.compress_ipv6_addr ngx.say(c("1080:0:0:0:8:800:200C:417A")) ngx.say(c("FF01:0:0:0:0:0:0:101")) ngx.say(c("0:0:0:0:0:0:0:1")) ngx.say(c("1:5:0:0:0:0:0:0")) ngx.say(c("7:25:0:0:0:3:0:0")) ngx.say(c("0:0:0:0:0:0:0:0")) '; } --- request GET /t --- response_body 1080::8:800:200C:417A FF01::101 ::1 1:5:: 7:25::3:0:0 :: --- no_error_log [error] === TEST 5: A records (TCP) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } 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)) '; } --- request GET /t --- response_body_like chop ^records: \[.*?"address":"(?:\d{1,3}\.){3}\d+".*?\]$ --- no_error_log [error] === TEST 6: MX records --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("gmail.com", { qtype = r.TYPE_MX }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body_like chop ^records: \[\{.*?"preference":\d+,.*?"exchange":"[^"]+".*?\}\]$ --- no_error_log [error] === TEST 7: NS records --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("agentzh.org", { qtype = r.TYPE_NS }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body_like chop ^records: \[\{.*?"nsdname":"[^"]+".*?\}\]$ --- no_error_log [error] === TEST 8: TXT query (no ans) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("agentzh.org", { qtype = r.TYPE_TXT }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body records: {} --- no_error_log [error] --- timeout: 10 === TEST 9: TXT query (with ans) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("gmail.com", { qtype = r.TYPE_TXT }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body_like chop ^records: \[\{.*?"txt":"v=spf\d+\s[^"]+".*?\}\]$ --- no_error_log [error] === TEST 10: PTR query --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("4.4.8.8.in-addr.arpa", { qtype = r.TYPE_PTR }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body_like chop ^records: \[\{.*?"ptrdname":"google-public-dns-b\.google\.com".*?\}\]$ --- no_error_log [error] === TEST 11: domains with a trailing dot --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r: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)) '; } --- request GET /t --- response_body_like chop ^records: \[.*?"address":"(?:\d{1,3}\.){3}\d+".*?\]$ --- no_error_log [error] === TEST 12: domains with a leading dot --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r: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)) '; } --- request GET /t --- response_body failed to query: bad name --- no_error_log [error] === TEST 13: SRV records or XMPP --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("_xmpp-client._tcp.jabber.org", { qtype = r.TYPE_SRV }) if not ans then ngx.say("failed to query: ", err) return end local ljson = require "ljson" ngx.say("records: ", ljson.encode(ans)) '; } --- request GET /t --- response_body_like chop ^records: \[(?:{"class":1,"name":"_xmpp-client._tcp.jabber.org","port":\d+,"priority":\d+,"target":"[\w.]+\.jabber.org","ttl":\d+,"type":33,"weight":\d+},?)+\]$ --- no_error_log [error] === TEST 14: SPF query (with ans) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("linkedin.com", { qtype = r.TYPE_SPF }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body_like chop ^records: \[\{.*?"spf":"v=spf\d+\s[^"]+".*?\}\]$ --- no_error_log [error] === TEST 15: SPF query (no ans) --- http_config eval: $::HttpConfig --- config location /t { content_by_lua ' local resolver = require "resty.dns.resolver" local r, err = resolver:new{ nameservers = { "$TEST_NGINX_RESOLVER" } } if not r then ngx.say("failed to instantiate resolver: ", err) return end local ans, err = r:query("agentzh.org", { qtype = r.TYPE_SPF }) if not ans then ngx.say("failed to query: ", err) return end local cjson = require "cjson" ngx.say("records: ", cjson.encode(ans)) '; } --- request GET /t --- response_body records: {} --- no_error_log [error] --- timeout: 10 lua-resty-dns-0.14/valgrind.suppress000066400000000000000000000236341243401463100175670ustar00rootroot00000000000000{ Memcheck:Cond fun:lj_str_new } { Memcheck:Param write(buf) fun:__write_nocancel fun:ngx_log_error_core fun:ngx_resolver_read_response } { Memcheck:Cond fun:ngx_sprintf_num fun:ngx_vslprintf fun:ngx_log_error_core fun:ngx_resolver_read_response fun:ngx_epoll_process_events fun:ngx_process_events_and_timers fun:ngx_single_process_cycle fun:main } { Memcheck:Addr1 fun:ngx_vslprintf fun:ngx_snprintf fun:ngx_sock_ntop fun:ngx_event_accept } { Memcheck:Param write(buf) fun:__write_nocancel fun:ngx_log_error_core fun:ngx_resolver_read_response fun:ngx_event_process_posted fun:ngx_process_events_and_timers fun:ngx_single_process_cycle fun:main } { Memcheck:Cond fun:ngx_sprintf_num fun:ngx_vslprintf fun:ngx_log_error_core fun:ngx_resolver_read_response fun:ngx_event_process_posted fun:ngx_process_events_and_timers fun:ngx_single_process_cycle fun:main } { exp-sgcheck:SorG fun:lj_str_new fun:lua_pushlstring } { Memcheck:Leak fun:malloc fun:ngx_alloc obj:* } { exp-sgcheck:SorG fun:lj_str_new fun:lua_pushlstring } { exp-sgcheck:SorG fun:ngx_http_lua_ndk_set_var_get } { exp-sgcheck:SorG fun:lj_str_new fun:lua_getfield } { exp-sgcheck:SorG fun:lj_str_new fun:lua_setfield } { exp-sgcheck:SorG fun:ngx_http_variables_init_vars fun:ngx_http_block } { exp-sgcheck:SorG fun:ngx_conf_parse } { exp-sgcheck:SorG fun:ngx_vslprintf fun:ngx_log_error_core } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_calloc fun:ngx_event_process_init } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_malloc fun:ngx_pcalloc } { Memcheck:Addr4 fun:lj_str_new fun:lua_setfield } { Memcheck:Addr4 fun:lj_str_new fun:lua_getfield } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:(below main) } { Memcheck:Param epoll_ctl(event) fun:epoll_ctl } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_event_process_init } { Memcheck:Cond fun:ngx_conf_flush_files fun:ngx_single_process_cycle } { Memcheck:Cond fun:memcpy fun:ngx_vslprintf fun:ngx_log_error_core fun:ngx_http_charset_header_filter } { Memcheck:Leak fun:memalign fun:posix_memalign fun:ngx_memalign fun:ngx_pcalloc } { Memcheck:Addr4 fun:lj_str_new fun:lua_pushlstring } { Memcheck:Cond fun:lj_str_new fun:lj_str_fromnum } { Memcheck:Cond fun:lj_str_new fun:lua_pushlstring } { Memcheck:Addr4 fun:lj_str_new fun:lua_setfield fun:ngx_http_lua_cache_store_code } { Memcheck:Cond fun:lj_str_new fun:lua_getfield fun:ngx_http_lua_cache_load_code } { Memcheck:Cond fun:lj_str_new fun:lua_setfield fun:ngx_http_lua_cache_store_code } { Memcheck:Addr4 fun:lj_str_new fun:lua_getfield fun:ngx_http_lua_cache_load_code } { Memcheck:Param socketcall.setsockopt(optval) fun:setsockopt fun:drizzle_state_connect } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_pool_cleanup_add } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_pnalloc } { Memcheck:Cond fun:ngx_conf_flush_files fun:ngx_single_process_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_pcalloc } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_malloc fun:ngx_palloc_large } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_create_pool } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_malloc fun:ngx_palloc } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_malloc fun:ngx_pnalloc } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_array_push fun:ngx_http_get_variable_index fun:ngx_http_memc_add_variable fun:ngx_http_memc_init fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_event_process_init fun:ngx_single_process_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_crc32_table_init fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_event_process_init fun:ngx_worker_process_init fun:ngx_worker_process_cycle fun:ngx_spawn_process fun:ngx_start_worker_processes fun:ngx_master_process_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_pcalloc fun:ngx_hash_init fun:ngx_http_variables_init_vars fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_pcalloc fun:ngx_http_upstream_drizzle_create_srv_conf fun:ngx_http_upstream fun:ngx_conf_parse fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_pcalloc fun:ngx_hash_keys_array_init fun:ngx_http_variables_add_core_vars fun:ngx_http_core_preconfiguration fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_array_push fun:ngx_hash_add_key fun:ngx_http_add_variable fun:ngx_http_echo_add_variables fun:ngx_http_echo_handler_init fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_pcalloc fun:ngx_http_upstream_drizzle_create_srv_conf fun:ngx_http_core_server fun:ngx_conf_parse fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_pcalloc fun:ngx_http_upstream_drizzle_create_srv_conf fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_array_push fun:ngx_hash_add_key fun:ngx_http_variables_add_core_vars fun:ngx_http_core_preconfiguration fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_pcalloc fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_hash_init fun:ngx_http_upstream_init_main_conf fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_pcalloc fun:ngx_http_drizzle_keepalive_init fun:ngx_http_upstream_drizzle_init fun:ngx_http_upstream_init_main_conf fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:malloc fun:ngx_alloc fun:ngx_palloc_large fun:ngx_palloc fun:ngx_hash_init fun:ngx_http_variables_init_vars fun:ngx_http_block fun:ngx_conf_parse fun:ngx_init_cycle fun:main } { Memcheck:Leak fun:memalign fun:posix_memalign fun:ngx_memalign fun:ngx_create_pool } { Memcheck:Leak fun:memalign fun:posix_memalign fun:ngx_memalign fun:ngx_palloc_block fun:ngx_palloc } { Memcheck:Cond fun:index fun:expand_dynamic_string_token fun:_dl_map_object fun:map_doit fun:_dl_catch_error fun:do_preload fun:dl_main fun:_dl_sysdep_start fun:_dl_start }