#include <lua.h>
#include <lauxlib.h>
+#include "daemon/bindings/impl.h"
#include "daemon/engine.h"
#include "daemon/ffimodule.h"
+#include "daemon/worker.h"
#include "lib/module.h"
#include "lib/layer.h"
};
#define SLOT_size sizeof(int)
+/** Lua registry indices for functions that wrap layer callbacks (shared by all lua modules). */
+static int l_ffi_wrap_slots[SLOT_count] = { 0 };
+
/** @internal Helper for retrieving the right function entrypoint. */
static inline lua_State *l_ffi_preface(struct kr_module *module, const char *call) {
lua_State *L = module->lib;
/** @internal Helper for retrieving layer Lua function by name. */
#define LAYER_FFI_CALL(ctx, slot_name) \
- const int *cb_slot = (ctx)->api->cb_slots + SLOT_ ## slot_name; \
- if (*cb_slot <= 0) { \
- return (ctx)->state; \
- } \
- struct kr_module *module = (ctx)->api->data; \
- lua_State *L = module->lib; \
- lua_rawgeti(L, LUA_REGISTRYINDEX, *cb_slot); \
- lua_pushnumber(L, (ctx)->state)
+ (ctx)->pkt = NULL; /* for debugging */ \
+ const int wrap_slot = l_ffi_wrap_slots[SLOT_ ## slot_name]; \
+ const int cb_slot = (ctx)->api->cb_slots[SLOT_ ## slot_name]; \
+ assert(wrap_slot > 0 && cb_slot > 0); \
+ lua_State *L = the_worker->engine->L; \
+ lua_rawgeti(L, LUA_REGISTRYINDEX, wrap_slot); \
+ lua_rawgeti(L, LUA_REGISTRYINDEX, cb_slot); \
+ lua_pushpointer(L, (ctx));
static int l_ffi_layer_begin(kr_layer_t *ctx)
{
LAYER_FFI_CALL(ctx, begin);
- lua_pushlightuserdata(L, ctx->req);
return l_ffi_call_layer(L, 2);
}
static int l_ffi_layer_reset(kr_layer_t *ctx)
{
LAYER_FFI_CALL(ctx, reset);
- lua_pushlightuserdata(L, ctx->req);
return l_ffi_call_layer(L, 2);
}
static int l_ffi_layer_finish(kr_layer_t *ctx)
{
- struct kr_request *req = ctx->req;
LAYER_FFI_CALL(ctx, finish);
- lua_pushlightuserdata(L, req);
- lua_pushlightuserdata(L, req->answer);
- return l_ffi_call_layer(L, 3);
+ ctx->pkt = ctx->req->answer;
+ return l_ffi_call_layer(L, 2);
}
static int l_ffi_layer_consume(kr_layer_t *ctx, knot_pkt_t *pkt)
return ctx->state; /* Already failed, skip */
}
LAYER_FFI_CALL(ctx, consume);
- lua_pushlightuserdata(L, ctx->req);
- lua_pushlightuserdata(L, pkt);
- return l_ffi_call_layer(L, 3);
+ ctx->pkt = pkt;
+ return l_ffi_call_layer(L, 2);
}
static int l_ffi_layer_produce(kr_layer_t *ctx, knot_pkt_t *pkt)
{
- if (ctx->state & (KR_STATE_FAIL)) {
- return ctx->state; /* Already failed or done, skip */
+ if (ctx->state & KR_STATE_FAIL) {
+ return ctx->state; /* Already failed, skip */
}
LAYER_FFI_CALL(ctx, produce);
- lua_pushlightuserdata(L, ctx->req);
- lua_pushlightuserdata(L, pkt);
- return l_ffi_call_layer(L, 3);
+ ctx->pkt = pkt;
+ return l_ffi_call_layer(L, 2);
}
static int l_ffi_layer_checkout(kr_layer_t *ctx, knot_pkt_t *pkt, struct sockaddr *dst, int type)
{
- if (ctx->state & (KR_STATE_FAIL)) {
- return ctx->state; /* Already failed or done, skip */
+ if (ctx->state & KR_STATE_FAIL) {
+ return ctx->state; /* Already failed, skip */
}
LAYER_FFI_CALL(ctx, checkout);
- lua_pushlightuserdata(L, ctx->req);
- lua_pushlightuserdata(L, pkt);
- lua_pushlightuserdata(L, dst);
- lua_pushboolean(L, type == SOCK_STREAM);
- return l_ffi_call_layer(L, 5);
+ ctx->pkt = pkt;
+ ctx->dst = dst;
+ ctx->is_stream = (type == SOCK_STREAM);
+ return l_ffi_call_layer(L, 2);
}
static int l_ffi_layer_answer_finalize(kr_layer_t *ctx)
{
LAYER_FFI_CALL(ctx, answer_finalize);
- lua_pushlightuserdata(L, ctx->req);
return l_ffi_call_layer(L, 2);
}
#undef LAYER_FFI_CALL
+int ffimodule_init(lua_State *L)
+{
+ /* Wrappers defined in ./lua/sandbox.lua */
+ /* for API: (int state, kr_request_t *req) */
+ lua_getglobal(L, "modules_ffi_layer_wrap1");
+ const int wrap1 = luaL_ref(L, LUA_REGISTRYINDEX);
+ /* for API: (int state, kr_request_t *req, knot_pkt_t *) */
+ lua_getglobal(L, "modules_ffi_layer_wrap2");
+ const int wrap2 = luaL_ref(L, LUA_REGISTRYINDEX);
+ lua_getglobal(L, "modules_ffi_layer_wrap_checkout");
+ const int wrap_checkout = luaL_ref(L, LUA_REGISTRYINDEX);
+ if (wrap1 == LUA_REFNIL || wrap2 == LUA_REFNIL || wrap_checkout == LUA_REFNIL) {
+ return kr_error(ENOENT);
+ }
+
+ const int slots[SLOT_count] = {
+ [SLOT_begin] = wrap1,
+ [SLOT_reset] = wrap1,
+ [SLOT_finish] = wrap2,
+ [SLOT_consume] = wrap2,
+ [SLOT_produce] = wrap2,
+ [SLOT_checkout] = wrap_checkout,
+ [SLOT_answer_finalize] = wrap1,
+ };
+ memcpy(l_ffi_wrap_slots, slots, sizeof(l_ffi_wrap_slots));
+ return kr_ok();
+}
+void ffimodule_deinit(lua_State *L)
+{
+ /* Unref each wrapper function from lua.
+ * It's probably useless, as we're about to destroy lua_State, but... */
+ const int wrapsIndices[] = {
+ SLOT_begin,
+ SLOT_consume,
+ SLOT_checkout,
+ };
+ for (int i = 0; i < sizeof(wrapsIndices) / sizeof(wrapsIndices[0]); ++i) {
+ luaL_unref(L, LUA_REGISTRYINDEX, l_ffi_wrap_slots[wrapsIndices[i]]);
+ }
+}
+
/** @internal Conditionally register layer trampoline
* @warning Expects 'module.layer' to be on top of Lua stack. */
#define LAYER_REGISTER(L, api, name) do { \
LAYER_REGISTER(L, api, checkout);
LAYER_REGISTER(L, api, answer_finalize);
LAYER_REGISTER(L, api, reset);
- /* Begin is always set, as it initializes layer baton. */
- api->begin = l_ffi_layer_begin;
api->data = module;
}
return api;
int state; /*!< The current state; bitmap of enum kr_layer_state. */
struct kr_request *req; /*!< The corresponding request. */
const struct kr_layer_api *api;
+ knot_pkt_t *pkt; /*!< In glue for lua kr_layer_api it's used to pass the parameter. */
+ struct sockaddr *dst; /*!< In glue for checkout layer it's used to pass the parameter. */
+ bool is_stream; /*!< In glue for checkout layer it's used to pass the parameter. */
} kr_layer_t;
/** Packet processing module API. All functions return the new kr_layer_state. */
/** Paired to begin, called both on successes and failures. */
int (*finish)(kr_layer_t *ctx);
- /** Processing an answer from upstream or the answer to the request. */
+ /** Processing an answer from upstream or the answer to the request.
+ * Lua API: call is omitted iff (state & KR_STATE_FAIL). */
int (*consume)(kr_layer_t *ctx, knot_pkt_t *pkt);
- /** Produce either an answer to the request or a query for upstream (or fail). */
+ /** Produce either an answer to the request or a query for upstream (or fail).
+ * Lua API: call is omitted iff (state & KR_STATE_FAIL). */
int (*produce)(kr_layer_t *ctx, knot_pkt_t *pkt);
/** Finalises the outbound query packet with the knowledge of the IP addresses.
* The checkout layer doesn't persist the state, so canceled subrequests
- * don't affect the resolution or rest of the processing. */
+ * don't affect the resolution or rest of the processing.
+ * Lua API: call is omitted iff (state & KR_STATE_FAIL). */
int (*checkout)(kr_layer_t *ctx, knot_pkt_t *packet, struct sockaddr *dst, int type);
/** Finalises the answer.
* Last chance to affect what will get into the answer, including EDNS.*/
int (*answer_finalize)(kr_layer_t *ctx);
- /** The module can store anything in here. */
+ /** The module can store anything in here.
+ * In lua case we store kr_module pointer. */
void *data;
/** Internal to ./daemon/ffimodule.c. */