]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Generalise the proxy protocol code
authorGeorge Thessalonikefs <george@nlnetlabs.nl>
Mon, 24 Apr 2023 14:15:56 +0000 (16:15 +0200)
committerGeorge Thessalonikefs <george@nlnetlabs.nl>
Mon, 24 Apr 2023 14:15:56 +0000 (16:15 +0200)
daemon/worker.c
libunbound/libworker.c
testcode/streamtcp.c
util/netevent.c
util/proxy_protocol.c
util/proxy_protocol.h

index 99dcf9940004a2de628b401637ca045dbc992d0b..46331376df4c1dfd334276c1e9097a6e09730211 100644 (file)
@@ -66,6 +66,7 @@
 #include "util/data/msgencode.h"
 #include "util/data/dname.h"
 #include "util/fptr_wlist.h"
+#include "util/proxy_protocol.h"
 #include "util/tube.h"
 #include "util/edns.h"
 #include "iterator/iter_fwd.h"
@@ -2168,6 +2169,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
                        worker->env.cfg->stat_interval);
                worker_restart_timer(worker);
        }
+       pp_init(&sldns_write_uint16, &sldns_write_uint32);
        return 1;
 }
 
index ebc1df2e521357c42b36e9e1e7ba3c795b250d99..7d08b488abb9158d8f011d8395abf85b9c1bec7b 100644 (file)
@@ -62,6 +62,7 @@
 #include "util/random.h"
 #include "util/config_file.h"
 #include "util/netevent.h"
+#include "util/proxy_protocol.h"
 #include "util/storage/lookup3.h"
 #include "util/storage/slabhash.h"
 #include "util/net_help.h"
@@ -263,6 +264,7 @@ libworker_setup(struct ub_ctx* ctx, int is_bg, struct ub_event_base* eb)
        w->env->kill_sub = &mesh_state_delete;
        w->env->detect_cycle = &mesh_detect_cycle;
        comm_base_timept(w->base, &w->env->now, &w->env->now_tv);
+       pp_init(&sldns_write_uint16, &sldns_write_uint32);
        return w;
 }
 
index b2c0d5328bb7e116105e66e544d30eb089a1db3f..53b29852f0a2ea69e20f3c258c5329cd2a32c75c 100644 (file)
@@ -353,6 +353,7 @@ static int parse_pp2_client(const char* pp2_client, int udp,
        sldns_buffer* proxy_buf)
 {
        struct sockaddr_storage pp2_addr;
+       size_t bytes_written;
        socklen_t pp2_addrlen = 0;
        memset(&pp2_addr, 0, sizeof(pp2_addr));
        if(*pp2_client == 0) return 0;
@@ -361,7 +362,9 @@ static int parse_pp2_client(const char* pp2_client, int udp,
                exit(1);
        }
        sldns_buffer_clear(proxy_buf);
-       pp2_write_to_buf(proxy_buf, &pp2_addr, !udp);
+       bytes_written = pp2_write_to_buf(sldns_buffer_begin(proxy_buf),
+               sldns_buffer_remaining(proxy_buf), &pp2_addr, !udp);
+       sldns_buffer_set_position(proxy_buf, bytes_written);
        sldns_buffer_flip(proxy_buf);
        return 1;
 }
@@ -529,6 +532,8 @@ int main(int argc, char** argv)
                                break;
                        case 'p':
                                pp2_client = optarg;
+                               pp_init(&sldns_write_uint16,
+                                       &sldns_write_uint32);
                                break;
                        case 'a':
                                onarrival = 1;
