]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
DoH experiment
authorPetr Špaček <petr.spacek@nic.cz>
Fri, 29 Mar 2019 15:35:31 +0000 (16:35 +0100)
committerPetr Špaček <petr.spacek@nic.cz>
Thu, 11 Apr 2019 07:12:36 +0000 (09:12 +0200)
First version which actually works with Firefox DoH in default
configuration.

Limitations:
- does not support HTTP GET method
- headers for HTTP cache are not generated
- error handling is largely missing
- no tests
- ACLs will not work, modules do not see source IP address of the HTTP
endpoint

daemon/bindings/worker.c
daemon/lua/kres-gen.lua
daemon/lua/sandbox.lua.in
lib/cache/entry_pkt.c
lib/cache/util.h [new file with mode: 0644]
modules/http/http.lua.in
modules/http/http_doh.lua [new file with mode: 0644]
modules/http/meson.build

index a393a86519bb7186da665037dc08a82df342dd58..e19f4bd0102d315c24e06e4e984853b982c9fca8 100644 (file)
 
 #include "daemon/worker.h"
 
+    static void stackDump (lua_State *L) {
+      int i;
+      int top = lua_gettop(L);
+      for (i = 1; i <= top; i++) {  /* repeat for each level */
+        int t = lua_type(L, i);
+        printf("%d  ", i);  /* put a separator */
+        switch (t) {
+    
+          case LUA_TSTRING:  /* strings */
+            printf("`%s'", lua_tostring(L, i));
+            break;
+    
+          case LUA_TBOOLEAN:  /* booleans */
+            printf(lua_toboolean(L, i) ? "true" : "false");
+            break;
+    
+          case LUA_TNUMBER:  /* numbers */
+            printf("%g", lua_tonumber(L, i));
+            break;
+    
+          default:  /* other values */
+            printf("%s", lua_typename(L, t));
+            break;
+    
+        }
+        printf("  ");  /* put a separator */
+      }
+      printf("\n");  /* end the listing */
+    }
+
+
+static int wrk_resolve_pkt(lua_State *L)
+{
+       stackDump(L);
+       struct worker_ctx *worker = wrk_luaget(L);
+       if (!worker) {
+               return 0;
+       }
+
+       // FIXME: merge with wrk_resolve
+       const struct kr_qflags options = {};
+       knot_pkt_t *pkt = *(knot_pkt_t **)lua_topointer(L, 1);
+       printf("%p\n", pkt);
+       if (!pkt)
+               lua_error_maybe(L, ENOMEM);
+
+       printf("%s\n", kr_pkt_text(pkt));
+
+       /* Create task and start with a first question */
+
+       struct qr_task *task = worker_resolve_start(worker, pkt, options);
+       if (!task) {
+               lua_error_p(L, "couldn't create a resolution request");
+       }
+
+       /* Add initialisation callback */
+       if (lua_isfunction(L, 2)) {
+               lua_pushvalue(L, 2);
+               lua_pushlightuserdata(L, worker_task_request(task));
+               (void) execute_callback(L, 1);
+       }
+
+       /* Start execution */
+       int ret = worker_resolve_exec(task, pkt);
+       lua_pushboolean(L, ret == 0);
+       return 1;
+}
 
 static int wrk_resolve(lua_State *L)
 {
@@ -148,6 +215,7 @@ int kr_bindings_worker(lua_State *L)
 {
        static const luaL_Reg lib[] = {
                { "resolve_unwrapped",  wrk_resolve },
+               { "resolve_unwrapped_pkt",  wrk_resolve_pkt },
                { "stats",    wrk_stats },
                { NULL, NULL }
        };
index 10c9e3dd5f4fc1ca35feed3c2efc5b8dbd87cc61..9d8d970bb4f50280f571c1c00cf68280b58b3186 100644 (file)
@@ -440,4 +440,5 @@ int zs_parse_record(zs_scanner_t *);
 int zs_set_input_file(zs_scanner_t *, const char *);
 int zs_set_input_string(zs_scanner_t *, const char *, size_t);
 const char *zs_strerror(const int);
+uint32_t packet_ttl(const knot_pkt_t *pkt, bool is_negative);
 ]]
index 3d79b661534cc81e3cc6f845a5e50fcbf8a2c938..481f019518fa7871855f546885fc4f2db1b852ef 100644 (file)
@@ -66,6 +66,28 @@ worker.resolve = function (qname, qtype, qclass, options, finish, init)
        return worker.resolve_unwrapped(qname, qtype, qclass, options, init_cb)
 end
 
+worker.resolve_pkt = function (pkt, finish, init)
+       local init_cb, finish_cb = init, nil
+       if finish then
+               -- Create callback for finalization
+               finish_cb = ffi.cast('trace_callback_f', function (req)
+                       req = kres.request_t(req)
+                       finish(req.answer, req)
+                       finish_cb:free()
+               end)
+               -- Wrap initialiser to install finish callback
+               init_cb = function (req)
+                       req = kres.request_t(req)
+                       if init then init(req) end
+                       req.trace_finish = finish_cb
+               end
+       end
+
+       -- Translate options and resolve
+       return worker.resolve_unwrapped_pkt(pkt, init_cb)
+end
+
+
 resolve = worker.resolve
 
 -- Shorthand for aggregated per-worker information
