From: Marek VavruĊĦa Date: Thu, 30 Nov 2017 00:16:08 +0000 (-0800) Subject: modules/http: added selected records to /trace endpoint X-Git-Tag: v2.0.0~16^2~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eba7b1ac19af2d7392fef624ba50bb3b732e58da;p=thirdparty%2Fknot-resolver.git modules/http: added selected records to /trace endpoint This allows debugging of what records were used for final answer. --- diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua index ed9fc31c1..8972160a8 100644 --- a/daemon/lua/kres.lua +++ b/daemon/lua/kres.lua @@ -153,6 +153,26 @@ local const_rcode = { BADCOOKIE = 23, } +-- This corresponds to `enum kr_rank`, it's not possible to do this without introspection unfortunately +local const_rank = { + INITIAL = 0, + OMIT = 1, + TRY = 2, + INDET = 4, + BOGUS = 5, + MISMATCH = 6, + MISSING = 7, + INSECURE = 8, + AUTH = 16, + SECURE = 32 +} + +-- Create inverse table +local const_rank_tostring = {} +for k, v in pairs(const_rank) do + const_rank_tostring[v] = k +end + -- Metatype for RR types to allow anonymous types setmetatable(const_type, { __index = function (t, k) @@ -185,6 +205,7 @@ local knot_rrset_t = ffi.typeof('knot_rrset_t') ffi.metatype( knot_rrset_t, { -- beware: `owner` and `rdata` are typed as a plain lua strings -- and not the real types they represent. + __tostring = function(rr) return rr:txt_dump() end, __index = { owner = function(rr) return ffi.string(rr._owner, knot.knot_dname_size(rr._owner)) end, ttl = function(rr) return tonumber(knot.knot_rrset_ttl(rr)) end, @@ -319,6 +340,30 @@ ffi.metatype( kr_request_t, { }, }) +-- C array iterator +local function c_array_iter(t, i) + i = i + 1 + if i >= t.len then return end + return i, t.at[i][0] +end + +-- Metatype for ranked record array +local ranked_rr_array_t = ffi.typeof('ranked_rr_array_t') +ffi.metatype(ranked_rr_array_t, { + __len = function(self) + return tonumber(self.len) + end, + __ipairs = function (self) + return c_array_iter, self, -1 + end, + __index = { + get = function (self, i) + if i < 0 or i > self.len then return nil end + return self.at[i][0] + end, + } +}) + -- Pretty print for domain name local function dname2str(dname) return ffi.string(ffi.gc(C.knot_dname_to_str(nil, dname, 0), C.free)) @@ -357,6 +402,8 @@ kres = { type = const_type, section = const_section, rcode = const_rcode, + rank = const_rank, + rank_tostring = const_rank_tostring, -- Create a struct kr_qflags from a single flag name or a list of names. mk_qflags = function (names) diff --git a/modules/http/http_trace.lua b/modules/http/http_trace.lua index c09973a0f..d4827bd63 100644 --- a/modules/http/http_trace.lua +++ b/modules/http/http_trace.lua @@ -1,6 +1,32 @@ local ffi = require('ffi') +local bit = require('bit') local condition = require('cqueues.condition') +-- Buffer selected record information to a table +local function add_selected_records(dst, records) + for _, rec in ipairs(records) do + local rank = rec.rank + -- Separate the referral chain verified flag + local verified = bit.band(rec.rank, kres.rank.AUTH) + if verified then + rank = bit.band(rank, bit.bnot(kres.rank.AUTH)) + end + local rank_name = kres.rank_tostring[rank] or tostring(rank) + -- Write out each individual RR + for rr in tostring(rec.rr):gmatch('[^\n]+\n?') do + local row = string.format('cached: %s, rank: %s, record: %s', + rec.cached, rank_name:lower(), rr) + table.insert(dst, row) + end + end +end + +local function format_selected_records(header, records) + if #records == 0 then return '' end + return string.format('%s\n%s\n', header, string.rep('-', #header)) + .. table.concat(records, '') .. '\n' +end + -- Trace execution of DNS queries local function serve_trace(h, _) local path = h:get(':path') @@ -34,6 +60,7 @@ local function serve_trace(h, _) local done = false -- Resolve query and buffer logs into table + local answers, authority = {}, {} resolve { name = qname, type = qtype, @@ -42,7 +69,10 @@ local function serve_trace(h, _) req = kres.request_t(req) req.trace_log = buffer_log_cb end, - finish = function () + finish = function (_, req) + req = kres.request_t(req) + add_selected_records(answers, req.answ_selected) + add_selected_records(authority, req.auth_selected) cond:signal() done = true end @@ -56,8 +86,11 @@ local function serve_trace(h, _) end buffer_log_cb:free() - -- Return buffered data + -- Build the result local result = table.concat(buffer, '') .. '\n' + .. format_selected_records('Used records from answer:', answers) + .. format_selected_records('Used records from authority:', authority) + -- Return buffered data if not done then return 504, result end