> net.listen("::", 853)
> net.listen("::", 443, {tls = true})
+.. function:: net.tls_padding([padding])
+
+ Get/set EDNS(0) padding. If set to value >= 2 it will pad the answers
+ to nearest *padding* boundary, e.g. if set to `64`, the answer will
+ have size of multiplies of 64 (64, 128, 192, ...). Setting padding to
+ value < 2 will disable it.
+
Trust anchors and DNSSEC
^^^^^^^^^^^^^^^^^^^^^^^^
return 1;
}
+static int net_tls_padding(lua_State *L)
+{
+ struct engine *engine = engine_luaget(L);
+
+ /* Only return current padding. */
+ if (lua_gettop(L) == 0) {
+ if (engine->resolver.tls_padding == 0) {
+ return -1;
+ }
+ lua_pushinteger(L, engine->resolver.tls_padding);
+ return 1;
+ }
+
+ if ((lua_gettop(L) != 1) || !lua_isnumber(L, 1)) {
+ lua_pushstring(L, "net.tls_padding takes one numeric parameter: (\"padding\")");
+ lua_error(L);
+ }
+ int padding = lua_tointeger(L, 1);
+ if ((padding < 0) || (padding > MAX_TLS_PADDING)) {
+ lua_pushstring(L, "net.tls_padding parameter has to be a number between <0, 4096>");
+ lua_error(L);
+ }
+ engine->resolver.tls_padding = padding;
+ lua_pushboolean(L, true);
+ return 1;
+}
+
int lib_net(lua_State *L)
{
static const luaL_Reg lib[] = {
{ "bufsize", net_bufsize },
{ "tcp_pipeline", net_pipeline },
{ "tls", net_tls },
+ { "tls_padding", net_tls_padding },
{ NULL, NULL }
};
register_lib(L, "net", lib);
return kr_error(ENOMEM);
}
knot_edns_init(engine->resolver.opt_rr, KR_EDNS_PAYLOAD, 0, KR_EDNS_VERSION, engine->pool);
+ /* Set default TLS padding */
+ engine->resolver.tls_padding = KR_DEFAULT_TLS_PADDING;
/* Set default root hints */
kr_zonecut_init(&engine->resolver.root_hints, (const uint8_t *)"", engine->pool);
kr_zonecut_set_sbelt(&engine->resolver, &engine->resolver.root_hints);
static const int BADCOOKIE_AGAIN = 1 << 22;
static const int CNAME = 1 << 23;
static const int REORDER_RR = 1 << 24;
+ static const int TLS = 1 << 25;
};
/*
#include <uv.h>
#include <gnutls/gnutls.h>
#include <libknot/packet/pkt.h>
+#include "lib/defines.h"
+
+#define MAX_TLS_PADDING KR_EDNS_PAYLOAD
struct tls_ctx_t;
struct tls_credentials;
task->addrlist = NULL;
task->addrlist_count = 0;
task->addrlist_turn = 0;
+ task->req.has_tls = (task->session && task->session->has_tls) ? true : false;
int state = kr_resolve_consume(&task->req, packet_source, packet);
while (state == KNOT_STATE_PRODUCE) {
state = kr_resolve_produce(&task->req, &task->addrlist, &sock_type, task->pktbuf);
* borrowed the task from parent session. */
struct session *session = handle->data;
if (session->outgoing) {
- worker_submit(worker, (uv_handle_t *)handle, NULL, NULL);
+ worker_submit(worker, (uv_handle_t *)handle, NULL, NULL);
} else {
discard_buffered(session);
}
#define KR_DNS_TLS_PORT 853
#define KR_EDNS_VERSION 0
#define KR_EDNS_PAYLOAD 4096 /* Default UDP payload (max unfragmented UDP is 1452B) */
+#define KR_DEFAULT_TLS_PADDING 128 /* Default EDNS(0) Padding is 128 */
/*
* Address sanitizer hints.
wire_size += KR_COOKIE_OPT_MAX_LEN;
}
#endif /* defined(ENABLE_COOKIES) */
+ if (req->has_tls && req->ctx->tls_padding >= 2) {
+ wire_size += KNOT_EDNS_OPTION_HDRLEN + req->ctx->tls_padding;
+ }
return knot_pkt_reserve(pkt, wire_size);
}
}
}
-static int answer_fail(knot_pkt_t *answer)
+static int answer_padding(struct kr_request *request)
{
+ uint16_t padding = request->ctx->tls_padding;
+ knot_pkt_t *answer = request->answer;
+ knot_rrset_t *opt_rr = answer->opt_rr;
+
+ if (padding < 2) {
+ return true;
+ }
+ int32_t max_pad_bytes = knot_edns_get_payload(opt_rr) - (answer->size + knot_rrset_size(opt_rr));
+
+ int32_t pad_bytes = MIN(knot_edns_alignment_size(answer->size, knot_rrset_size(opt_rr), padding),
+ max_pad_bytes);
+
+ if (pad_bytes > 0) {
+ uint8_t zeros[pad_bytes];
+ memset(zeros, 0, sizeof(zeros));
+ int r = knot_edns_add_option(opt_rr, KNOT_EDNS_OPTION_PADDING,
+ pad_bytes, zeros, &answer->mm);
+ if (r != KNOT_EOK) {
+ knot_rrset_clear(opt_rr, &answer->mm);
+ return false;
+ }
+ }
+ return true;
+}
+
+static int answer_fail(struct kr_request *request)
+{
+ knot_pkt_t *answer = request->answer;
int ret = kr_pkt_clear_payload(answer);
knot_wire_clear_ad(answer->wire);
knot_wire_clear_aa(answer->wire);
if (ret == 0 && answer->opt_rr) {
/* OPT in SERVFAIL response is still useful for cookies/additional info. */
knot_pkt_begin(answer, KNOT_ADDITIONAL);
+ answer_padding(request); /* Ignore failed padding in SERVFAIL answer a*/
ret = edns_put(answer);
}
return ret;
if (state == KNOT_STATE_FAIL && rplan->pending.len > 0) {
struct kr_query *last = array_tail(rplan->pending);
if ((last->flags & QUERY_DNSSEC_WANT) && (last->flags & QUERY_DNSSEC_BOGUS)) {
- return answer_fail(answer);
+ return answer_fail(request);
}
}
/* Write EDNS information */
int ret = 0;
if (answer->opt_rr) {
+ if (request->has_tls) {
+ if (!answer_padding(request)) {
+ return answer_fail(request);
+ }
+ }
knot_pkt_begin(answer, KNOT_ADDITIONAL);
ret = edns_put(answer);
}
* module because of better access. */
struct kr_cookie_ctx cookie_ctx;
kr_cookie_lru_t *cache_cookie;
+ uint32_t tls_padding;
knot_mm_t *pool;
};
rr_array_t authority;
rr_array_t additional;
struct kr_rplan rplan;
+ int has_tls;
knot_mm_t pool;
};
X(STRICT, 1 << 21) /**< Strict resolver mode. */ \
X(BADCOOKIE_AGAIN, 1 << 22) /**< Query again because bad cookie returned. */ \
X(CNAME, 1 << 23) /**< Query response contains CNAME in answer section. */ \
- X(REORDER_RR, 1 << 24) /**< Reorder cached RRs. */
+ X(REORDER_RR, 1 << 24) /**< Reorder cached RRs. */ \
+ X(TLS, 1 << 25) /**< Use TLS for this query. */
/** Query flags */
enum kr_query_flag {