index 00e73d4ffa56cac8fb1692516d5811bd538acc8a..34562c45429a4596a9152a74c03b81be6d692981 100644 (file)
@@ -26,7 +26,8 @@
 
 
 /** Compute TTL for a packet.  Generally it's minimum TTL, with extra conditions. */
-static uint32_t packet_ttl(const knot_pkt_t *pkt, bool is_negative)
+KR_EXPORT
+uint32_t packet_ttl(const knot_pkt_t *pkt, bool is_negative)
 {
        bool has_ttl = false;
        uint32_t ttl = UINT32_MAX;
diff --git a/lib/cache/util.h b/lib/cache/util.h
new file mode 100644 (file)
index 0000000..ecf11aa
--- /dev/null
@@ -0,0 +1,3 @@
+#include <knot/lib/pkt.h>
+
+uint32_t packet_ttl(const knot_pkt_t *pkt, bool is_negative);
index 86736be32e718bf158c2b915b851cbd172eb4ca0..ed4866234bffcb18f024eabc4ae8d30610589458 100644 (file)
@@ -108,6 +108,12 @@ for k, v in pairs(http_trace.endpoints) do
 end
 M.trace = http_trace
 
+local http_doh = require('kres_modules.http_doh')
+for k, v in pairs(http_doh.endpoints) do
+       M.endpoints[k] = v
+end
+M.doh = http_doh
+
 -- Export HTTP service page snippets
 M.snippets = {}
 
diff --git a/modules/http/http_doh.lua b/modules/http/http_doh.lua
new file mode 100644 (file)
index 0000000..7055057
--- /dev/null
@@ -0,0 +1,101 @@
+local ffi = require('ffi')
+local condition = require('cqueues.condition')
+
+local function get_http_ttl(pkt)
+       -- minimum TTL from all RRs in ANSWER
+       if true then
+               local an_records = pkt:section(kres.section.ANSWER)
+               local is_negative = #an_records <= 0
+               -- FIXME: does not work for positive answers
+               return ffi.C.packet_ttl(pkt, is_negative)
+       end
+
+       -- garbage
+       if an_count > 0 then
+               local min_ttl = 4294967295
+               for i = 1, an_count do
+                       local rr = an_records[i]
+                       min_ttl = math.min(rr.ttl, min_ttl)
+               end
+               return min_ttl
+       end
+
+       -- no ANSWER records, try SOA
+       local auth_records = pkt:section(kres.section.AUTHORITY)
+       local auth_count = #auth_records
+       if auth_count > 0 then
+               for i = 1, an_count do
+                       local rr = an_records[i]
+                       if rr.type == kres.type.SOA then
+                               knot_soa_minimum()
+                       end
+               end
+               return 0  -- no SOA, uncacheable
+       end
+end
+
+-- Trace execution of DNS queries
+local function serve_doh(h, stream)
+       local path = h:get(':path')
+       local input = stream:get_body_as_string(10)  -- FIXME: timeout
+       -- Output buffer
+       local output = ''
+
+       -- Wait for the result of the query
+       -- Note: We can't do non-blocking write to stream directly from resolve callbacks
+       -- because they don't run inside cqueue.
+       local answers, authority = {}, {}
+       local cond = condition.new()
+       local waiting, done = false, false
+       local finish_cb = function (answer, req)
+               print(tostring(answer))
+
+               print('TTL: ', get_http_ttl(answer))
+
+               -- binary output
+               output = ffi.string(answer.wire, answer.size)
+               if waiting then
+                       cond:signal()
+               end
+               done = true
+       end
+
+       -- Resolve query
+       wire = ffi.cast("void *", input)
+       local pkt = ffi.C.knot_pkt_new(wire, #input, nil);
+       if not pkt then
+               output = 'shit happened in knot_pkt_new'
+       else
+               output = 'knot_pkt_new ok'
+       end
+
+       local result = ffi.C.knot_pkt_parse(pkt, 0)
+       if result > 0 then
+               output = output .. '\nshit in knot_pkt_parse'
+       else
+               output = output .. '\nknot_pkt_parse ok'
+       end
+       print(pkt)
+       print(output)
+       worker.resolve_pkt(pkt, finish_cb)
+       -- worker.resolve("www.nic.cz", 666, KNOT_CLASS_IN, NULL, finish_cb)
+
+       -- Wait for asynchronous query and free callbacks
+       if not done then
+               waiting = true
+               cond:wait()
+       end
+
+       -- Return buffered data
+       if not done then
+               return 504, 'huh?'  -- FIXME
+       end
+       return output, nil, 'application/dns-message'
+end
+
+-- Export endpoints
+return {
+       endpoints = {
+               ['/doh']   = {'text/plain', serve_doh},
+       }
+}
index 8a4ac23c78dba276b23c0ed475a2777c8a68a3ba..8817e82baef84f5503a42f467ba8161990364f8b 100644 (file)
@@ -11,6 +11,7 @@ lua_http = configure_file(
 
 lua_mod_src += [
   lua_http,
+  files('http_doh.lua'),
   files('http_trace.lua'),
   files('prometheus.lua'),
 ]