]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
doh: C implementation of DoH - WiP
authorJan Hak <jan.hak@nic.cz>
Fri, 13 Mar 2020 14:02:50 +0000 (15:02 +0100)
committerTomas Krizek <tomas.krizek@nic.cz>
Tue, 13 Oct 2020 10:55:22 +0000 (12:55 +0200)
Working server-side GET/POST HTTPS method - Proof-of-Concept
Working server-side GET/POST HTTP/2 method - WiP

16 files changed:
contrib/base64url.c [new file with mode: 0644]
contrib/base64url.h [new file with mode: 0644]
contrib/meson.build
daemon/bindings/net.c
daemon/http.c [new file with mode: 0644]
daemon/http.h [new file with mode: 0644]
daemon/io.c
daemon/io.h
daemon/meson.build
daemon/network.c
daemon/network.h
daemon/session.c
daemon/session.h
daemon/worker.c
lib/defines.h
meson.build

diff --git a/contrib/base64url.c b/contrib/base64url.c
new file mode 100644 (file)
index 0000000..05697f4
--- /dev/null
@@ -0,0 +1,268 @@
+/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "contrib/base64url.h"
+#include "libknot/errcode.h"
+
+#include <stdlib.h>
+#include <stdint.h>
+
+/*! \brief Maximal length of binary input to Base64url encoding. */
+#define MAX_BIN_DATA_LEN       ((INT32_MAX / 4) * 3)
+
+/*! \brief Base64url padding character. */
+static const uint8_t base64url_pad = '\0';
+/*! \brief Base64 alphabet. */
+static const uint8_t base64url_enc[] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+
+/*! \brief Indicates bad Base64 character. */
+#define KO     255
+/*! \brief Indicates Base64 padding character. */
+#define PD      64
+
+/*! \brief Transformation and validation table for decoding Base64. */
+static const uint8_t base64url_dec[256] = {
+       [  0] = PD, [ 43] = KO, ['V'] = 21, [129] = KO, [172] = KO, [215] = KO,
+       [  1] = KO, [ 44] = KO, ['W'] = 22, [130] = KO, [173] = KO, [216] = KO,
+       [  2] = KO, ['-'] = 62, ['X'] = 23, [131] = KO, [174] = KO, [217] = KO,
+       [  3] = KO, [ 46] = KO, ['Y'] = 24, [132] = KO, [175] = KO, [218] = KO,
+       [  4] = KO, [ 47] = KO, ['Z'] = 25, [133] = KO, [176] = KO, [219] = KO,
+       [  5] = KO, ['0'] = 52, [ 91] = KO, [134] = KO, [177] = KO, [220] = KO,
+       [  6] = KO, ['1'] = 53, [ 92] = KO, [135] = KO, [178] = KO, [221] = KO,
+       [  7] = KO, ['2'] = 54, [ 93] = KO, [136] = KO, [179] = KO, [222] = KO,
+       [  8] = KO, ['3'] = 55, [ 94] = KO, [137] = KO, [180] = KO, [223] = KO,
+       [  9] = KO, ['4'] = 56, ['_'] = 63, [138] = KO, [181] = KO, [224] = KO,
+       [ 10] = KO, ['5'] = 57, [ 96] = KO, [139] = KO, [182] = KO, [225] = KO,
+       [ 11] = KO, ['6'] = 58, ['a'] = 26, [140] = KO, [183] = KO, [226] = KO,
+       [ 12] = KO, ['7'] = 59, ['b'] = 27, [141] = KO, [184] = KO, [227] = KO,
+       [ 13] = KO, ['8'] = 60, ['c'] = 28, [142] = KO, [185] = KO, [228] = KO,
+       [ 14] = KO, ['9'] = 61, ['d'] = 29, [143] = KO, [186] = KO, [229] = KO,
+       [ 15] = KO, [ 58] = KO, ['e'] = 30, [144] = KO, [187] = KO, [230] = KO,
+       [ 16] = KO, [ 59] = KO, ['f'] = 31, [145] = KO, [188] = KO, [231] = KO,
+       [ 17] = KO, [ 60] = KO, ['g'] = 32, [146] = KO, [189] = KO, [232] = KO,
+       [ 18] = KO, [ 61] = KO, ['h'] = 33, [147] = KO, [190] = KO, [233] = KO,
+       [ 19] = KO, [ 62] = KO, ['i'] = 34, [148] = KO, [191] = KO, [234] = KO,
+       [ 20] = KO, [ 63] = KO, ['j'] = 35, [149] = KO, [192] = KO, [235] = KO,
+       [ 21] = KO, [ 64] = KO, ['k'] = 36, [150] = KO, [193] = KO, [236] = KO,
+       [ 22] = KO, ['A'] =  0, ['l'] = 37, [151] = KO, [194] = KO, [237] = KO,
+       [ 23] = KO, ['B'] =  1, ['m'] = 38, [152] = KO, [195] = KO, [238] = KO,
+       [ 24] = KO, ['C'] =  2, ['n'] = 39, [153] = KO, [196] = KO, [239] = KO,
+       [ 25] = KO, ['D'] =  3, ['o'] = 40, [154] = KO, [197] = KO, [240] = KO,
+       [ 26] = KO, ['E'] =  4, ['p'] = 41, [155] = KO, [198] = KO, [241] = KO,
+       [ 27] = KO, ['F'] =  5, ['q'] = 42, [156] = KO, [199] = KO, [242] = KO,
+       [ 28] = KO, ['G'] =  6, ['r'] = 43, [157] = KO, [200] = KO, [243] = KO,
+       [ 29] = KO, ['H'] =  7, ['s'] = 44, [158] = KO, [201] = KO, [244] = KO,
+       [ 30] = KO, ['I'] =  8, ['t'] = 45, [159] = KO, [202] = KO, [245] = KO,
+       [ 31] = KO, ['J'] =  9, ['u'] = 46, [160] = KO, [203] = KO, [246] = KO,
+       [ 32] = KO, ['K'] = 10, ['v'] = 47, [161] = KO, [204] = KO, [247] = KO,
+       [ 33] = KO, ['L'] = 11, ['w'] = 48, [162] = KO, [205] = KO, [248] = KO,
+       [ 34] = KO, ['M'] = 12, ['x'] = 49, [163] = KO, [206] = KO, [249] = KO,
+       [ 35] = KO, ['N'] = 13, ['y'] = 50, [164] = KO, [207] = KO, [250] = KO,
+       [ 36] = KO, ['O'] = 14, ['z'] = 51, [165] = KO, [208] = KO, [251] = KO,
+       [ 37] = KO, ['P'] = 15, [123] = KO, [166] = KO, [209] = KO, [252] = KO,
+       [ 38] = KO, ['Q'] = 16, [124] = KO, [167] = KO, [210] = KO, [253] = KO,
+       [ 39] = KO, ['R'] = 17, [125] = KO, [168] = KO, [211] = KO, [254] = KO,
+       [ 40] = KO, ['S'] = 18, [126] = KO, [169] = KO, [212] = KO, [255] = KO,
+       [ 41] = KO, ['T'] = 19, [127] = KO, [170] = KO, [213] = KO,
+       [ 42] = KO, ['U'] = 20, [128] = KO, [171] = KO, [214] = KO,
+};
+
+int32_t kr_base64url_encode(const uint8_t  *in,
+                      const uint32_t in_len,
+                      uint8_t        *out,
+                      const uint32_t out_len)
+{
+       // Checking inputs.
+       if (in == NULL || out == NULL) {
+               return KNOT_EINVAL;
+       }
+       if (in_len > MAX_BIN_DATA_LEN || out_len < ((in_len + 2) / 3) * 4) {
+               return KNOT_ERANGE;
+       }
+
+       uint8_t         rest_len = in_len % 3;
+       const uint8_t   *stop = in + in_len - rest_len;
+       uint8_t         *text = out;
+
+       // Encoding loop takes 3 bytes and creates 4 characters.
+       while (in < stop) {
+               text[0] = base64url_enc[in[0] >> 2];
+               text[1] = base64url_enc[(in[0] & 0x03) << 4 | in[1] >> 4];
+               text[2] = base64url_enc[(in[1] & 0x0F) << 2 | in[2] >> 6];
+               text[3] = base64url_enc[in[2] & 0x3F];
+               text += 4;
+               in += 3;
+       }
+
+       // Processing of padding, if any.
+       switch (rest_len) {
+       case 2:
+               text[0] = base64url_enc[in[0] >> 2];
+               text[1] = base64url_enc[(in[0] & 0x03) << 4 | in[1] >> 4];
+               text[2] = base64url_enc[(in[1] & 0x0F) << 2];
+               text[3] = base64url_pad;
+               text += 3;
+               break;
+       case 1:
+               text[0] = base64url_enc[in[0] >> 2];
+               text[1] = base64url_enc[(in[0] & 0x03) << 4];
+               text[2] = base64url_pad;
+               text[3] = base64url_pad;
+               text += 2;
+               break;
+       }
+       return (text - out);
+}
+
+int32_t kr_base64url_encode_alloc(const uint8_t  *in,
+                            const uint32_t in_len,
+                            uint8_t        **out)
+{
+       // Checking inputs.
+       if (out == NULL) {
+               return KNOT_EINVAL;
+       }
+       if (in_len > MAX_BIN_DATA_LEN) {
+               return KNOT_ERANGE;
+       }
+
+       // Compute output buffer length.
+       uint32_t out_len = ((in_len + 2) / 3) * 4;
+
+       // Allocate output buffer.
+       *out = malloc(out_len);
+       if (*out == NULL) {
+               return KNOT_ENOMEM;
+       }
+
+       // Encode data.
+       int32_t ret = kr_base64url_encode(in, in_len, *out, out_len);
+       if (ret < 0) {
+               free(*out);
+               *out = NULL;
+       }
+
+       return ret;
+}
+
+int32_t kr_base64url_decode(const uint8_t  *in,
+                      const uint32_t in_len,
+                      uint8_t        *out,
+                      const uint32_t out_len)
+{
+       // Checking inputs.
+       if (in == NULL || out == NULL) {
+               return KNOT_EINVAL;
+       }
+       if (in_len > INT32_MAX || out_len < ((in_len + 3) / 4) * 3) {
+               return KNOT_ERANGE;
+       }
+
+       const uint8_t   *stop = in + in_len;
+       uint8_t         *bin = out;
+       uint8_t         pad_len = 0;
+       uint8_t         c1, c2, c3, c4;
+
+       // Decoding loop takes 4 characters and creates 3 bytes.
+       while (in < stop) {
+               // Filling and transforming 4 Base64 chars.
+               c1 =                   base64url_dec[in[0]]     ;
+               c2 = (in + 1 < stop) ? base64url_dec[in[1]] : PD;
+               c3 = (in + 2 < stop) ? base64url_dec[in[2]] : PD;
+               c4 = (in + 3 < stop) ? base64url_dec[in[3]] : PD;
+
+               // Check 4. char if is bad or padding.
+               if (c4 >= PD) {
+                       if (c4 == PD && pad_len == 0) {
+                               pad_len = 1;
+                       } else {
+                               return KNOT_BASE64_ECHAR;
+                       }
+               }
+
+               // Check 3. char if is bad or padding.
+               if (c3 >= PD) {
+                       if (c3 == PD && pad_len == 1) {
+                               pad_len = 2;
+                       } else {
+                               return KNOT_BASE64_ECHAR;
+                       }
+               }
+
+               // Check 1. and 2. chars if are not padding.
+               if (c2 >= PD || c1 >= PD) {
+                       return KNOT_BASE64_ECHAR;
+               }
+
+               // Computing of output data based on padding length.
+               switch (pad_len) {
+               case 0:
+                       bin[2] = (c3 << 6) + c4;
+                       // FALLTHROUGH
+               case 1:
+                       bin[1] = (c2 << 4) + (c3 >> 2);
+                       // FALLTHROUGH
+               case 2:
+                       bin[0] = (c1 << 2) + (c2 >> 4);
+               }
+
+               // Update output end.
+               switch (pad_len) {
+               case 0:
+                       bin += 3;
+                       break;
+               case 1:
+                       bin += 2;
+                       break;
+               case 2:
+                       bin += 1;
+                       break;
+               }
+
+               in += 4;
+       }
+
+       return (bin - out);
+}
+
+int32_t kr_base64url_decode_alloc(const uint8_t  *in,
+                            const uint32_t in_len,
+                            uint8_t        **out)
+{
+       // Checking inputs.
+       if (out == NULL) {
+               return KNOT_EINVAL;
+       }
+
+       // Compute output buffer length.
+       uint32_t out_len = ((in_len + 3) / 4) * 3;
+
+       // Allocate output buffer.
+       *out = malloc(out_len);
+       if (*out == NULL) {
+               return KNOT_ENOMEM;
+       }
+
+       // Decode data.
+       int32_t ret = kr_base64url_decode(in, in_len, *out, out_len);
+       if (ret < 0) {
+               free(*out);
+               *out = NULL;
+       }
+
+       return ret;
+}
diff --git a/contrib/base64url.h b/contrib/base64url.h
new file mode 100644 (file)
index 0000000..66a3afc
--- /dev/null
@@ -0,0 +1,103 @@
+/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+/*!
+ * \brief Base64url implementation (RFC 4648).
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*!
+ * \brief Encodes binary data using Base64.
+ *
+ * \note Output data buffer contains Base64 text string which isn't
+ *       terminated with '\0'!
+ *
+ * \param in           Input binary data.
+ * \param in_len       Length of input data.
+ * \param out          Output data buffer.
+ * \param out_len      Size of output buffer.
+ *
+ * \retval >=0         length of output string.
+ * \retval KNOT_E*     if error.
+ */
+int32_t kr_base64url_encode(const uint8_t  *in,
+                      const uint32_t in_len,
+                      uint8_t        *out,
+                      const uint32_t out_len);
+
+/*!
+ * \brief Encodes binary data using Base64 and output stores to own buffer.
+ *
+ * \note Output data buffer contains Base64 text string which isn't
+ *       terminated with '\0'!
+ *
+ * \note Output buffer should be deallocated after use.
+ *
+ * \param in           Input binary data.
+ * \param in_len       Length of input data.
+ * \param out          Output data buffer.
+ *
+ * \retval >=0         length of output string.
+ * \retval KNOT_E*     if error.
+ */
+int32_t kr_base64url_encode_alloc(const uint8_t  *in,
+                            const uint32_t in_len,
+                            uint8_t        **out);
+
+/*!
+ * \brief Decodes text data using Base64.
+ *
+ * \note Input data needn't be terminated with '\0'.
+ *
+ * \note Input data must be continuous Base64 string!
+ *
+ * \param in           Input text data.
+ * \param in_len       Length of input string.
+ * \param out          Output data buffer.
+ * \param out_len      Size of output buffer.
+ *
+ * \retval >=0         length of output data.
+ * \retval KNOT_E*     if error.
+ */
+int32_t kr_base64url_decode(const uint8_t  *in,
+                      const uint32_t in_len,
+                      uint8_t        *out,
+                      const uint32_t out_len);
+
+/*!
+ * \brief Decodes text data using Base64 and output stores to own buffer.
+ *
+ * \note Input data needn't be terminated with '\0'.
+ *
+ * \note Input data must be continuous Base64 string!
+ *
+ * \note Output buffer should be deallocated after use.
+ *
+ * \param in           Input text data.
+ * \param in_len       Length of input string.
+ * \param out          Output data buffer.
+ *
+ * \retval >=0         length of output data.
+ * \retval KNOT_E*     if error.
+ */
+int32_t kr_base64url_decode_alloc(const uint8_t  *in,
+                            const uint32_t in_len,
+                            uint8_t        **out);
+
+/*! @} */
index 9e6a6390fa67d1d5597e158a3f4e10a9f392d989..effbafda4cd1698921d8e7b57ad44aceeb6ed10c 100644 (file)
@@ -8,7 +8,8 @@ contrib_src = files([
   'ucw/mempool-fmt.c',
   'murmurhash3/murmurhash3.c',
   'base32hex.c',
-  'base64.c'
+  'base64.c',
+  'base64url.c'
 ])
 
 contrib_inc = include_directories('.', '..')
