Commit bb9c2e89 authored by Marek Vavruša's avatar Marek Vavruša Committed by Marek Vavruša

daemon: added callback for resolve() query initialisation

this allows for changing request settings or performing other actions
just after the request is created.
parent 07ff6fda
......@@ -450,15 +450,28 @@ Environment
> user('root')
Operation not permitted
.. function:: resolve(qname, qtype[, qclass = kres.class.IN, options = 0, callback = nil])
.. function:: resolve(name, type[, class = kres.class.IN, options = 0, finish = nil, begin = nil])
:param string qname: Query name (e.g. 'com.')
:param number qtype: Query type (e.g. ``kres.type.NS``)
:param number qclass: Query class *(optional)* (e.g. ``kres.class.IN``)
:param string name: Query name (e.g. 'com.')
:param number type: Query type (e.g. ``kres.type.NS``)
:param number class: Query class *(optional)* (e.g. ``kres.class.IN``)
:param number options: Resolution options (see query flags)
:param function callback: Callback to be executed when resolution completes (e.g. `function cb (pkt, req) end`). The callback gets a packet containing the final answer and doesn't have to return anything.
:param function finish: Callback to be executed when resolution completes (e.g. `function cb (pkt, req) end`). The callback gets a packet containing the final answer and doesn't have to return anything.
:param function begin: Callback to be executed when the request is created.
:return: boolean
The function can also be executed with a table of arguments instead. This is useful if you'd like to skip some arguments, for example:
.. code-block:: lua
resolve {
name = 'example.com',
type = kres.type.AAAA,
finish = function ()
print('done')
end,
}
Example:
.. code-block:: lua
......
......@@ -1244,22 +1244,40 @@ static int wrk_resolve(lua_State *L)
if (options->DNSSEC_WANT) {
knot_edns_set_do(pkt->opt_rr);
}
if (options->DNSSEC_CD) {
knot_wire_set_cd(pkt->wire);
}
/* Create task and start with a first question */
struct qr_task *task = worker_resolve_start(worker, pkt, *options);
if (!task) {
knot_rrset_free(&pkt->opt_rr, NULL);
knot_pkt_free(&pkt);
lua_pushstring(L, "couldn't create a resolution request");
lua_error(L);
}
/* Store completion callback in registry */
if (lua_isfunction(L, 5)) {
/* Store callback in registry */
lua_pushvalue(L, 5);
int cb = luaL_ref(L, LUA_REGISTRYINDEX);
ret = worker_resolve(worker, pkt, *options, resolve_callback, (void *) (intptr_t)cb);
} else {
ret = worker_resolve(worker, pkt, *options, NULL, NULL);
task->on_complete = resolve_callback;
task->baton = (void *) (intptr_t)cb;
}
/* Add initialisation callback */
if (lua_isfunction(L, 6)) {
lua_pushvalue(L, 6);
lua_pushlightuserdata(L, &task->req);
(void) execute_callback(L, 1);
}
/* Start execution */
int ret = worker_resolve_exec(task, pkt);
lua_pushboolean(L, ret == 0);
knot_rrset_free(&pkt->opt_rr, NULL);
knot_pkt_free(&pkt);
lua_pushboolean(L, ret == 0);
return 1;
}
......
......@@ -23,16 +23,27 @@ end
-- Resolver bindings
kres = require('kres')
trust_anchors = require('trust_anchors')
if rawget(kres, 'str2dname') ~= nil then
todname = kres.str2dname
end
-- Compat. wrapper for query flags.
worker.resolve = function (p1, p2, p3, options, p5)
-- Compatibility wrapper for query flags.
worker.resolve = function (qname, qtype, qclass, options, finish, begin)
-- Alternatively use named arguments
if type(qname) == 'table' then
local t = qname
qname = t.name
qtype = t.type or kres.type.A
qclass = t.class or kres.class.IN
options = t.options
finish = t.finish
begin = t.begin
end
-- Translate options and resolve
options = kres.mk_qflags(options)
return worker.resolve_unwrapped (p1, p2, p3, options, p5)
return worker.resolve_unwrapped(qname, qtype, qclass, options, finish, begin)
end
resolve = worker.resolve
-- Shorthand for aggregated per-worker information
......@@ -172,7 +183,7 @@ end
-- Make sandboxed environment
local function make_sandbox(defined)
local __protected = { modules = true, cache = true, net = true, trust_anchors = true }
local __protected = { worker = true, env = true, modules = true, cache = true, net = true, trust_anchors = true }
-- Compute and export the list of top-level names (hidden otherwise)
local nl = ""
......@@ -203,6 +214,7 @@ else -- Lua 5.2+
end
-- Load embedded modules
trust_anchors = require('trust_anchors')
modules.load('ta_signal_query')
modules.load('priming')
modules.load('detect_time_skew')
......
......@@ -57,7 +57,7 @@ struct req
#define qr_task_ref(task) \
do { ++(task)->refs; } while(0)
#define qr_task_unref(task) \
do { if (--(task)->refs == 0) { qr_task_free(task); } } while (0)
do { if (task && --(task)->refs == 0) { qr_task_free(task); } } while (0)
#define qr_valid_handle(task, checked) \
(!uv_is_closing((checked)) || (task)->source.handle == (checked))
......@@ -280,6 +280,7 @@ static struct qr_task *qr_task_create(struct worker_ctx *worker, uv_handle_t *ha
task->source.handle = handle;
task->timeout = NULL;
task->on_complete = NULL;
task->baton = NULL;
task->req.qsource.key = NULL;
task->req.qsource.addr = NULL;
task->req.qsource.dst_addr = NULL;
......@@ -1037,33 +1038,55 @@ int worker_process_tcp(struct worker_ctx *worker, uv_stream_t *handle, const uin
return submitted;
}
int worker_resolve(struct worker_ctx *worker, knot_pkt_t *query, struct kr_qflags options,
worker_cb_t on_complete, void *baton)
struct qr_task *worker_resolve_start(struct worker_ctx *worker, knot_pkt_t *query, struct kr_qflags options)
{
if (!worker || !query) {
return kr_error(EINVAL);
return NULL;
}
/* Create task */
struct qr_task *task = qr_task_create(worker, NULL, NULL);
if (!task) {
return kr_error(ENOMEM);
return NULL;
}
task->baton = baton;
task->on_complete = on_complete;
/* Start task */
int ret = qr_task_start(task, query);
if (ret != 0) {
qr_task_unref(task);
return NULL;
}
/* Set options late, as qr_task_start() -> kr_resolve_begin() rewrite it. */
kr_qflags_set(&task->req.options, options);
return task;
}
if (ret != 0) {
qr_task_unref(task);
return ret;
int worker_resolve_exec(struct qr_task *task, knot_pkt_t *query)
{
if (!task) {
return kr_error(EINVAL);
}
return qr_task_step(task, NULL, query);
}
int worker_resolve(struct worker_ctx *worker, knot_pkt_t *query, struct kr_qflags options,
worker_cb_t on_complete, void *baton)
{
if (!worker || !query) {
return kr_error(EINVAL);
}
/* Create task */
struct qr_task *task = worker_resolve_start(worker, query, options);
if (!task) {
return kr_error(ENOMEM);
}
/* Install completion handler */
task->baton = baton;
task->on_complete = on_complete;
return worker_resolve_exec(task, query);
}
/** Reserve worker buffers */
static int worker_reserve(struct worker_ctx *worker, size_t ring_maxlen)
{
......
......@@ -23,6 +23,7 @@
/** Worker state (opaque). */
struct worker_ctx;
struct qr_task;
/** Worker callback */
typedef void (*worker_cb_t)(struct worker_ctx *worker, struct kr_request *req, void *baton);
......@@ -52,6 +53,21 @@ int worker_process_tcp(struct worker_ctx *worker, uv_stream_t *handle,
*/
int worker_end_tcp(struct worker_ctx *worker, uv_handle_t *handle);
/**
* Start query resolution with given query.
*
* @return task or NULL
*/
struct qr_task *worker_resolve_start(struct worker_ctx *worker, knot_pkt_t *query, struct kr_qflags options);
/**
* Execute a request with given query.
* It expects task to be created with \fn worker_resolve_start.
*
* @return 0 or an error code
*/
int worker_resolve_exec(struct qr_task *task, knot_pkt_t *query);
/**
* Schedule query for resolution.
*
......
......@@ -34,7 +34,7 @@ function predict.drain()
local deleted = 0
for key, _ in pairs(predict.queue) do
local qtype, qname = key:match('(%S*)%s(.*)')
worker.resolve(qname, kres.type[qtype], kres.class.IN, 'NO_CACHE')
resolve(qname, kres.type[qtype], kres.class.IN, {'NO_CACHE'})
predict.queue[key] = nil
deleted = deleted + 1
-- Resolve smaller batches at a time
......@@ -177,7 +177,7 @@ predict.layer = {
for i = 0, (tonumber(qrys.len) - 1) do -- size_t doesn't work for some reason
local qry = qrys.at[i]
if qry.flags.EXPIRING == true then
worker.resolve(kres.dname2str(qry.sname), qry.stype, qry.sclass, 'NO_CACHE')
resolve(kres.dname2str(qry.sname), qry.stype, qry.sclass, {'NO_CACHE'})
end
end
end
......
......@@ -5,7 +5,7 @@ modules = { 'predict' }
local resolve_count = 0
local current_epoch = 0
worker.resolve = function ()
resolve = function ()
resolve_count = resolve_count + 1
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment