]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
daemon/proxyv2: move PROXY protocol into its own layer
authorOto Šťáva <oto.stava@nic.cz>
Tue, 21 May 2024 17:04:38 +0000 (19:04 +0200)
committerOto Šťáva <oto.stava@nic.cz>
Mon, 27 May 2024 12:02:30 +0000 (14:02 +0200)
Previously, PROXYv2 handling was partially implemented in the `io.c`
unit in the `_TCP` and `_UDP` protocol layers, which technically made
very little sense. This commit moves this handling into separate
`_PROXYV2_DGRAM` and `_PROXYV2_STREAM` protocol layers, basically
encapsulating the handling of proxies in the `proxyv2.c` unit.

daemon/io.c
daemon/main.c
daemon/proxyv2.c
daemon/proxyv2.h
daemon/session2.c
daemon/session2.h

index 8333577ded448e9b2db53298697f9210b61f0df5..d19da0ebd801e87e8e823686cdebddc1acd76bdb 100644 (file)
@@ -17,7 +17,6 @@
 #endif
 
 #include "daemon/network.h"
-#include "daemon/proxyv2.h"
 #include "daemon/worker.h"
 #include "daemon/tls.h"
 #include "daemon/http.h"
@@ -137,73 +136,6 @@ static int family_to_freebind_option(sa_family_t sa_family, int *level, int *nam
 }
 
 
-struct pl_udp_iter_data {
-       struct protolayer_data h;
-       struct proxy_result proxy;
-       bool has_proxy;
-};
-
-static enum protolayer_iter_cb_result pl_udp_unwrap(
-               void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx)
-{
-       ctx->payload = protolayer_payload_as_buffer(&ctx->payload);
-       if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_BUFFER)) {
-               /* unsupported payload */
-               return protolayer_break(ctx, kr_error(EINVAL));
-       }
-
-       struct session2 *s = ctx->manager->session;
-       struct pl_udp_iter_data *udp = iter_data;
-
-       char *data = ctx->payload.buffer.buf;
-       ssize_t data_len = ctx->payload.buffer.len;
-       struct comm_info *comm = &ctx->comm;
-       if (!s->outgoing && proxy_header_present(data, data_len)) {
-               if (!proxy_allowed(comm->comm_addr)) {
-                       kr_log_debug(IO, "<= ignoring PROXYv2 UDP from disallowed address '%s'\n",
-                                       kr_straddr(comm->comm_addr));
-                       return protolayer_break(ctx, kr_error(EPERM));
-               }
-
-               ssize_t trimmed = proxy_process_header(&udp->proxy, data, data_len);
-               if (trimmed == KNOT_EMALF) {
-                       if (kr_log_is_debug(IO, NULL)) {
-                               kr_log_debug(IO, "<= ignoring malformed PROXYv2 UDP "
-                                               "from address '%s'\n",
-                                               kr_straddr(comm->comm_addr));
-                       }
-                       return protolayer_break(ctx, kr_error(EINVAL));
-               } else if (trimmed < 0) {
-                       if (kr_log_is_debug(IO, NULL)) {
-                               kr_log_debug(IO, "<= error processing PROXYv2 UDP "
-                                               "from address '%s', ignoring\n",
-                                               kr_straddr(comm->comm_addr));
-                       }
-                       return protolayer_break(ctx, kr_error(EINVAL));
-               }
-
-               if (udp->proxy.command == PROXY2_CMD_PROXY && udp->proxy.family != AF_UNSPEC) {
-                       udp->has_proxy = true;
-
-                       comm->src_addr = &udp->proxy.src_addr.ip;
-                       comm->dst_addr = &udp->proxy.dst_addr.ip;
-                       comm->proxy = &udp->proxy;
-
-                       if (kr_log_is_debug(IO, NULL)) {
-                               kr_log_debug(IO, "<= UDP query from '%s'\n",
-                                               kr_straddr(comm->src_addr));
-                               kr_log_debug(IO, "<= proxied through '%s'\n",
-                                               kr_straddr(comm->comm_addr));
-                       }
-               }
-
-               ctx->payload = protolayer_payload_buffer(
-                               data + trimmed, data_len - trimmed, false);
-       }
-
-       return protolayer_continue(ctx);
-}
-
 static enum protolayer_event_cb_result pl_udp_event_wrap(
                enum protolayer_event_type event, void **baton,
                struct protolayer_manager *manager, void *sess_data)
