]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
Add EDNS(0) padding support
authorOndřej Surý <ondrej@sury.org>
Sun, 13 Nov 2016 01:54:28 +0000 (02:54 +0100)
committerOndřej Surý <ondrej@sury.org>
Mon, 14 Nov 2016 02:05:06 +0000 (03:05 +0100)
daemon/README.rst
daemon/bindings.c
daemon/engine.c
daemon/lua/kres.lua
daemon/tls.h
daemon/worker.c
lib/defines.h
lib/resolve.c
lib/resolve.h
lib/rplan.h

index b3958eecc28831a9f285eb942bc9c06fc3efc4f8..7405721ad8efef22f5e6c58a95f560bf72d1eea6 100644 (file)
@@ -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
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
index 408603a0532bdfa8712e19e486c50e6a6511967b..1233ce012c579e7647da1f6627761525504bcec2 100644 (file)
@@ -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);
index e4d05d0115f928d108957296084065d8538d1d5f..1751855d6bc6b5615863402d6632010cdae33ae7 100644 (file)
@@ -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);
index d69314f5f2361981eb892a02dcf6cf621e52ddb9..7256c3f2f9816b935ed5050a33f4f9df2f60682d 100644 (file)
@@ -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;
 };
 
 /*
index 12375c7cdaf1fe662cfb9c54aaa97a301a479190..21671d4bf218c21c4b9fba3031dfefa54a0cbdab 100644 (file)
@@ -19,6 +19,9 @@
 #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;
index b6f1afb6843971ea16122f57089b1b22acb77e50..bf468d1754e0ee86e6efd66db3bb622f9ec8fbad 100644 (file)
@@ -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);
        }
index 2e64e564847fcfa8dc7bfd30b7a02938ac253365..0be6b2d202629eb7ad6c8cd5860fc00ea2af0dc4 100644 (file)
@@ -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.
index 7f8c7f896427bbda078a2d5d2ccc3bb846c5b440..706c5b6a84a6d263efbcee7e406e171d2433c2ef 100644 (file)
@@ -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);
        }
index d8434bb70ac12af113d832f7120ffb2f31763d4a..099701cdabc487f74a3f0946b8bd31f6c0065ead 100644 (file)
@@ -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;
 };
 
index 66bdef0995a1741aace3502913f302be04568bb3..402a811f81b1a11576a6a035c68db570518bf75c 100644 (file)
@@ -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 {