index 2131d16cbf9002afd9716302f7c673598b580ed9..1e6a992fb1ec0263b05b884e54751aaf99b8551c 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2015-2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2015-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -95,7 +95,7 @@ static int net_list(lua_State *L)
 /** Listen on an address list represented by the top of lua stack.
  * \note kind ownership is not transferred
  * \return success */
-static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind, bool freebind)
+static bool net_listen_addrs(lua_State *L, int port, bool tls, bool http, const char *kind, bool freebind)
 {
        /* Case: table with 'addr' field; only follow that field directly. */
        lua_getfield(L, -1, "addr");
@@ -110,7 +110,7 @@ static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind,
        if (str != NULL) {
                struct network *net = &the_worker->engine->net;
                int ret = 0;
-               endpoint_flags_t flags = { .tls = tls, .freebind = freebind };
+               endpoint_flags_t flags = { .tls = tls, .http = http, .freebind = freebind };
                if (!kind && !flags.tls) { /* normal UDP */
                        flags.sock_type = SOCK_DGRAM;
                        ret = network_listen(net, str, port, flags);
@@ -142,7 +142,7 @@ static bool net_listen_addrs(lua_State *L, int port, bool tls, const char *kind,
                lua_error_p(L, "bad type for address");
        lua_pushnil(L);
        while (lua_next(L, -2)) {
-               if (!net_listen_addrs(L, port, tls, kind, freebind))
+               if (!net_listen_addrs(L, port, tls, http, kind, freebind))
                        return false;
                lua_pop(L, 1);
        }
@@ -182,6 +182,8 @@ static int net_listen(lua_State *L)
        }
 
        bool tls = (port == KR_DNS_TLS_PORT);
+       bool http = (port == KR_DNS_HTTP_PORT);
+       http = tls = (port == KR_DNS_DOH_PORT);
        bool freebind = false;
        const char *kind = NULL;
        if (n > 2 && !lua_isnil(L, 3)) {
@@ -193,10 +195,19 @@ static int net_listen(lua_State *L)
                lua_getfield(L, 3, "kind");
                const char *k = lua_tostring(L, -1);
                if (k && strcasecmp(k, "dns") == 0) {
-                       tls = false;
+                       tls = http = false;
                } else
                if (k && strcasecmp(k, "tls") == 0) {
                        tls = true;
+                       http = false;
+               } else
+               //TODO temporary moved HTTP (without TLS) here
+               if (k && strcasecmp(k, "http") == 0) {
+                       tls = false;
+                       http = true;
+               } else
+               if (k && strcasecmp(k, "doh") == 0) {
+                       tls = http = true;
                } else
                if (k) {
                        kind = k;
@@ -214,7 +225,7 @@ static int net_listen(lua_State *L)
 
        /* Now focus on the first argument. */
        lua_settop(L, 1);
-       if (!net_listen_addrs(L, port, tls, kind, freebind))
+       if (!net_listen_addrs(L, port, tls, http, kind, freebind))
                lua_error_p(L, "net.listen() failed to bind");
        lua_pushboolean(L, true);
        return 1;
diff --git a/daemon/http.c b/daemon/http.c
new file mode 100644 (file)
index 0000000..d145555
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2020 CZ.NIC, z.s.p.o
+ *
+ * Initial Author: Jan Hák <jan.hak@nic.cz>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <nghttp2/nghttp2.h>
+#include <uv.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "daemon/io.h"
+#include "daemon/http.h"
+#include "daemon/worker.h"
+#include "daemon/session.h"
+
+#include "contrib/base64url.h"
+
+#define MAKE_NV(K, KS, V, VS) \
+       { (uint8_t *)K, (uint8_t *)V, KS, VS, NGHTTP2_NV_FLAG_NONE }
+
+#define MAKE_STATIC_NV(K, V) \
+       MAKE_NV(K, sizeof(K) - 1, V, sizeof(V) - 1)
+
+#define HTTP_MAX_CONCURRENT_STREAMS 1
+
+#define MAX_DECIMAL_LENGTH(VT) (CHAR_BIT * sizeof(VT) / 3) + 3
+
+struct http_data_buffer {
+       uint8_t *data;
+       const uint8_t *end;
+};
+
+static char const server_logstring[] = "http";
+
+static ssize_t send_callback(nghttp2_session *session, const uint8_t *data, size_t length, int flags, void *user_data)
+{
+       struct http_ctx_t *ctx = (struct http_ctx_t *)user_data;
+       return ctx->send_cb(data, length, ctx->user_ctx);
+}
+
+static int header_callback(nghttp2_session *session, const nghttp2_frame *frame, const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen, uint8_t flags, void *user_data)
+{
+       //TODO some validation.. When POST, no DNS variable in path...
+       //In knot we parse path using some static lib, think of use it too but not necessary
+       static const char key[] = "dns=";
+       struct http_ctx_t *ctx = (struct http_ctx_t *)user_data;
+       if (!strcasecmp(":path", (const char *)name)) {
+               char *beg = strstr((const char *)value, key);
+               if (beg) {
+                       beg += sizeof(key) - 1;
+                       char *end = strchrnul(beg, '&');
+                       ctx->wire_len = kr_base64url_decode((uint8_t*)beg, end - beg, ctx->wire + sizeof(uint16_t), ctx->wire_len - sizeof(uint16_t));
+                       ctx->request_stream_id = frame->hd.stream_id;
+               }
+       }
+       return 0;
+}
+
+static int query_recv_callback(nghttp2_session *session, uint8_t flags, int32_t stream_id, const uint8_t *data, size_t len, void *user_data)
+{
+       struct http_ctx_t *ctx = (struct http_ctx_t *)user_data;
+       memcpy(ctx->wire + sizeof(uint16_t), data, len);
+       ctx->wire_len = len;
+       ctx->request_stream_id = stream_id;
+       return 0;
+}
+
+struct http_ctx_t* http_new(http_send_callback cb, void *user_ctx)
+{
+       assert(cb != NULL);
+
+       nghttp2_session_callbacks *callbacks;
+       nghttp2_session_callbacks_new(&callbacks);
+       nghttp2_session_callbacks_set_send_callback(callbacks, send_callback);
+       nghttp2_session_callbacks_set_on_header_callback(callbacks, header_callback);
+       nghttp2_session_callbacks_set_on_data_chunk_recv_callback(callbacks, query_recv_callback);
+
+       struct http_ctx_t *ctx = calloc(1UL, sizeof(struct http_ctx_t));
+       ctx->send_cb = cb;
+       ctx->user_ctx = user_ctx;
+       ctx->request_stream_id = -1;
+
+       nghttp2_session_server_new(&ctx->session, callbacks, ctx);
+       nghttp2_session_callbacks_del(callbacks);
+
+       static const nghttp2_settings_entry iv[] = {
+               { NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, HTTP_MAX_CONCURRENT_STREAMS }
+       };
+
+       nghttp2_submit_settings(ctx->session, NGHTTP2_FLAG_NONE, iv, sizeof(iv)/sizeof(*iv) );
+
+       return ctx;
+}
+
+ssize_t http_process_input_data(struct session *s, const uint8_t *in_buf, ssize_t in_buf_len)
+{
+       struct http_ctx_t *http_p = session_http_get_server_ctx(s);
+       if (!http_p->session) {
+               return kr_error(ENOSYS);
+       }
+
+       http_p->wire = session_wirebuf_get_free_start(s);
+       http_p->wire_len = session_wirebuf_get_free_size(s);
+       ssize_t ret = 0;
+       if ((ret = nghttp2_session_mem_recv(http_p->session, in_buf, in_buf_len)) < 0) {
+               kr_log_error("[%s] nghttp2_session_mem_recv failed: %s (%zd)\n", server_logstring, nghttp2_strerror(ret), ret);
+               return kr_error(EIO);
+       }
+
+       if ((ret = nghttp2_session_send(http_p->session)) < 0) {
+               kr_log_error("[%s] nghttp2_session_send failed: %s (%zd)\n", server_logstring, nghttp2_strerror(ret), ret);
+               return kr_error(EIO);
+       }
+
+       ssize_t submitted = 0;
+       if (http_p->request_stream_id >= 0) {
+               knot_wire_write_u16(http_p->wire, http_p->wire_len);
+               submitted = http_p->wire_len + sizeof(uint16_t);
+       }
+
+       return submitted;
+}
+
+static ssize_t send_response_callback(nghttp2_session *session, int32_t stream_id, uint8_t *buf, size_t length, uint32_t *data_flags, nghttp2_data_source *source, void *user_data)
+{
+       struct http_data_buffer *buffer = (struct http_data_buffer *)source->ptr;
+       struct http_ctx_t *ctx = (struct http_ctx_t *)user_data; //TODO remove maybe
+       size_t send = MIN(buffer->end - buffer->data, length);
+       memcpy(buf, buffer->data, send);
+       buffer->data += send;
+       //*data_flags |= (buffer->data == buffer->end) ? NGHTTP2_DATA_FLAG_EOF : 0;
+       if (buffer->data == buffer->end) {
+               *data_flags |= NGHTTP2_DATA_FLAG_EOF;
+               ctx->request_stream_id = -1;
+       }
+       return send;
+}
+
+int http_write(uv_write_t *req, uv_handle_t *handle, knot_pkt_t *pkt, uv_write_cb cb)
+{
+       if (!pkt || !handle || !handle->data) {
+               return kr_error(EINVAL);
+       }
+
+       struct session *s = handle->data;
+       struct http_ctx_t *http_ctx = session_http_get_server_ctx(s);
+
+       assert (http_ctx);
+       assert (!session_flags(s)->outgoing);
+
+       char size[MAX_DECIMAL_LENGTH(pkt->size)] = { 0 };
+       int size_len = sprintf(size, "%ld", pkt->size);
+
+       struct http_data_buffer data_buff = {
+               .data = pkt->wire,
+               .end = pkt->wire + pkt->size
+       };
+
+       const nghttp2_data_provider data_prd = {
+               .source = {
+                       .ptr = &data_buff
+               },
+               .read_callback = send_response_callback
+       };
+
+       nghttp2_nv hdrs[] = {
+               MAKE_STATIC_NV(":status", "200"),
+               MAKE_STATIC_NV("content-type", "application/dns-message"),
+               MAKE_NV("content-length", 14, size, size_len)
+       };
+
+       int ret = nghttp2_submit_response(http_ctx->session, http_ctx->request_stream_id, hdrs, sizeof(hdrs)/sizeof(*hdrs), &data_prd);
+       if (ret != 0) {
+               kr_log_error("[%s] nghttp2_submit_response failed: %s (%d)\n", server_logstring, nghttp2_strerror(ret), ret);
+               return kr_error(EIO);
+       }
+
+       if((ret = nghttp2_session_send(http_ctx->session)) < 0) {
+               kr_log_error("[%s] nghttp2_session_send failed: %s (%d)\n", server_logstring, nghttp2_strerror(ret), ret);
+               return kr_error(EIO);
+       }
+       
+       /* The data is now accepted in gnutls internal buffers, the message can be treated as sent */
+       req->handle = (uv_stream_t *)handle;
+       cb(req, 0);
+
+       return kr_ok();
+}
+
+void http_free(struct http_ctx_t *ctx)
+{
+       if (ctx == NULL || ctx->session == NULL) {
+               return;
+       }
+       nghttp2_session_del(ctx->session);
+       ctx->session = NULL;
+}
\ No newline at end of file
diff --git a/daemon/http.h b/daemon/http.h
new file mode 100644 (file)
index 0000000..3ab1a23
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 CZ.NIC, z.s.p.o
+ *
+ * Initial Author: Jan Hák <jan.hak@nic.cz>
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <uv.h>
+#include <nghttp2/nghttp2.h>
+#include <libknot/packet/pkt.h>
+
+/** Transport session (opaque). */
+struct session;
+
+typedef ssize_t(*http_send_callback)(const uint8_t *buffer, const size_t buffer_len, void *user_ctx);
+
+struct http_ctx_t {
+       struct nghttp2_session *session;
+       http_send_callback send_cb;
+       void *user_ctx;
+       int32_t request_stream_id;
+       uint8_t *wire;
+       int32_t wire_len;
+};
+
+struct http_ctx_t* http_new(http_send_callback cb, void *user_ctx);
+ssize_t http_process_input_data(struct session *s, const uint8_t *buf, ssize_t nread);
+int http_write(uv_write_t *req, uv_handle_t *handle, knot_pkt_t *pkt, uv_write_cb cb);
+void http_free(struct http_ctx_t *ctx);
\ No newline at end of file
index 7b5f910cbf85b9edb86b43f83aadf146036ed486..7154c2f43c65d521aa3e26728aee844cc9f81980 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2014-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -12,6 +12,7 @@
 #include "daemon/network.h"
 #include "daemon/worker.h"
 #include "daemon/tls.h"
+#include "daemon/http.h"
 #include "daemon/session.h"
 #include "contrib/cleanup.h"
 #include "lib/utils.h"
@@ -183,7 +184,7 @@ int io_listen_udp(uv_loop_t *loop, uv_udp_t *handle, int fd)
        uv_handle_t *h = (uv_handle_t *)handle;
        check_bufsize(h);
        /* Handle is already created, just create context. */
-       struct session *s = session_new(h, false);
+       struct session *s = session_new(h, false, false);
        assert(s);
        session_flags(s)->outgoing = false;
 
@@ -305,6 +306,24 @@ static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
                data = session_wirebuf_get_free_start(s);
                data_len = consumed;
        }
+       if (session_flags(s)->has_http) {
+               consumed = http_process_input_data(s, data, data_len);
+               if (consumed < 0) {
+                       if (kr_verbose_status) {
+                               struct sockaddr *peer = session_get_peer(s);
+                               char *peer_str = kr_straddr(peer);
+                               kr_log_verbose("[io] => connection to '%s': "
+                                      "error processing HTTP data, close\n",
+                                      peer_str ? peer_str : "");
+                       }
+                       worker_end_tcp(s);
+                       return;
+               } else if (consumed == 0) {
+                       return;
+               }
+               data = session_wirebuf_get_free_start(s);
+               data_len = consumed;
+       }
 
        /* data points to start of the free space in session wire buffer.
           Simple increase internal counter. */
@@ -320,7 +339,52 @@ static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf)
        mp_flush(the_worker->pkt_pool.ctx);
 }
 
-static void _tcp_accept(uv_stream_t *master, int status, bool tls)
+static void on_write(uv_write_t *req, int status)
+{
+       struct qr_task *task = req->data;
+       if (task) {
+               uv_handle_t *h = (uv_handle_t *)req->handle;
+               qr_task_on_send(task, h, status);
+       }
+       free(req);
+}
+
+static ssize_t tcp_send(const uint8_t *buffer, const size_t buffer_len, void *user_ctx)
+{
+       //TODO not complete, probably do not respect the sending policy in the software
+       struct session *session = user_ctx;
+       uv_handle_t *handle = session_get_handle(session);
+       //const uint8_t *buffer_backup = (const uint8_t *)calloc(buffer_len, sizeof(*buffer));
+       //if (!buffer_backup) {
+       //      return kr_error(EIO);
+       //}
+       //memcpy(buffer_backup, buffer, buffer_len);
+       
+       uv_write_t *req = (uv_write_t *)calloc(1, sizeof(uv_write_t));
+       if (!req) {
+               return kr_error(EIO);
+       }
+       
+       const uv_buf_t uv_buffer = {
+               //.base = buffer_backup,
+               .base = buffer,
+               .len = buffer_len
+       };
+       uv_write(req, (uv_stream_t *)handle, &uv_buffer, 1, on_write);
+       return buffer_len;
+}
+
+static ssize_t tls_send(const uint8_t *buffer, const size_t buffer_len, void *user_ctx)
+{
+       struct tls_ctx_t *ctx = user_ctx;
+       ssize_t len = 0;
+       if ((len = gnutls_record_send(ctx->c.tls_session, buffer, buffer_len)) < 0) {
+               return kr_error(EIO);
+       }
+       return len;
+}
+
+static void _tcp_accept(uv_stream_t *master, int status, bool tls, bool http)
 {
        if (status != 0) {
                return;
@@ -332,7 +396,7 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
                return;
        }
        int res = io_create(master->loop, (uv_handle_t *)client,
-                           SOCK_STREAM, AF_UNSPEC, tls);
+                           SOCK_STREAM, AF_UNSPEC, tls, http);
        if (res) {
                if (res == UV_EMFILE) {
                        worker->too_many_open = true;
@@ -396,23 +460,70 @@ static void _tcp_accept(uv_stream_t *master, int status, bool tls)
                        session_tls_set_server_ctx(s, ctx);
                }
        }
+       if (http) {
+               struct http_ctx_t *ctx = session_http_get_server_ctx(s);
+               if (!ctx) {
+                       ctx = http_new((tls) ? tls_send : tcp_send,
+                                      (tls) ? (void*)session_tls_get_server_ctx(s) : (void*)s
+                       );
+                       if (!ctx) {
+                               session_close(s);
+                               return;
+                       }
+
+                       struct tls_ctx_t *tls_ctx = session_tls_get_server_ctx(s);
+                       if (tls_ctx) {
+                               const gnutls_datum_t protos[] = {
+                                       {(unsigned char *)"h2", 2}
+                               };
+                               ret = gnutls_alpn_set_protocols(tls_ctx->c.tls_session,
+                                                               protos, sizeof(protos)/sizeof(*protos),
+                                                                                               0);
+                               if (ret != GNUTLS_E_SUCCESS) {
+                                       session_close(s);
+                                       return;
+                               }
+                       }
+                       session_http_set_server_ctx(s, ctx);
+               }
+       }
        session_timer_start(s, tcp_timeout_trigger, timeout, idle_in_timeout);
        io_start_read((uv_handle_t *)client);
 }
 
 static void tcp_accept(uv_stream_t *master, int status)
 {
-       _tcp_accept(master, status, false);
+       _tcp_accept(master, status, false, false);
 }
 
 static void tls_accept(uv_stream_t *master, int status)
 {
-       _tcp_accept(master, status, true);
+       _tcp_accept(master, status, true, false);
+}
+
+static void http_accept(uv_stream_t *master, int status)
+{
+       _tcp_accept(master, status, false, true);
 }
 
-int io_listen_tcp(uv_loop_t *loop, uv_tcp_t *handle, int fd, int tcp_backlog, bool has_tls)
+static void https_accept(uv_stream_t *master, int status)
 {
-       const uv_connection_cb connection = has_tls ? tls_accept : tcp_accept;
+       _tcp_accept(master, status, true, true);
+}
+
+int io_listen_tcp(uv_loop_t *loop, uv_tcp_t *handle, int fd, int tcp_backlog, bool has_tls, bool has_http)
+{
+       uv_connection_cb connection;
+       if (has_tls && has_http) {
+               connection = https_accept;
+       } else if (has_tls) {
+               connection = tls_accept;
+       } else if (has_http) {
+               connection = http_accept;
+       } else {
+               connection = tcp_accept;
+       }
+
        if (!handle) {
                return kr_error(EINVAL);
        }
@@ -686,7 +797,7 @@ int io_listen_pipe(uv_loop_t *loop, uv_pipe_t *handle, int fd)
        return 0;
 }
 
-int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family, bool has_tls)
+int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family, bool has_tls, bool has_http)
 {
        int ret = -1;
        if (type == SOCK_DGRAM) {
@@ -698,7 +809,7 @@ int io_create(uv_loop_t *loop, uv_handle_t *handle, int type, unsigned family, b
        if (ret != 0) {
                return ret;
        }
-       struct session *s = session_new(handle, has_tls);
+       struct session *s = session_new(handle, has_tls, has_http);
        if (s == NULL) {
                ret = -1;
        }
index eefde8097f894c00ab30f4c0d0a190150270c9a1..541cd2ae69de3f62ac1de3cc7531157256dc7d8b 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2014-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -21,7 +21,7 @@ int io_bind(const struct sockaddr *addr, int type, const endpoint_flags_t *flags
 /** Initialize a UDP handle and start listening. */
 int io_listen_udp(uv_loop_t *loop, uv_udp_t *handle, int fd);
 /** Initialize a TCP handle and start listening. */
-int io_listen_tcp(uv_loop_t *loop, uv_tcp_t *handle, int fd, int tcp_backlog, bool has_tls);
+int io_listen_tcp(uv_loop_t *loop, uv_tcp_t *handle, int fd, int tcp_backlog, bool has_tls, bool has_http);
 /** Initialize a pipe handle and start listening. */
 int io_listen_pipe(uv_loop_t *loop, uv_pipe_t *handle, int fd);
 
@@ -38,7 +38,7 @@ void tcp_timeout_trigger(uv_timer_t *timer);
  * \param family = AF_*
  * \param has_tls has meanings only when type is SOCK_STREAM */
 int io_create(uv_loop_t *loop, uv_handle_t *handle, int type,
-             unsigned family, bool has_tls);
+             unsigned family, bool has_tls, bool has_http);
 void io_deinit(uv_handle_t *handle);
 void io_free(uv_handle_t *handle);
 
index e6f98ebd9a7c1a15c6d9a304ec9bacb17c58f16a..52fad30f88d9ce29d105746a69d959995af29848 100644 (file)
@@ -10,6 +10,7 @@ kresd_src = files([
   'bindings/worker.c',
   'engine.c',
   'ffimodule.c',
+  'http.c',
   'io.c',
   'main.c',
   'network.c',
@@ -17,6 +18,7 @@ kresd_src = files([
   'tls.c',
   'tls_ephemeral_credentials.c',
   'tls_session_ticket-srv.c',
+  'http.c',
   'udp_queue.c',
   'worker.c',
   'zimport.c',
@@ -43,6 +45,7 @@ kresd_deps = [
   gnutls,
   libsystemd,
   capng,
+  nghttp2,
 ]
 
 
index ee8dc56e416685dff3c4e5f38de146b9f32225f3..bf63b175197f15e55d629e5c2f75eab3e50aee7b 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2015-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2015-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -312,7 +312,7 @@ static int open_endpoint(struct network *net, struct endpoint *ep,
                        return kr_error(ENOMEM);
                }
                return io_listen_tcp(net->loop, ep_handle, ep->fd,
-                                       net->tcp_backlog, ep->flags.tls);
+                                       net->tcp_backlog, ep->flags.tls, ep->flags.http);
        } /* else */
 
        assert(!EINVAL);
index c3ee79f2ac32a9da973a0f3587bd4a111a177cb9..a960effa4c59738f3c5748f0d64b3ff9b9e87eda 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2015-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2015-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -20,6 +20,7 @@ struct engine;
 typedef struct {
        int sock_type;    /**< SOCK_DGRAM or SOCK_STREAM */
        bool tls;         /**< only used together with .kind == NULL and .tcp */
+       bool http;        /**< only used together with .kind == NULL and .tcp */
        const char *kind; /**< tag for other types than the three usual */
        bool freebind;    /**< used for binding to non-local address **/
 } endpoint_flags_t;
index 616e851956e71df806eb7d62183584f5a852e938..48ceafeae631a28eaff5d0986c3f4f7e858f9ebd 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2018-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -10,6 +10,7 @@
 #include "daemon/session.h"
 #include "daemon/engine.h"
 #include "daemon/tls.h"
+#include "daemon/http.h"
 #include "daemon/worker.h"
 #include "daemon/io.h"
 #include "lib/generic/queue.h"
@@ -32,6 +33,8 @@ struct session {
        struct tls_ctx *tls_ctx;      /**< server side tls-related data. */
        struct tls_client_ctx *tls_client_ctx;  /**< client side tls-related data. */
 
+       struct http_ctx_t *http_ctx;  /**< server side http-related data. */
+
        trie_t *tasks;                /**< list of tasks assotiated with given session. */
        queue_t(struct qr_task *) waiting;  /**< list of tasks waiting for sending to upstream. */
 
@@ -81,6 +84,7 @@ void session_clear(struct session *session)
        queue_deinit(session->waiting);
        tls_free(session->tls_ctx);
        tls_client_ctx_free(session->tls_client_ctx);
+       http_free(session->http_ctx);
        memset(session, 0, sizeof(*session));
 }
 
@@ -284,6 +288,16 @@ struct tls_common_ctx *session_tls_get_common_ctx(const struct session *session)
        return tls_ctx;
 }
 
+struct http_ctx_t *session_http_get_server_ctx(const struct session *session)
+{
+       return session->http_ctx;
+}
+
+void session_http_set_server_ctx(struct session *session, struct http_ctx_t *ctx)
+{
+       session->http_ctx = ctx;
+}
+
 uv_handle_t *session_get_handle(struct session *session)
 {
        return session->handle;
@@ -294,7 +308,7 @@ struct session *session_get(uv_handle_t *h)
        return h->data;
 }
 
-struct session *session_new(uv_handle_t *handle, bool has_tls)
+struct session *session_new(uv_handle_t *handle, bool has_tls, bool has_http)
 {
        if (!handle) {
                return NULL;
@@ -314,6 +328,9 @@ struct session *session_new(uv_handle_t *handle, bool has_tls)
                        wire_buffer_size += TLS_CHUNK_SIZE;
                        session->sflags.has_tls = true;
                }
+               if (has_http) {
+                       session->sflags.has_http = true;
+               }
                uint8_t *wire_buf = malloc(wire_buffer_size);
                if (!wire_buf) {
                        free(session);
index a36eb6134e87e7df90a1e7de5bb1e3045bd76c54..e71653fb315905e5af1416c2d678778bcfd09eb9 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2018 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2018-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -17,6 +17,7 @@ struct session_flags {
        bool outgoing : 1;      /**< True: to upstream; false: from a client. */
        bool throttled : 1;     /**< True: data reading from peer is temporarily stopped. */
        bool has_tls : 1;       /**< True: given session uses TLS. */
+       bool has_http : 1;      /**< True: given session uses HTTP. */
        bool connected : 1;     /**< True: TCP connection is established. */
        bool closing : 1;       /**< True: session close sequence is in progress. */
        bool wirebuf_error : 1; /**< True: last operation with wirebuf ended up with an error. */
@@ -24,7 +25,7 @@ struct session_flags {
 
 /* Allocate new session for a libuv handle.
  * If handle->tyoe is UV_UDP, tls parameter will be ignored. */
-struct session *session_new(uv_handle_t *handle, bool has_tls);
+struct session *session_new(uv_handle_t *handle, bool has_tls, bool has_http);
 /* Clear and free given session. */
 void session_free(struct session *session);
 /* Clear session. */
@@ -95,6 +96,11 @@ void session_tls_set_client_ctx(struct session *session, struct tls_client_ctx *
  *  server and client. */
 struct tls_common_ctx *session_tls_get_common_ctx(const struct session *session);
 
+/** Get pointer to server-side http-related data. */
+struct http_ctx_t *session_http_get_server_ctx(const struct session *session);
+/** Set pointer to server-side http-related data. */
+void session_http_set_server_ctx(struct session *session, struct http_ctx_t *ctx);
+
 /** Get pointer to underlying libuv handle for IO operations. */
 uv_handle_t *session_get_handle(struct session *session);
 struct session *session_get(uv_handle_t *h);
index dc262003a15ea9078055d9d1d335430985358c54..47bb3b0e139931474f7bd4f4493d9eded1d8fe00 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2014-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -26,6 +26,7 @@
 #include "daemon/io.h"
 #include "daemon/session.h"
 #include "daemon/tls.h"
+#include "daemon/http.h"
 #include "daemon/udp_queue.h"
 #include "daemon/zimport.h"
 #include "lib/layer.h"
@@ -128,7 +129,8 @@ struct worker_ctx *the_worker = NULL;
 /*! @internal Create a UDP/TCP handle for an outgoing AF_INET* connection.
  *  socktype is SOCK_* */
 static uv_handle_t *ioreq_spawn(struct worker_ctx *worker,
-                               int socktype, sa_family_t family, bool has_tls)
+                               int socktype, sa_family_t family, bool has_tls,
+                               bool has_http)
 {
        bool precond = (socktype == SOCK_DGRAM || socktype == SOCK_STREAM)
                        && (family == AF_INET  || family == AF_INET6);
@@ -144,7 +146,7 @@ static uv_handle_t *ioreq_spawn(struct worker_ctx *worker,
        if (!handle) {
                return NULL;
        }
-       int ret = io_create(worker->loop, handle, socktype, family, has_tls);
+       int ret = io_create(worker->loop, handle, socktype, family, has_tls, has_http);
        if (ret) {
                if (ret == UV_EMFILE) {
                        worker->too_many_open = true;
@@ -295,6 +297,7 @@ static struct request_ctx *request_create(struct worker_ctx *worker,
                req->qsource.dst_addr = session_get_sockname(session);
                req->qsource.flags.tcp = session_get_handle(session)->type == UV_TCP;
                req->qsource.flags.tls = session_flags(session)->has_tls;
+               req->qsource.flags.http = session_flags(session)->has_http;
                /* We need to store a copy of peer address. */
                memcpy(&ctx->source.addr.ip, peer, kr_sockaddr_len(peer));
                req->qsource.addr = &ctx->source.addr.ip;
@@ -606,7 +609,11 @@ static int qr_task_send(struct qr_task *task, struct session *session,
        struct worker_ctx *worker = ctx->worker;
        /* Send using given protocol */
        assert(!session_flags(session)->closing);
-       if (session_flags(session)->has_tls) {
+       if (session_flags(session)->has_http) {
+               uv_write_t *write_req = (uv_write_t *)ioreq;
+               write_req->data = task;
+               ret = http_write(write_req, handle, pkt, &on_write);
+       } else if (session_flags(session)->has_tls) {
                uv_write_t *write_req = (uv_write_t *)ioreq;
                write_req->data = task;
                ret = tls_write(write_req, handle, pkt, &on_write);
@@ -1034,7 +1041,7 @@ static uv_handle_t *retransmit(struct qr_task *task)
                if (kr_resolve_checkout(&ctx->req, NULL, (struct sockaddr *)choice, SOCK_DGRAM, task->pktbuf) != 0) {
                        return ret;
                }
-               ret = ioreq_spawn(ctx->worker, SOCK_DGRAM, choice->sin6_family, false);
+               ret = ioreq_spawn(ctx->worker, SOCK_DGRAM, choice->sin6_family, false, false);
                if (!ret) {
                        return ret;
                }
@@ -1317,8 +1324,9 @@ static int tcp_task_make_connection(struct qr_task *task, const struct sockaddr
                tls_client_ctx_free(tls_ctx);
                return kr_error(EINVAL);
        }
+       bool has_http = false;
        bool has_tls = (tls_ctx != NULL);
-       uv_handle_t *client = ioreq_spawn(worker, SOCK_STREAM, addr->sa_family, has_tls);
+       uv_handle_t *client = ioreq_spawn(worker, SOCK_STREAM, addr->sa_family, has_tls, has_http);
        if (!client) {
                tls_client_ctx_free(tls_ctx);
                free(conn);
index be2d3a94904174b1a2ed20d3cf64456a6daa5d81..2b13b7695de02bab1a39ad531a5061dfebeb0b8c 100644 (file)
@@ -1,4 +1,4 @@
-/*  Copyright (C) 2014-2017 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/*  Copyright (C) 2014-2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
  *  SPDX-License-Identifier: GPL-3.0-or-later
  */
 
@@ -59,6 +59,8 @@ static inline int KR_COLD kr_error(int x) {
  * Defines.
  */
 #define KR_DNS_PORT   53
+#define KR_DNS_HTTP_PORT 80
+#define KR_DNS_DOH_PORT 443
 #define KR_DNS_TLS_PORT 853
 #define KR_EDNS_VERSION 0
 #define KR_EDNS_PAYLOAD 4096 /* Default UDP payload (max unfragmented UDP is 1452B) */
index bf43d458b5df41810e09ed54a17d7740b0885d61..58abd209d2ecd187e4b0f59ca579c63ecd81cd80 100644 (file)
@@ -29,6 +29,7 @@ if not lmdb.found()  # darwin workaround: missing pkgconfig
 endif
 gnutls = dependency('gnutls')
 luajit = dependency('luajit')
+nghttp2 = dependency('libnghttp2')
 # NOTE avoid using link_args for luajit due to a macOS issue
 # https://github.com/Homebrew/homebrew-core/issues/37169
 luajit_inc = luajit.partial_dependency(compile_args: true, includes: true)