@@ -219,127 +151,6 @@ static enum protolayer_event_cb_result pl_udp_event_wrap(
        return PROTOLAYER_EVENT_PROPAGATE;
 }
 
-
-struct pl_tcp_sess_data {
-       struct protolayer_data h;
-       struct proxy_result proxy;
-       struct wire_buf wire_buf;
-       bool had_data : 1;
-       bool has_proxy : 1;
-};
-
-static int pl_tcp_sess_init(struct protolayer_manager *manager,
-                            void *data,
-                            void *param)
-{
-       struct sockaddr *peer = session2_get_peer(manager->session);
-       manager->session->comm = (struct comm_info) {
-               .comm_addr = peer,
-               .src_addr = peer
-       };
-       return 0;
-}
-
-static int pl_tcp_sess_deinit(struct protolayer_manager *manager, void *sess_data)
-{
-       struct pl_tcp_sess_data *tcp = sess_data;
-       wire_buf_deinit(&tcp->wire_buf);
-       return 0;
-}
-
-static enum protolayer_iter_cb_result pl_tcp_unwrap(
-               void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx)
-{
-       struct session2 *s = ctx->manager->session;
-       struct pl_tcp_sess_data *tcp = sess_data;
-       struct sockaddr *peer = session2_get_peer(s);
-
-       if (ctx->payload.type == PROTOLAYER_PAYLOAD_BUFFER) {
-               const char *buf = ctx->payload.buffer.buf;
-               const size_t len = ctx->payload.buffer.len;
-
-               /* Copy a simple buffer into internal wirebuffer. */
-               if (len > KNOT_WIRE_MAX_PKTSIZE)
-                       return protolayer_break(ctx, kr_error(EMSGSIZE));
-
-               if (!tcp->wire_buf.buf) {
-                       int ret = wire_buf_reserve(&tcp->wire_buf,
-                                       KNOT_WIRE_MAX_PKTSIZE);
-                       if (ret)
-                               return protolayer_break(ctx, ret);
-               }
-
-               /* Check if space can be made */
-               if (len > wire_buf_free_space_length(&tcp->wire_buf)) {
-                       if (len > tcp->wire_buf.size - wire_buf_data_length(&tcp->wire_buf))
-                               return protolayer_break(ctx, kr_error(EMSGSIZE));
-                       wire_buf_movestart(&tcp->wire_buf);
-               }
-
-               memcpy(wire_buf_free_space(&tcp->wire_buf), buf, len);
-               wire_buf_consume(&tcp->wire_buf, ctx->payload.buffer.len);
-               ctx->payload = protolayer_payload_wire_buf(&tcp->wire_buf, false);
-       }
-
-       if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF)) {
-               /* TODO: iovec support unimplemented */
-               return protolayer_break(ctx, kr_error(EINVAL));
-       }
-
-       char *data = wire_buf_data(ctx->payload.wire_buf); /* layer's or session's wirebuf */
-       ssize_t data_len = wire_buf_data_length(ctx->payload.wire_buf);
-       struct comm_info *comm = &ctx->manager->session->comm;
-       if (!s->outgoing && !tcp->had_data && proxy_header_present(data, data_len)) {
-               if (!proxy_allowed(comm->src_addr)) {
-                       if (kr_log_is_debug(IO, NULL)) {
-                               kr_log_debug(IO, "<= connection to '%s': PROXYv2 not allowed "
-                                               "for this peer, close\n",
-                                               kr_straddr(peer));
-                       }
-                       worker_end_tcp(s);
-                       return protolayer_break(ctx, kr_error(ECONNRESET));
-               }
-
-               ssize_t trimmed = proxy_process_header(&tcp->proxy, data, data_len);
-               if (trimmed < 0) {
-                       if (kr_log_is_debug(IO, NULL)) {
-                               if (trimmed == KNOT_EMALF) {
-                                       kr_log_debug(IO, "<= connection to '%s': "
-                                                       "malformed PROXYv2 header, close\n",
-                                                       kr_straddr(comm->src_addr));
-                               } else {
-                                       kr_log_debug(IO, "<= connection to '%s': "
-                                                       "error processing PROXYv2 header, close\n",
-                                                       kr_straddr(comm->src_addr));
-                               }
-                       }
-                       worker_end_tcp(s);
-                       return protolayer_break(ctx, kr_error(ECONNRESET));
-               } else if (trimmed == 0) {
-                       session2_close(s);
-                       return protolayer_break(ctx, kr_error(ECONNRESET));
-               }
-
-               if (tcp->proxy.command != PROXY2_CMD_LOCAL && tcp->proxy.family != AF_UNSPEC) {
-                       comm->src_addr = &tcp->proxy.src_addr.ip;
-                       comm->dst_addr = &tcp->proxy.dst_addr.ip;
-
-                       if (kr_log_is_debug(IO, NULL)) {
-                               kr_log_debug(IO, "<= TCP stream from '%s'\n",
-                                               kr_straddr(comm->src_addr));
-                               kr_log_debug(IO, "<= proxied through '%s'\n",
-                                               kr_straddr(comm->comm_addr));
-                       }
-               }
-
-               wire_buf_trim(ctx->payload.wire_buf, trimmed);
-       }
-
-       tcp->had_data = true;
-       ctx->comm = ctx->manager->session->comm;
-       return protolayer_continue(ctx);
-}
-
 static enum protolayer_event_cb_result pl_tcp_event_wrap(
                enum protolayer_event_type event, void **baton,
                struct protolayer_manager *manager, void *sess_data)
