From: Ondřej Surý Date: Sun, 13 Nov 2016 01:54:28 +0000 (+0100) Subject: Add EDNS(0) padding support X-Git-Tag: v1.2.0-rc1~71^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=632ace758370254e74fdb17c3ddd69de6c1f00a8;p=thirdparty%2Fknot-resolver.git Add EDNS(0) padding support --- diff --git a/daemon/README.rst b/daemon/README.rst index b3958eecc..7405721ad 100644 --- a/daemon/README.rst +++ b/daemon/README.rst @@ -594,6 +594,13 @@ For when listening on ``localhost`` just doesn't cut it. > 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 ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/daemon/bindings.c b/daemon/bindings.c index 408603a05..1233ce012 100644 --- a/daemon/bindings.c +++ b/daemon/bindings.c @@ -394,6 +394,33 @@ static int net_tls(lua_State *L) 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[] = { @@ -404,6 +431,7 @@ int lib_net(lua_State *L) { "bufsize", net_bufsize }, { "tcp_pipeline", net_pipeline }, { "tls", net_tls }, + { "tls_padding", net_tls_padding }, { NULL, NULL } }; register_lib(L, "net", lib); diff --git a/daemon/engine.c b/daemon/engine.c index e4d05d011..1751855d6 100644 --- a/daemon/engine.c +++ b/daemon/engine.c @@ -483,6 +483,8 @@ static int init_resolver(struct engine *engine) 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); diff --git a/daemon/lua/kres.lua b/daemon/lua/kres.lua index d69314f5f..7256c3f2f 100644 --- a/daemon/lua/kres.lua +++ b/daemon/lua/kres.lua @@ -112,6 +112,7 @@ struct query_flag { 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; }; /* diff --git a/daemon/tls.h b/daemon/tls.h index 12375c7cd..21671d4bf 100644 --- a/daemon/tls.h +++ b/daemon/tls.h @@ -19,6 +19,9 @@ #include #include #include +#include "lib/defines.h" + +#define MAX_TLS_PADDING KR_EDNS_PAYLOAD struct tls_ctx_t; struct tls_credentials; diff --git a/daemon/worker.c b/daemon/worker.c index b6f1afb68..bf468d175 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -721,6 +721,7 @@ static int qr_task_step(struct qr_task *task, const struct sockaddr *packet_sour 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); @@ -881,7 +882,7 @@ int worker_end_tcp(struct worker_ctx *worker, uv_handle_t *handle) * 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); } diff --git a/lib/defines.h b/lib/defines.h index 2e64e5648..0be6b2d20 100644 --- a/lib/defines.h +++ b/lib/defines.h @@ -64,6 +64,7 @@ static inline int __attribute__((__cold__)) kr_error(int x) { #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. diff --git a/lib/resolve.c b/lib/resolve.c index 7f8c7f896..706c5b6a8 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -308,6 +308,9 @@ static int edns_create(knot_pkt_t *pkt, knot_pkt_t *template, struct kr_request 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); } @@ -337,8 +340,36 @@ static void write_extra_records(rr_array_t *arr, knot_pkt_t *answer) } } -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); @@ -346,6 +377,7 @@ static int answer_fail(knot_pkt_t *answer) 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; @@ -360,7 +392,7 @@ static int answer_finalize(struct kr_request *request, int state) 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); } } @@ -375,6 +407,11 @@ static int answer_finalize(struct kr_request *request, int state) /* 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); } diff --git a/lib/resolve.h b/lib/resolve.h index d8434bb70..099701cda 100644 --- a/lib/resolve.h +++ b/lib/resolve.h @@ -99,6 +99,7 @@ struct kr_context * 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; }; @@ -132,6 +133,7 @@ struct kr_request { rr_array_t authority; rr_array_t additional; struct kr_rplan rplan; + int has_tls; knot_mm_t pool; }; diff --git a/lib/rplan.h b/lib/rplan.h index 66bdef099..402a811f8 100644 --- a/lib/rplan.h +++ b/lib/rplan.h @@ -49,7 +49,8 @@ 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 {