index fe3d511643f8b5de1a789f5244b013cc32f5f6df..c0fe1e6a6f07c2c5e16d9472e90c0f1b73699dbd 100644 (file)
@@ -761,8 +761,11 @@ static int udp_recv_needs_log(int err)
 static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
        int stream) {
        size_t size;
-       struct pp2_header *header = pp2_read_header(buf);
-       if(header == NULL) return 0;
+       struct pp2_header *header;
+       int err = pp2_read_header(sldns_buffer_begin(buf),
+               sldns_buffer_remaining(buf));
+       if(err) return 0;
+       header = (struct pp2_header*)sldns_buffer_begin(buf);
        size = PP2_HEADER_SIZE + ntohs(header->len);
        if((header->ver_cmd & 0xF) == PP2_CMD_LOCAL) {
                /* A connection from the proxy itself.
@@ -803,6 +806,10 @@ static int consume_pp2_header(struct sldns_buffer* buf, struct comm_reply* rep,
                        }
                        /* Ignore the destination address; it should be us. */
                        break;
+               default:
+                       log_err("proxy_protocol: unsupported family and "
+                               "protocol");
+                       return 0;
        }
        rep->is_proxied = 1;
 done:
@@ -1675,12 +1682,17 @@ ssl_handle_read(struct comm_point* c)
                        }
                }
                if(c->pp2_header_state == pp2_header_init) {
-                       header = pp2_read_header(c->buffer);
-                       if(!header) {
+                       int err;
+                       err = pp2_read_header(
+                               sldns_buffer_begin(c->buffer),
+                               sldns_buffer_remaining(c->buffer));
+                       if(err) {
                                log_err("proxy_protocol: could not parse "
-                                       "PROXYv2 header");
+                                       "PROXYv2 header (%s)",
+                                       pp_lookup_error(err));
                                return 0;
                        }
+                       header = (struct pp2_header*)sldns_buffer_begin(c->buffer);
                        want_read_size = ntohs(header->len);
                        if(sldns_buffer_remaining(c->buffer) <
                                PP2_HEADER_SIZE + want_read_size) {
@@ -2067,12 +2079,17 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
                        }
                }
                if(c->pp2_header_state == pp2_header_init) {
-                       header = pp2_read_header(c->buffer);
-                       if(!header) {
+                       int err;
+                       err = pp2_read_header(
+                               sldns_buffer_begin(c->buffer),
+                               sldns_buffer_remaining(c->buffer));
+                       if(err) {
                                log_err("proxy_protocol: could not parse "
-                                       "PROXYv2 header");
+                                       "PROXYv2 header (%s)",
+                                       pp_lookup_error(err));
                                return 0;
                        }
+                       header = (struct pp2_header*)sldns_buffer_begin(c->buffer);
                        want_read_size = ntohs(header->len);
                        if(sldns_buffer_remaining(c->buffer) <
                                PP2_HEADER_SIZE + want_read_size) {
index 757c5141db969887371f459907cb8f856642767b..fd253372f87f1c138fe1a894761e472e0cf1b05c 100644 (file)
  *
  * This file contains PROXY protocol functions.
  */
-#include "config.h"
-#include "util/log.h"
 #include "util/proxy_protocol.h"
 
-int
-pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
+/**
+ * Internal struct initialized with function pointers for writing uint16 and
+ * uint32.
+ */
+struct proxy_protocol_data {
+       void (*write_uint16)(void* buf, uint16_t data);
+       void (*write_uint32)(void* buf, uint32_t data);
+};
+struct proxy_protocol_data pp_data;
+
+/**
+ * Internal lookup table; could be further generic like sldns_lookup_table
+ * for all the future generic stuff.
+ */
+struct proxy_protocol_lookup_table {
+       int id;
+       const char *text;
+};
+
+/**
+ * Internal parsing error text; could be exposed with pp_lookup_error.
+ */
+static struct proxy_protocol_lookup_table pp_parse_errors_data[] = {
+       { PP_PARSE_NOERROR, "no parse error" },
+       { PP_PARSE_SIZE, "not enough space for header" },
+       { PP_PARSE_WRONG_HEADERv2, "could not match PROXYv2 header" },
+       { PP_PARSE_UNKNOWN_CMD, "unknown command" },
+       { PP_PARSE_UNKNOWN_FAM_PROT, "unknown family and protocol" },
+};
+
+void
+pp_init(void (*write_uint16)(void* buf, uint16_t data),
+       void (*write_uint32)(void* buf, uint32_t data)) {
+       pp_data.write_uint16 = write_uint16;
+       pp_data.write_uint32 = write_uint32;
+}
+
+const char*
+pp_lookup_error(enum pp_parse_errors error) {
+       return pp_parse_errors_data[error].text;
+}
+
+size_t
+pp2_write_to_buf(uint8_t* buf, size_t buflen, struct sockaddr_storage* src,
        int stream)
 {
        int af;
+       size_t expected_size;
        if(!src) return 0;
        af = (int)((struct sockaddr_in*)src)->sin_family;
-       if(sldns_buffer_remaining(buf) <
-               PP2_HEADER_SIZE + (af==AF_INET?12:36)) {
+       expected_size = PP2_HEADER_SIZE + (af==AF_INET?12:36);
+       if(buflen < expected_size) {
                return 0;
        }
        /* sig */
-       sldns_buffer_write(buf, PP2_SIG, PP2_SIG_LEN);
+       memcpy(buf, PP2_SIG, PP2_SIG_LEN);
+       buf += PP2_SIG_LEN;
        /* version and command */
-       sldns_buffer_write_u8(buf, (PP2_VERSION << 4) | PP2_CMD_PROXY);
+       *buf = (PP2_VERSION << 4) | PP2_CMD_PROXY;
+       buf++;
        if(af==AF_INET) {
                /* family and protocol */
-               sldns_buffer_write_u8(buf,
-                       (PP2_AF_INET<<4) |
-                       (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+               *buf = (PP2_AF_INET<<4) |
+                       (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+               buf++;
                /* length */
-               sldns_buffer_write_u16(buf, 12);
+               (*pp_data.write_uint16)(buf, 12);
+               buf += 2;
                /* src addr */
-               sldns_buffer_write(buf,
+               memcpy(buf,
                        &((struct sockaddr_in*)src)->sin_addr.s_addr, 4);
+               buf += 4;
                /* dst addr */
-               sldns_buffer_write_u32(buf, 0);
+               (*pp_data.write_uint32)(buf, 0);
+               buf += 4;
                /* src port */
-               sldns_buffer_write(buf,
+               memcpy(buf,
                        &((struct sockaddr_in*)src)->sin_port, 2);
+               buf += 2;
+               /* dst addr */
                /* dst port */
-               sldns_buffer_write_u16(buf, 0);
+               (*pp_data.write_uint16)(buf, 12);
        } else {
                /* family and protocol */
-               sldns_buffer_write_u8(buf,
-                       (PP2_AF_INET6<<4) |
-                       (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM));
+               *buf = (PP2_AF_INET6<<4) |
+                       (stream?PP2_PROT_STREAM:PP2_PROT_DGRAM);
+               buf++;
                /* length */
-               sldns_buffer_write_u16(buf, 36);
+               (*pp_data.write_uint16)(buf, 36);
+               buf += 2;
                /* src addr */
-               sldns_buffer_write(buf,
+               memcpy(buf,
                        &((struct sockaddr_in6*)src)->sin6_addr, 16);
+               buf += 16;
                /* dst addr */
-               sldns_buffer_set_at(buf,
-                       sldns_buffer_position(buf), 0, 16);
-               sldns_buffer_skip(buf, 16);
+               memset(buf, 0, 16);
+               buf += 16;
                /* src port */
-               sldns_buffer_write(buf,
-                       &((struct sockaddr_in6*)src)->sin6_port, 2);
+               memcpy(buf, &((struct sockaddr_in6*)src)->sin6_port, 2);
+               buf += 2;
                /* dst port */
-               sldns_buffer_write_u16(buf, 0);
+               (*pp_data.write_uint16)(buf, 0);
        }
-       return 1;
+       return expected_size;
 }
 
-struct pp2_header*
-pp2_read_header(struct sldns_buffer* buf)
+int
+pp2_read_header(uint8_t* buf, size_t buflen)
 {
        size_t size;
-       struct pp2_header* header = (struct pp2_header*)sldns_buffer_begin(buf);
+       struct pp2_header* header = (struct pp2_header*)buf;
        /* Try to fail all the unsupported cases first. */
-       if(sldns_buffer_remaining(buf) < PP2_HEADER_SIZE) {
-               log_err("proxy_protocol: not enough space for header");
-               return NULL;
+       if(buflen < PP2_HEADER_SIZE) {
+               return PP_PARSE_SIZE;
        }
        /* Check for PROXYv2 header */
        if(memcmp(header, PP2_SIG, PP2_SIG_LEN) != 0 ||
                ((header->ver_cmd & 0xF0)>>4) != PP2_VERSION) {
-               log_err("proxy_protocol: could not match PROXYv2 header");
-               return NULL;
+               return PP_PARSE_WRONG_HEADERv2;
        }
        /* Check the length */
        size = PP2_HEADER_SIZE + ntohs(header->len);
-       if(sldns_buffer_remaining(buf) < size) {
-               log_err("proxy_protocol: not enough space for header");
-               return NULL;
+       if(buflen < size) {
+               return PP_PARSE_SIZE;
        }
        /* Check for supported commands */
        if((header->ver_cmd & 0xF) != PP2_CMD_LOCAL &&
                (header->ver_cmd & 0xF) != PP2_CMD_PROXY) {
-               log_err("proxy_protocol: unsupported command");
-               return NULL;
+               return PP_PARSE_UNKNOWN_CMD;
        }
        /* Check for supported family and protocol */
        if(header->fam_prot != 0x00 /* AF_UNSPEC|UNSPEC */ &&
                header->fam_prot != 0x11 /* AF_INET|STREAM */ &&
                header->fam_prot != 0x12 /* AF_INET|DGRAM */ &&
                header->fam_prot != 0x21 /* AF_INET6|STREAM */ &&
-               header->fam_prot != 0x22 /* AF_INET6|DGRAM */) {
-               log_err("proxy_protocol: unsupported family and protocol");
-               return NULL;
+               header->fam_prot != 0x22 /* AF_INET6|DGRAM */ &&
+               header->fam_prot != 0x31 /* AF_UNIX|STREAM */ &&
+               header->fam_prot != 0x32 /* AF_UNIX|DGRAM */) {
+               return PP_PARSE_UNKNOWN_FAM_PROT;
        }
        /* We have a correct header */
-       return header;
+       return PP_PARSE_NOERROR;
 }
index 13cab9d7438ee23c0874f8cb8d5388265806e202..f4867ad6c09c1fc0b852841c1f9c78c323aaa091 100644 (file)
@@ -42,7 +42,7 @@
 #ifndef PROXY_PROTOCOL_H
 #define PROXY_PROTOCOL_H
 
-#include "sldns/sbuffer.h"
+#include "config.h"
 
 /** PROXYv2 minimum header size */
 #define PP2_HEADER_SIZE 16
@@ -109,23 +109,51 @@ struct pp2_header {
        } addr;
 };
 
+/**
+ * PROXY parse errors.
+ */
+enum pp_parse_errors {
+       PP_PARSE_NOERROR = 0,
+       PP_PARSE_SIZE,
+       PP_PARSE_WRONG_HEADERv2,
+       PP_PARSE_UNKNOWN_CMD,
+       PP_PARSE_UNKNOWN_FAM_PROT,
+};
+
+/**
+ * Initialize the internal proxy structure.
+ * @param write_uint16: pointer to a function that can write uint16.
+ * @param write_uint32: pointer to a function that can write uint32.
+ */
+void pp_init(void (*write_uint16)(void* buf, uint16_t data),
+       void (*write_uint32)(void* buf, uint32_t data));
+
+/**
+ * Lookup the parsing error description.
+ * @param error: parsing error from pp2_read_header.
+ * @return the description.
+ */
+const char* pp_lookup_error(enum pp_parse_errors error);
+
 /**
  * Write a PROXYv2 header at the current position of the buffer.
- * @param buf: the buffer to write to.
+ * @param buf: pointer to the buffer to write data to.
+ * @param buflen: available size on the buffer.
  * @param src: the source address.
  * @param stream: if the protocol is stream or datagram.
  * @return 1 on success, 0 on failure.
  */
-int pp2_write_to_buf(struct sldns_buffer* buf, struct sockaddr_storage* src,
-       int stream);
+size_t pp2_write_to_buf(uint8_t* buf, size_t buflen,
+       struct sockaddr_storage* src, int stream);
 
 /**
  * Read a PROXYv2 header from the current position of the buffer.
  * It does initial validation and returns a pointer to the buffer position on
  * success.
- * @param buf: the buffer to read from.
- * @return the pointer to the buffer position on success, NULL on error.
+ * @param buf: pointer to the buffer data to read from.
+ * @param buflen: available size on the buffer.
+ * @return parsing error, 0 on success.
  */
-struct pp2_header* pp2_read_header(struct sldns_buffer* buf);
+int pp2_read_header(uint8_t* buf, size_t buflen);
 
 #endif /* PROXY_PROTOCOL_H */