@@ -358,16 +169,10 @@ static enum protolayer_event_cb_result pl_tcp_event_wrap(
 void io_protolayers_init(void)
 {
        protolayer_globals[PROTOLAYER_TYPE_UDP] = (struct protolayer_globals){
-               .iter_size = sizeof(struct pl_udp_iter_data),
-               .unwrap = pl_udp_unwrap,
                .event_wrap = pl_udp_event_wrap,
        };
 
        protolayer_globals[PROTOLAYER_TYPE_TCP] = (struct protolayer_globals){
-               .sess_size = sizeof(struct pl_tcp_sess_data),
-               .sess_init = pl_tcp_sess_init,
-               .sess_deinit = pl_tcp_sess_deinit,
-               .unwrap = pl_tcp_unwrap,
                .event_wrap = pl_tcp_event_wrap,
        };
 }
index 8185c1c67b08ec499b39b10bed83e1cd92284bfb..63cdd7f2493b13de8572f34c95faf8414a334fbe 100644 (file)
@@ -7,6 +7,7 @@
 #include "contrib/ucw/mempool.h"
 #include "daemon/engine.h"
 #include "daemon/io.h"
+#include "daemon/proxyv2.h"
 #include "daemon/network.h"
 #include "daemon/udp_queue.h"
 #include "daemon/worker.h"
@@ -585,6 +586,7 @@ int main(int argc, char **argv)
 
        io_protolayers_init();
        tls_protolayers_init();
+       proxy_protolayers_init();
 #ifdef ENABLE_DOH2
        http_protolayers_init();
 #endif
index ce0ea0a6861813ddaa7145897e1d6bd8c1b88a45..1b61f20f4f3e2ce55371c5e35fbb85e5f44fa065 100644 (file)
@@ -3,14 +3,17 @@
  */
 
 #include "daemon/network.h"
+#include "daemon/session2.h"
+#include "daemon/worker.h"
 #include "lib/generic/trie.h"
 
 #include "daemon/proxyv2.h"
 
-const char PROXY2_SIGNATURE[12] = {
+static const char PROXY2_SIGNATURE[12] = {
        0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51, 0x55, 0x49, 0x54, 0x0A
 };
 
+#define PROXY2_MIN_SIZE 16
 #define PROXY2_IP6_ADDR_SIZE 16
 #define PROXY2_UNIX_ADDR_SIZE 108
 
@@ -129,7 +132,9 @@ static inline void next_tlv(struct proxy2_tlv **tlv)
 }
 
 
-bool proxy_allowed(const struct sockaddr *saddr)
+/** Checks whether the use of PROXYv2 protocol is allowed for the specified
+ * address. */
+static bool proxy_allowed(const struct sockaddr *saddr)
 {
        union kr_in_addr addr;
        trie_t *trie;
@@ -167,7 +172,10 @@ bool proxy_allowed(const struct sockaddr *saddr)
        return kr_bitcmp((char *)&addr, (char *)&found->addr, found->netmask) == 0;
 }
 
-ssize_t proxy_process_header(struct proxy_result *out,
+/** Parses the PROXYv2 header from buf of size nread and writes the result into
+ * out. The function assumes that the PROXYv2 signature is present
+ * and has been already checked by the caller (like `udp_recv` or `tcp_recv`). */
+static ssize_t proxy_process_header(struct proxy_result *out,
                const void *buf, const ssize_t nread)
 {
        if (!buf)
@@ -293,3 +301,179 @@ ssize_t proxy_process_header(struct proxy_result *out,
 fill_wirebuf:
        return hdr_len;
 }
+
+/** Checks for a PROXY protocol version 2 signature in the specified buffer. */
+static inline bool proxy_header_present(const void* buf, const ssize_t nread)
+{
+       return nread >= PROXY2_MIN_SIZE &&
+               memcmp(buf, PROXY2_SIGNATURE, sizeof(PROXY2_SIGNATURE)) == 0;
+}
+
+
+struct pl_proxyv2_dgram_iter_data {
+       struct protolayer_data h;
+       struct proxy_result proxy;
+       bool has_proxy;
+};
+
+static enum protolayer_iter_cb_result pl_proxyv2_dgram_unwrap(
+               void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx)
+{
+       ctx->payload = protolayer_payload_as_buffer(&ctx->payload);
+       if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_BUFFER)) {
+               /* unsupported payload */
+               return protolayer_break(ctx, kr_error(EINVAL));
+       }
+
+       struct session2 *s = ctx->manager->session;
+       struct pl_proxyv2_dgram_iter_data *udp = iter_data;
+
+       char *data = ctx->payload.buffer.buf;
+       ssize_t data_len = ctx->payload.buffer.len;
+       struct comm_info *comm = &ctx->comm;
+       if (!s->outgoing && proxy_header_present(data, data_len)) {
+               if (!proxy_allowed(comm->comm_addr)) {
+                       kr_log_debug(IO, "<= ignoring PROXYv2 UDP from disallowed address '%s'\n",
+                                       kr_straddr(comm->comm_addr));
+                       return protolayer_break(ctx, kr_error(EPERM));
+               }
+
+               ssize_t trimmed = proxy_process_header(&udp->proxy, data, data_len);
+               if (trimmed == KNOT_EMALF) {
+                       if (kr_log_is_debug(IO, NULL)) {
+                               kr_log_debug(IO, "<= ignoring malformed PROXYv2 UDP "
+                                               "from address '%s'\n",
+                                               kr_straddr(comm->comm_addr));
+                       }
+                       return protolayer_break(ctx, kr_error(EINVAL));
+               } else if (trimmed < 0) {
+                       if (kr_log_is_debug(IO, NULL)) {
+                               kr_log_debug(IO, "<= error processing PROXYv2 UDP "
+                                               "from address '%s', ignoring\n",
+                                               kr_straddr(comm->comm_addr));
+                       }
+                       return protolayer_break(ctx, kr_error(EINVAL));
+               }
+
+               if (udp->proxy.command == PROXY2_CMD_PROXY && udp->proxy.family != AF_UNSPEC) {
+                       udp->has_proxy = true;
+
+                       comm->src_addr = &udp->proxy.src_addr.ip;
+                       comm->dst_addr = &udp->proxy.dst_addr.ip;
+                       comm->proxy = &udp->proxy;
+
+                       if (kr_log_is_debug(IO, NULL)) {
+                               kr_log_debug(IO, "<= UDP query from '%s'\n",
+                                               kr_straddr(comm->src_addr));
+                               kr_log_debug(IO, "<= proxied through '%s'\n",
+                                               kr_straddr(comm->comm_addr));
+                       }
+               }
+
+               ctx->payload = protolayer_payload_buffer(
+                               data + trimmed, data_len - trimmed, false);
+       }
+
+       return protolayer_continue(ctx);
+}
+
+
+struct pl_proxyv2_stream_sess_data {
+       struct protolayer_data h;
+       struct proxy_result proxy;
+       bool had_data : 1;
+       bool has_proxy : 1;
+};
+
+static int pl_proxyv2_stream_sess_init(struct protolayer_manager *manager,
+                            void *data,
+                            void *param)
+{
+       struct sockaddr *peer = session2_get_peer(manager->session);
+       manager->session->comm = (struct comm_info) {
+               .comm_addr = peer,
+               .src_addr = peer
+       };
+       return 0;
+}
+
+static enum protolayer_iter_cb_result pl_proxyv2_stream_unwrap(
+               void *sess_data, void *iter_data, struct protolayer_iter_ctx *ctx)
+{
+       struct session2 *s = ctx->manager->session;
+       struct pl_proxyv2_stream_sess_data *tcp = sess_data;
+       struct sockaddr *peer = session2_get_peer(s);
+
+       if (kr_fails_assert(ctx->payload.type == PROTOLAYER_PAYLOAD_WIRE_BUF)) {
+               /* Only wire buffer is supported */
+               return protolayer_break(ctx, kr_error(EINVAL));
+       }
+
+       char *data = wire_buf_data(ctx->payload.wire_buf); /* layer's or session's wirebuf */
+       ssize_t data_len = wire_buf_data_length(ctx->payload.wire_buf);
+       struct comm_info *comm = &ctx->manager->session->comm;
+       if (!s->outgoing && !tcp->had_data && proxy_header_present(data, data_len)) {
+               if (!proxy_allowed(comm->src_addr)) {
+                       if (kr_log_is_debug(IO, NULL)) {
+                               kr_log_debug(IO, "<= connection to '%s': PROXYv2 not allowed "
+                                               "for this peer, close\n",
+                                               kr_straddr(peer));
+                       }
+                       worker_end_tcp(s);
+                       return protolayer_break(ctx, kr_error(ECONNRESET));
+               }
+
+               ssize_t trimmed = proxy_process_header(&tcp->proxy, data, data_len);
+               if (trimmed < 0) {
+                       if (kr_log_is_debug(IO, NULL)) {
+                               if (trimmed == KNOT_EMALF) {
+                                       kr_log_debug(IO, "<= connection to '%s': "
+                                                       "malformed PROXYv2 header, close\n",
+                                                       kr_straddr(comm->src_addr));
+                               } else {
+                                       kr_log_debug(IO, "<= connection to '%s': "
+                                                       "error processing PROXYv2 header, close\n",
+                                                       kr_straddr(comm->src_addr));
+                               }
+                       }
+                       worker_end_tcp(s);
+                       return protolayer_break(ctx, kr_error(ECONNRESET));
+               } else if (trimmed == 0) {
+                       session2_close(s);
+                       return protolayer_break(ctx, kr_error(ECONNRESET));
+               }
+
+               if (tcp->proxy.command != PROXY2_CMD_LOCAL && tcp->proxy.family != AF_UNSPEC) {
+                       comm->src_addr = &tcp->proxy.src_addr.ip;
+                       comm->dst_addr = &tcp->proxy.dst_addr.ip;
+
+                       if (kr_log_is_debug(IO, NULL)) {
+                               kr_log_debug(IO, "<= TCP stream from '%s'\n",
+                                               kr_straddr(comm->src_addr));
+                               kr_log_debug(IO, "<= proxied through '%s'\n",
+                                               kr_straddr(comm->comm_addr));
+                       }
+               }
+
+               wire_buf_trim(ctx->payload.wire_buf, trimmed);
+       }
+
+       tcp->had_data = true;
+       ctx->comm = ctx->manager->session->comm;
+       return protolayer_continue(ctx);
+}
+
+
+void proxy_protolayers_init(void)
+{
+       protolayer_globals[PROTOLAYER_TYPE_PROXYV2_DGRAM] = (struct protolayer_globals){
+               .iter_size = sizeof(struct pl_proxyv2_dgram_iter_data),
+               .unwrap = pl_proxyv2_dgram_unwrap,
+       };
+
+       protolayer_globals[PROTOLAYER_TYPE_PROXYV2_STREAM] = (struct protolayer_globals){
+               .sess_size = sizeof(struct pl_proxyv2_stream_sess_data),
+               .sess_init = pl_proxyv2_stream_sess_init,
+               .unwrap = pl_proxyv2_stream_unwrap,
+       };
+}
index a21f14b1eb69f0c023b2c32c2d0e423f911359bf..6a6bc1794905bdbf0617980442848acc652f5514 100644 (file)
@@ -8,10 +8,6 @@
 
 #include "lib/utils.h"
 
-extern const char PROXY2_SIGNATURE[12];
-
-#define PROXY2_MIN_SIZE 16
-
 enum proxy2_command {
        PROXY2_CMD_LOCAL = 0x0,
        PROXY2_CMD_PROXY = 0x1
@@ -35,19 +31,5 @@ struct proxy_result {
        bool has_tls : 1;
 };
 
-/** Checks for a PROXY protocol version 2 signature in the specified buffer. */
-static inline bool proxy_header_present(const void* buf, const ssize_t nread)
-{
-       return nread >= PROXY2_MIN_SIZE &&
-               memcmp(buf, PROXY2_SIGNATURE, sizeof(PROXY2_SIGNATURE)) == 0;
-}
-
-/** Checks whether the use of PROXYv2 protocol is allowed for the specified
- * address. */
-bool proxy_allowed(const struct sockaddr *saddr);
-
-/** Parses the PROXYv2 header from buf of size nread and writes the result into
- * out. The function assumes that the PROXYv2 signature is present
- * and has been already checked by the caller (like `udp_recv` or `tcp_recv`). */
-ssize_t proxy_process_header(struct proxy_result *out,
-                             const void *buf, ssize_t nread);
+/** Initializes the protocol layers managed by the PROXYv2 "module". */
+void proxy_protolayers_init(void);
index fa3feb9c4bb91a4cd362632dd8eb8ff5b9549489..818300d862b9c8519fa97225708504ad2ffa1378 100644 (file)
@@ -35,22 +35,26 @@ struct protolayer_globals protolayer_globals[PROTOLAYER_TYPE_COUNT] = {{0}};
 
 static const enum protolayer_type protolayer_grp_udp53[] = {
        PROTOLAYER_TYPE_UDP,
+       PROTOLAYER_TYPE_PROXYV2_DGRAM,
        PROTOLAYER_TYPE_DNS_DGRAM,
 };
 
 static const enum protolayer_type protolayer_grp_tcp53[] = {
        PROTOLAYER_TYPE_TCP,
+       PROTOLAYER_TYPE_PROXYV2_STREAM,
        PROTOLAYER_TYPE_DNS_MULTI_STREAM,
 };
 
 static const enum protolayer_type protolayer_grp_dot[] = {
        PROTOLAYER_TYPE_TCP,
+       PROTOLAYER_TYPE_PROXYV2_STREAM,
        PROTOLAYER_TYPE_TLS,
        PROTOLAYER_TYPE_DNS_MULTI_STREAM,
 };
 
 static const enum protolayer_type protolayer_grp_doh[] = {
        PROTOLAYER_TYPE_TCP,
+       PROTOLAYER_TYPE_PROXYV2_STREAM,
        PROTOLAYER_TYPE_TLS,
        PROTOLAYER_TYPE_HTTP,
        PROTOLAYER_TYPE_DNS_UNSIZED_STREAM,
index f65cf5d6ba1dbb7e907244486908e8fbef8164c3..ac83d8dec6c9de4100a97c828bdb789c022daaf6 100644 (file)
@@ -212,6 +212,10 @@ static inline size_t wire_buf_free_space_length(const struct wire_buf *wb)
        XX(TLS)\
        XX(HTTP)\
        \
+       /* PROXYv2 */\
+       XX(PROXYV2_DGRAM)\
+       XX(PROXYV2_STREAM)\
+       \
        /* DNS (`worker`) */\
        XX(DNS_DGRAM) /**< Packets WITHOUT prepended size, one per (un)wrap,
                       * limited to UDP sizes, multiple sources (single