--- /dev/null
+/*
+ * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
+ *
+ * SPDX-License-Identifier: MPL-2.0
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at https://mozilla.org/MPL/2.0/.
+ *
+ * See the COPYRIGHT file distributed with this work for additional
+ * information regarding copyright ownership.
+ */
+
+#include <sched.h> /* IWYU pragma: keep */
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define UNIT_TESTING
+#include <cmocka.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/os.h>
+#include <isc/proxy2.h>
+#include <isc/random.h>
+
+#include "proxyheader_test_data.h"
+
+#include <tests/isc.h>
+
+typedef struct dummy_handler_cbarg {
+ isc_proxy2_command_t cmd;
+ int socktype;
+ isc_sockaddr_t src_addr;
+ isc_sockaddr_t dst_addr;
+ size_t no_more_calls;
+ size_t tlvs;
+ size_t tls_subtlvs;
+ uint8_t tls_client_flags;
+ bool client_cert_verified;
+ isc_region_t tlv_data;
+ isc_region_t extra;
+ isc_region_t tls_version;
+ isc_region_t tls_common_name;
+} dummy_handler_cbarg_t;
+
+static bool
+dummy_subtlv_iter_cb(const uint8_t client, const bool client_cert_verified,
+ const isc_proxy2_tlv_subtype_tls_t tls_subtlv_type,
+ const isc_region_t *restrict data, void *cbarg) {
+ dummy_handler_cbarg_t *arg = (dummy_handler_cbarg_t *)cbarg;
+
+ UNUSED(client);
+ UNUSED(client_cert_verified);
+
+ arg->tls_subtlvs++;
+
+ switch (tls_subtlv_type) {
+ case ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION:
+ arg->tls_version = *data;
+ break;
+ case ISC_PROXY2_TLV_SUBTYPE_TLS_CN:
+ arg->tls_common_name = *data;
+ break;
+ default:
+ break;
+ };
+
+ return (true);
+}
+
+static bool
+dummy_tlv_iter_cb(const isc_proxy2_tlv_type_t tlv_type,
+ const isc_region_t *restrict data, void *cbarg) {
+ dummy_handler_cbarg_t *arg = (dummy_handler_cbarg_t *)cbarg;
+
+ if (arg != NULL) {
+ arg->tlvs++;
+ }
+
+ if (tlv_type == ISC_PROXY2_TLV_TYPE_TLS) {
+ isc_result_t result = isc_proxy2_subtlv_tls_header_data(
+ data, &arg->tls_client_flags,
+ &arg->client_cert_verified);
+
+ assert_true(result == ISC_R_SUCCESS);
+
+ result = isc_proxy2_subtlv_tls_iterate(
+ data, dummy_subtlv_iter_cb, cbarg);
+
+ assert_true(result == ISC_R_SUCCESS);
+ }
+ return (true);
+}
+
+static void
+proxy2_handler_dummy(const isc_result_t result, const isc_proxy2_command_t cmd,
+ const int socktype,
+ const isc_sockaddr_t *restrict src_addr,
+ const isc_sockaddr_t *restrict dst_addr,
+ const isc_region_t *restrict tlv_blob,
+ const isc_region_t *restrict extra, void *cbarg) {
+ dummy_handler_cbarg_t *arg = (dummy_handler_cbarg_t *)cbarg;
+
+ UNUSED(extra);
+
+ if (result == ISC_R_NOMORE && arg != NULL) {
+ arg->no_more_calls++;
+ return;
+ } else if (result != ISC_R_SUCCESS) {
+ return;
+ }
+
+ if (cmd == ISC_PROXY2_CMD_PROXY && socktype != 0 /* unspec */) {
+ INSIST(src_addr != NULL);
+ INSIST(dst_addr != NULL);
+ } else if (cmd == ISC_PROXY2_CMD_LOCAL) {
+ INSIST(tlv_blob == NULL);
+ INSIST(src_addr == NULL);
+ INSIST(dst_addr == NULL);
+ }
+
+ if (arg != NULL) {
+ arg->cmd = cmd;
+ arg->socktype = socktype;
+ if (src_addr != NULL) {
+ INSIST(dst_addr != NULL);
+ arg->src_addr = *src_addr;
+ arg->dst_addr = *dst_addr;
+ }
+ }
+
+ if (tlv_blob) {
+ assert_true(isc_proxy2_tlv_data_verify(tlv_blob) ==
+ ISC_R_SUCCESS);
+ if (cbarg != NULL) {
+ isc_proxy2_tlv_iterate(tlv_blob, dummy_tlv_iter_cb,
+ cbarg);
+ }
+ }
+}
+
+static int
+setup_test_proxy(void **state) {
+ isc_proxy2_handler_t **handler = (isc_proxy2_handler_t **)state;
+ *handler = isc_proxy2_handler_new(mctx, 0, proxy2_handler_dummy, NULL);
+ return (0);
+}
+
+static int
+teardown_test_proxy(void **state) {
+ isc_proxy2_handler_free((isc_proxy2_handler_t **)state);
+
+ return (0);
+}
+
+static void
+test_header_data(isc_proxy2_handler_t *handler, const void *data,
+ const size_t size, const bool tear_apart,
+ const bool tear_randomly) {
+ isc_region_t region = { 0 };
+ isc_result_t result;
+
+ if (tear_apart) {
+ isc_buffer_t databuf = { 0 };
+ isc_buffer_init(&databuf, (void *)data, size);
+ isc_buffer_add(&databuf, size);
+
+ for (; isc_buffer_remaininglength(&databuf) > 0;) {
+ isc_region_t remaining = { 0 };
+ size_t sz = 1;
+
+ if (tear_randomly) {
+ sz = 1 + isc_random_uniform(
+ isc_buffer_remaininglength(
+ &databuf));
+ }
+
+ isc_buffer_remainingregion(&databuf, &remaining);
+ remaining.length = sz;
+
+ result = isc_proxy2_handler_push(handler, &remaining);
+ assert_true(isc_proxy2_handler_result(handler) ==
+ result);
+
+ isc_buffer_forward(&databuf, sz);
+ if (result == ISC_R_SUCCESS) {
+ break;
+ }
+ }
+
+ } else {
+ result = isc_proxy2_handler_push_data(handler, data, size);
+ assert_true(isc_proxy2_handler_result(handler) == result);
+ }
+
+ assert_true(isc_proxy2_handler_result(handler) == ISC_R_SUCCESS);
+ isc_proxy2_handler_header(handler, ®ion);
+ assert_true(region.length == size);
+ assert_true(memcmp(region.base, data, region.length) == 0);
+}
+
+static void
+verify_proxy_v2_header(isc_proxy2_handler_t *handler,
+ dummy_handler_cbarg_t *cbarg) {
+ char sabuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
+ isc_sockaddr_t src_addr = { 0 }, dst_addr = { 0 };
+ isc_result_t result;
+ int socktype = -1;
+
+ assert_true(cbarg->cmd == ISC_PROXY2_CMD_PROXY);
+ assert_true(cbarg->socktype == SOCK_STREAM);
+ assert_true(isc_sockaddr_pf(&cbarg->dst_addr) == AF_INET);
+ assert_true(isc_sockaddr_pf(&cbarg->src_addr) == AF_INET);
+
+ isc_sockaddr_format(&cbarg->dst_addr, sabuf, sizeof(sabuf));
+ assert_true(strcmp(sabuf, "127.0.0.66#11883") == 0);
+ isc_sockaddr_format(&cbarg->src_addr, sabuf, sizeof(sabuf));
+ assert_true(strcmp(sabuf, "127.0.0.1#56784") == 0);
+
+ if (handler != NULL) {
+ result = isc_proxy2_handler_addresses(handler, &socktype,
+ &src_addr, &dst_addr);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(isc_sockaddr_equal(&src_addr, &cbarg->src_addr));
+ assert_true(isc_sockaddr_equal(&dst_addr, &cbarg->dst_addr));
+ assert_true(socktype == cbarg->socktype);
+ }
+
+ assert_true(cbarg->tlvs == 0);
+ assert_true(cbarg->tls_subtlvs == 0);
+ assert_true(cbarg->tls_client_flags == 0);
+ assert_true(cbarg->client_cert_verified == false);
+}
+
+static void
+verify_proxy_v2_header_with_TLS(isc_proxy2_handler_t *handler,
+ dummy_handler_cbarg_t *cbarg) {
+ char sabuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
+ isc_sockaddr_t src_addr = { 0 }, dst_addr = { 0 };
+ isc_result_t result;
+ int socktype = -1;
+
+ assert_true(cbarg->cmd == ISC_PROXY2_CMD_PROXY);
+ assert_true(cbarg->socktype == SOCK_STREAM);
+ assert_true(isc_sockaddr_pf(&cbarg->dst_addr) == AF_INET);
+ assert_true(isc_sockaddr_pf(&cbarg->src_addr) == AF_INET);
+
+ isc_sockaddr_format(&cbarg->dst_addr, sabuf, sizeof(sabuf));
+ assert_true(strcmp(sabuf, "127.0.0.67#11883") == 0);
+ isc_sockaddr_format(&cbarg->src_addr, sabuf, sizeof(sabuf));
+ assert_true(strcmp(sabuf, "127.0.0.1#39754") == 0);
+
+ if (handler != NULL) {
+ result = isc_proxy2_handler_addresses(handler, &socktype,
+ &src_addr, &dst_addr);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(isc_sockaddr_equal(&src_addr, &cbarg->src_addr));
+ assert_true(isc_sockaddr_equal(&dst_addr, &cbarg->dst_addr));
+ assert_true(socktype == cbarg->socktype);
+ }
+
+ assert_true(cbarg->tlvs == 1);
+ assert_true(cbarg->tls_subtlvs == 1);
+ assert_true(cbarg->tls_client_flags == ISC_PROXY2_CLIENT_TLS);
+ assert_true(cbarg->client_cert_verified == true);
+
+ /* "TLSv1.2" (w/o trailing '\0') */
+ assert_true(cbarg->tls_version.length == 7);
+ assert_true(memcmp(cbarg->tls_version.base, "TLSv1.2", 7) == 0);
+}
+
+static void
+verify_proxy_v2_header_with_TLS_CN(isc_proxy2_handler_t *handler,
+ dummy_handler_cbarg_t *cbarg) {
+ char sabuf[ISC_SOCKADDR_FORMATSIZE] = { 0 };
+ isc_sockaddr_t src_addr = { 0 }, dst_addr = { 0 };
+ isc_result_t result;
+ int socktype = -1;
+
+ assert_true(cbarg->cmd == ISC_PROXY2_CMD_PROXY);
+ assert_true(cbarg->socktype == SOCK_STREAM);
+ assert_true(isc_sockaddr_pf(&cbarg->dst_addr) == AF_INET);
+ assert_true(isc_sockaddr_pf(&cbarg->src_addr) == AF_INET);
+
+ isc_sockaddr_format(&cbarg->dst_addr, sabuf, sizeof(sabuf));
+ assert_true(strcmp(sabuf, "127.0.0.67#11883") == 0);
+ isc_sockaddr_format(&cbarg->src_addr, sabuf, sizeof(sabuf));
+ assert_true(strcmp(sabuf, "127.0.0.1#40402") == 0);
+
+ if (handler != NULL) {
+ result = isc_proxy2_handler_addresses(handler, &socktype,
+ &src_addr, &dst_addr);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(isc_sockaddr_equal(&src_addr, &cbarg->src_addr));
+ assert_true(isc_sockaddr_equal(&dst_addr, &cbarg->dst_addr));
+ assert_true(socktype == cbarg->socktype);
+ }
+
+ assert_true(cbarg->tlvs == 1);
+ assert_true(cbarg->tls_subtlvs == 2); /* version and common name */
+ assert_true(cbarg->tls_client_flags ==
+ (ISC_PROXY2_CLIENT_TLS | ISC_PROXY2_CLIENT_CERT_SESS |
+ ISC_PROXY2_CLIENT_CERT_CONN));
+ assert_true(cbarg->client_cert_verified == true);
+
+ /* "TLSv1.2" (w/o trailing '\0') */
+ assert_true(cbarg->tls_version.length == 7);
+ assert_true(memcmp(cbarg->tls_version.base, "TLSv1.2", 7) == 0);
+
+ /* "mqttuser1" (w/o trailing '\0') */
+ assert_true(cbarg->tls_common_name.length == 9);
+ assert_true(memcmp(cbarg->tls_common_name.base, "mqttuser1", 9) == 0);
+}
+
+static void
+verify_proxy_v2_header_with_AF_UNIX(isc_proxy2_handler_t *handler,
+ dummy_handler_cbarg_t *cbarg) {
+ assert_true(cbarg->cmd == ISC_PROXY2_CMD_PROXY);
+ assert_true(cbarg->socktype == 0);
+
+ if (handler != NULL) {
+ int socktype = -1;
+ isc_result_t result;
+
+ result = isc_proxy2_handler_addresses(handler, &socktype, NULL,
+ NULL);
+
+ assert_int_equal(result, ISC_R_SUCCESS);
+
+ assert_int_equal(socktype, 0);
+ }
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_generic_test) {
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+ dummy_handler_cbarg_t cbarg = { 0 };
+
+ isc_proxy2_handler_setcb(handler, proxy2_handler_dummy, &cbarg);
+
+ test_header_data(handler, proxy_v2_header, sizeof(proxy_v2_header),
+ false, false);
+ verify_proxy_v2_header(handler, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_TLS,
+ sizeof(proxy_v2_header_with_TLS), false, false);
+ verify_proxy_v2_header_with_TLS(handler, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_TLS_CN,
+ sizeof(proxy_v2_header_with_TLS_CN), false, false);
+ verify_proxy_v2_header_with_TLS_CN(handler, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_AF_UNIX,
+ sizeof(proxy_v2_header_with_AF_UNIX), false, false);
+ verify_proxy_v2_header_with_AF_UNIX(handler, &cbarg);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_generic_byte_by_byte_test) {
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+ dummy_handler_cbarg_t cbarg = { 0 };
+
+ isc_proxy2_handler_setcb(handler, proxy2_handler_dummy, &cbarg);
+
+ test_header_data(handler, proxy_v2_header, sizeof(proxy_v2_header),
+ true, false);
+ verify_proxy_v2_header(handler, &cbarg);
+ assert_true(cbarg.no_more_calls == sizeof(proxy_v2_header) - 1);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_TLS,
+ sizeof(proxy_v2_header_with_TLS), true, false);
+ verify_proxy_v2_header_with_TLS(handler, &cbarg);
+ assert_true(cbarg.no_more_calls ==
+ sizeof(proxy_v2_header_with_TLS) - 1);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_TLS_CN,
+ sizeof(proxy_v2_header_with_TLS_CN), true, false);
+ verify_proxy_v2_header_with_TLS_CN(handler, &cbarg);
+ assert_true(cbarg.no_more_calls ==
+ sizeof(proxy_v2_header_with_TLS_CN) - 1);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_AF_UNIX,
+ sizeof(proxy_v2_header_with_AF_UNIX), true, false);
+ verify_proxy_v2_header_with_AF_UNIX(handler, &cbarg);
+ assert_true(cbarg.no_more_calls ==
+ sizeof(proxy_v2_header_with_AF_UNIX) - 1);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_generic_torn_apart_randomly_test) {
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+ dummy_handler_cbarg_t cbarg = { 0 };
+
+ isc_proxy2_handler_setcb(handler, proxy2_handler_dummy, &cbarg);
+
+ test_header_data(handler, proxy_v2_header, sizeof(proxy_v2_header),
+ true, true);
+ verify_proxy_v2_header(handler, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_TLS,
+ sizeof(proxy_v2_header_with_TLS), true, true);
+ verify_proxy_v2_header_with_TLS(handler, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_TLS_CN,
+ sizeof(proxy_v2_header_with_TLS_CN), true, true);
+ verify_proxy_v2_header_with_TLS_CN(handler, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ test_header_data(handler, (void *)proxy_v2_header_with_AF_UNIX,
+ sizeof(proxy_v2_header_with_AF_UNIX), true, true);
+ verify_proxy_v2_header_with_AF_UNIX(handler, &cbarg);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_direct_test) {
+ isc_result_t result;
+ isc_region_t region = { 0 };
+ dummy_handler_cbarg_t cbarg = { 0 };
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ region.base = (uint8_t *)proxy_v2_header;
+ region.length = sizeof(proxy_v2_header);
+ result = isc_proxy2_header_handle_directly(
+ ®ion, proxy2_handler_dummy, &cbarg);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(cbarg.no_more_calls == 0);
+ verify_proxy_v2_header(NULL, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ region.base = (uint8_t *)proxy_v2_header_with_TLS;
+ region.length = sizeof(proxy_v2_header_with_TLS);
+ result = isc_proxy2_header_handle_directly(
+ ®ion, proxy2_handler_dummy, &cbarg);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(cbarg.no_more_calls == 0);
+ isc_proxy2_tlv_iterate(&cbarg.tlv_data, dummy_tlv_iter_cb, &cbarg);
+ verify_proxy_v2_header_with_TLS(NULL, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ region.base = (uint8_t *)proxy_v2_header_with_TLS_CN;
+ region.length = sizeof(proxy_v2_header_with_TLS_CN);
+ result = isc_proxy2_header_handle_directly(
+ ®ion, proxy2_handler_dummy, &cbarg);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(cbarg.no_more_calls == 0);
+ isc_proxy2_tlv_iterate(&cbarg.tlv_data, dummy_tlv_iter_cb, &cbarg);
+ verify_proxy_v2_header_with_TLS_CN(NULL, &cbarg);
+
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ region.base = (uint8_t *)proxy_v2_header_with_AF_UNIX;
+ region.length = sizeof(proxy_v2_header_with_AF_UNIX);
+ result = isc_proxy2_header_handle_directly(
+ ®ion, proxy2_handler_dummy, &cbarg);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(cbarg.no_more_calls == 0);
+ verify_proxy_v2_header_with_AF_UNIX(NULL, &cbarg);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_detect_bad_signature_test) {
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+
+ for (size_t i = 0; i < ISC_PROXY2_HEADER_SIGNATURE_SIZE; i++) {
+ isc_result_t result;
+ uint8_t sig[ISC_PROXY2_HEADER_SIGNATURE_SIZE];
+ memmove(sig, ISC_PROXY2_HEADER_SIGNATURE,
+ ISC_PROXY2_HEADER_SIGNATURE_SIZE);
+
+ sig[i] = 0x0C; /* it is not present in the valid signature */
+
+ /*
+ * We are expected to detect bad signature as early as possible,
+ * so we are passing only a part of the header.
+ */
+ result = isc_proxy2_handler_push_data(handler, sig, i + 1);
+ assert_true(result == ISC_R_UNEXPECTED);
+ }
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_extra_data_test) {
+ isc_result_t result;
+ isc_buffer_t databuf;
+ isc_region_t region = { 0 };
+ size_t sz;
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+ uint8_t header[] = { 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51,
+ 0x55, 0x49, 0x54, 0x0a, 0x21, 0x11, 0x00, 0x1e,
+ 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x43,
+ 0x9b, 0x4a, 0x2e, 0x6b, 0x20, 0x00, 0x0f, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x07, 0x54,
+ 0x4c, 0x53, 0x76, 0x31, 0x2e, 0x32 };
+ uint8_t extra_data[] = { 0x10, 0x1a, 0x00, 0x04, 0x4d, 0x51, 0x54,
+ 0x54, 0x04, 0x02, 0x00, 0x3c, 0x00, 0x0e,
+ 0x4d, 0x51, 0x54, 0x54, 0x5f, 0x46, 0x58,
+ 0x5f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74 };
+ uint8_t data[sizeof(header) + sizeof(extra_data)];
+
+ isc_buffer_init(&databuf, (void *)data, sizeof(data));
+
+ isc_buffer_putmem(&databuf, header, sizeof(header));
+ isc_buffer_putmem(&databuf, extra_data, sizeof(extra_data));
+
+ isc_buffer_remainingregion(&databuf, ®ion);
+
+ result = isc_proxy2_handler_push(handler, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ region = (isc_region_t){ 0 };
+ sz = isc_proxy2_handler_header(handler, ®ion);
+ assert_true(sz == sizeof(header));
+ assert_true(sz == region.length);
+ assert_true(memcmp(header, region.base, sz) == 0);
+
+ region = (isc_region_t){ 0 };
+ sz = isc_proxy2_handler_extra(handler, ®ion);
+ assert_true(sz == sizeof(extra_data));
+ assert_true(sz == region.length);
+ assert_true(memcmp(extra_data, region.base, sz) == 0);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_max_size_test) {
+ isc_result_t result;
+ isc_proxy2_handler_t handler;
+
+ UNUSED(state);
+
+ isc_proxy2_handler_init(&handler, mctx, sizeof(proxy_v2_header),
+ proxy2_handler_dummy, NULL);
+
+ result = isc_proxy2_handler_push_data(&handler, proxy_v2_header,
+ sizeof(proxy_v2_header));
+
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_proxy2_handler_uninit(&handler);
+
+ isc_proxy2_handler_init(&handler, mctx, sizeof(proxy_v2_header) - 1,
+ proxy2_handler_dummy, NULL);
+
+ result = isc_proxy2_handler_push_data(&handler, proxy_v2_header,
+ sizeof(proxy_v2_header));
+
+ assert_true(result == ISC_R_RANGE);
+
+ isc_proxy2_handler_uninit(&handler);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_make_header_test) {
+ isc_result_t result;
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+ isc_buffer_t databuf;
+ uint8_t data[ISC_PROXY2_MAX_SIZE];
+ isc_buffer_t sslbuf;
+ uint8_t ssldata[ISC_PROXY2_MAX_SIZE];
+ isc_region_t region = { 0 };
+ uint8_t extra[256] = { 0 };
+ const char *tls_version = "TLSv1.3";
+ const char *tls_cn = "name.test";
+ dummy_handler_cbarg_t cbarg = { 0 };
+ struct in_addr localhost4 = { 0 };
+ isc_sockaddr_t src_addrv4 = { 0 }, dst_addrv4 = { 0 },
+ src_addrv6 = { 0 }, dst_addrv6 = { 0 };
+ const uint16_t src_port = 1236;
+ const uint16_t dst_port = 9582;
+
+ localhost4.s_addr = htonl(INADDR_LOOPBACK);
+
+ isc_sockaddr_fromin(&src_addrv4, &localhost4, src_port);
+ isc_sockaddr_fromin(&dst_addrv4, &localhost4, dst_port);
+ isc_sockaddr_fromin6(&src_addrv6, &in6addr_loopback, src_port);
+ isc_sockaddr_fromin6(&dst_addrv6, &in6addr_loopback, dst_port);
+ isc_proxy2_handler_setcb(handler, proxy2_handler_dummy, &cbarg);
+
+ isc_buffer_init(&databuf, (void *)data, sizeof(data));
+ isc_buffer_init(&sslbuf, (void *)ssldata, sizeof(ssldata));
+
+ /* unspec */
+ result = isc_proxy2_make_header(&databuf, ISC_PROXY2_CMD_LOCAL, 0, NULL,
+ NULL, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ assert_true(region.length == ISC_PROXY2_HEADER_SIZE);
+
+ region = (isc_region_t){ .base = extra, .length = sizeof(extra) };
+ result = isc_proxy2_header_append_tlv(
+ &databuf, ISC_PROXY2_TLV_TYPE_NOOP, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ assert_true(region.length == ISC_PROXY2_HEADER_SIZE + sizeof(extra) +
+ ISC_PROXY2_TLV_HEADER_SIZE);
+
+ result = isc_proxy2_handler_push(handler, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(cbarg.tlvs == 0); /* in unspec mode we ignore TLVs */
+
+ /* AF_INET, SOCK_STREAM */
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ isc_buffer_clear(&databuf);
+
+ result = isc_proxy2_make_header(&databuf, ISC_PROXY2_CMD_PROXY,
+ SOCK_STREAM, &src_addrv4, &dst_addrv4,
+ NULL);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ assert_true(region.length == ISC_PROXY2_MIN_AF_INET_SIZE);
+
+ region = (isc_region_t){ .base = extra, .length = sizeof(extra) };
+ result = isc_proxy2_header_append_tlv(
+ &databuf, ISC_PROXY2_TLV_TYPE_NOOP, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ assert_true(region.length == ISC_PROXY2_MIN_AF_INET_SIZE +
+ sizeof(extra) +
+ ISC_PROXY2_TLV_HEADER_SIZE);
+
+ result = isc_proxy2_handler_push(handler, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ assert_true(cbarg.tlvs == 1); /* ISC_PROXY2_TLV_TYPE_NOOP */
+
+ assert_true(cbarg.socktype == SOCK_STREAM);
+ assert_true(isc_sockaddr_pf(&cbarg.src_addr) == AF_INET);
+ assert_true(isc_sockaddr_pf(&cbarg.dst_addr) == AF_INET);
+
+ assert_true(isc_sockaddr_equal(&cbarg.src_addr, &src_addrv4));
+ assert_true(isc_sockaddr_equal(&cbarg.dst_addr, &dst_addrv4));
+
+ /* AF_INET6, SOCK_STREAM (+ TLS version and CN) */
+ cbarg = (dummy_handler_cbarg_t){ 0 };
+ isc_buffer_clear(&databuf);
+
+ result = isc_proxy2_make_header(&databuf, ISC_PROXY2_CMD_PROXY,
+ SOCK_STREAM, &src_addrv6, &dst_addrv6,
+ NULL);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ assert_true(region.length == ISC_PROXY2_MIN_AF_INET6_SIZE);
+
+ region = (isc_region_t){ .base = extra, .length = sizeof(extra) };
+ result = isc_proxy2_header_append_tlv(
+ &databuf, ISC_PROXY2_TLV_TYPE_NOOP, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ result = isc_proxy2_make_tls_subheader(
+ &sslbuf, ISC_PROXY2_CLIENT_TLS | ISC_PROXY2_CLIENT_CERT_CONN,
+ true, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv_string(
+ &sslbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION, tls_version);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv_string(
+ &sslbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_CN, tls_cn);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&sslbuf, ®ion);
+ result = isc_proxy2_header_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ size_t expected = ISC_PROXY2_MIN_AF_INET6_SIZE + sizeof(extra) +
+ (4 * ISC_PROXY2_TLV_HEADER_SIZE) +
+ ISC_PROXY2_TLS_SUBHEADER_MIN_SIZE +
+ strlen(tls_version) + strlen(tls_cn);
+ assert_true(region.length == expected);
+
+ result = isc_proxy2_handler_push(handler, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ assert_true(cbarg.socktype == SOCK_STREAM);
+ assert_true(isc_sockaddr_pf(&cbarg.src_addr) == AF_INET6);
+ assert_true(isc_sockaddr_pf(&cbarg.dst_addr) == AF_INET6);
+
+ assert_true(isc_sockaddr_equal(&cbarg.src_addr, &src_addrv6));
+ assert_true(isc_sockaddr_equal(&cbarg.dst_addr, &dst_addrv6));
+
+ region = (isc_region_t){ 0 };
+ (void)isc_proxy2_handler_tlvs(handler, ®ion);
+ assert_true(isc_proxy2_tlv_data_verify(®ion) == ISC_R_SUCCESS);
+ /* ISC_PROXY2_TLV_TYPE_NOOP+ISC_PROXY2_TLV_TYPE_TLS */
+ assert_true(cbarg.tlvs == 2);
+ /* ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION+ISC_PROXY2_TLV_SUBTYPE_TLS_CN */
+ assert_true(cbarg.tls_subtlvs == 2);
+
+ assert_true(cbarg.tls_version.length == strlen(tls_version));
+ assert_true(memcmp(cbarg.tls_version.base, tls_version,
+ strlen(tls_version)) == 0);
+
+ assert_true(cbarg.tls_common_name.length == strlen(tls_cn));
+ assert_true(memcmp(cbarg.tls_common_name.base, tls_cn,
+ strlen(tls_cn)) == 0);
+}
+
+static bool
+rebuild_subtlv_iter_cb(const uint8_t client, const bool client_cert_verified,
+ const isc_proxy2_tlv_subtype_tls_t tls_subtlv_type,
+ const isc_region_t *restrict data, void *cbarg) {
+ isc_result_t result;
+ isc_buffer_t *outbuf = (isc_buffer_t *)cbarg;
+
+ UNUSED(client);
+ UNUSED(client_cert_verified);
+
+ result = isc_proxy2_append_tlv(outbuf, tls_subtlv_type, data);
+ assert_true(result == ISC_R_SUCCESS);
+
+ return (true);
+}
+
+static bool
+rebuild_tlv_iter_cb(const isc_proxy2_tlv_type_t tlv_type,
+ const isc_region_t *restrict data, void *cbarg) {
+ isc_result_t result;
+ isc_buffer_t *outbuf = (isc_buffer_t *)cbarg;
+
+ if (tlv_type == ISC_PROXY2_TLV_TYPE_TLS) {
+ uint8_t client_flags = 0;
+ bool client_cert_verified = false;
+ isc_buffer_t databuf = { 0 };
+ isc_region_t region = { 0 };
+ uint8_t storage[ISC_PROXY2_MAX_SIZE];
+
+ isc_buffer_init(&databuf, (void *)storage, sizeof(storage));
+
+ /* get flags values */
+ result = isc_proxy2_subtlv_tls_header_data(
+ data, &client_flags, &client_cert_verified);
+ assert_true(result == ISC_R_SUCCESS);
+
+ /* create header */
+ result = isc_proxy2_make_tls_subheader(
+ &databuf, client_flags, client_cert_verified, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+
+ /* process and append values */
+ result = isc_proxy2_subtlv_tls_iterate(
+ data, rebuild_subtlv_iter_cb, &databuf);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_header_append_tlv(outbuf, tlv_type,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ } else {
+ result = isc_proxy2_header_append_tlv(outbuf, tlv_type, data);
+ assert_true(result == ISC_R_SUCCESS);
+ }
+
+ return (true);
+}
+
+static void
+proxy2_handler_rebuild_cb(const isc_result_t header_result,
+ const isc_proxy2_command_t cmd, const int socktype,
+ const isc_sockaddr_t *restrict src_addr,
+ const isc_sockaddr_t *restrict dst_addr,
+ const isc_region_t *restrict tlv_blob,
+ const isc_region_t *restrict extra, void *cbarg) {
+ isc_result_t result;
+ isc_buffer_t *outbuf = (isc_buffer_t *)cbarg;
+
+ if (header_result != ISC_R_SUCCESS) {
+ return;
+ }
+
+ result = isc_proxy2_make_header(outbuf, cmd, socktype, src_addr,
+ dst_addr, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+
+ if (tlv_blob != NULL) {
+ isc_proxy2_tlv_iterate(tlv_blob, rebuild_tlv_iter_cb, outbuf);
+ }
+
+ if (extra != NULL) {
+ result = isc_proxy2_tlv_data_verify(tlv_blob);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_putmem(outbuf, extra->base, extra->length);
+ }
+}
+
+static void
+proxy2_handler_rebuild(isc_buffer_t *restrict outbuf, const void *data,
+ const size_t size) {
+ isc_proxy2_handler_t handler = { 0 };
+
+ isc_proxy2_handler_init(&handler, mctx, 0, proxy2_handler_rebuild_cb,
+ outbuf);
+
+ isc_proxy2_handler_push_data(&handler, data, size);
+
+ isc_proxy2_handler_uninit(&handler);
+}
+
+static void
+try_rebuild_header(const void *data, size_t size) {
+ isc_buffer_t databuf = { 0 };
+ isc_region_t region = { 0 };
+ uint8_t storage[ISC_PROXY2_MAX_SIZE];
+
+ isc_buffer_init(&databuf, (void *)storage, sizeof(storage));
+
+ proxy2_handler_rebuild(&databuf, data, size);
+ isc_buffer_usedregion(&databuf, ®ion);
+ assert_true(region.length == size);
+ assert_true(memcmp(region.base, data, size) == 0);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_rebuild_header_test) {
+ try_rebuild_header(proxy_v2_header, sizeof(proxy_v2_header));
+ try_rebuild_header(proxy_v2_header_with_TLS,
+ sizeof(proxy_v2_header_with_TLS));
+ try_rebuild_header(proxy_v2_header_with_TLS_CN,
+ sizeof(proxy_v2_header_with_TLS_CN));
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_bad_header_signature_test) {
+ size_t i;
+ isc_result_t result;
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+
+ for (i = 0; i < ISC_PROXY2_HEADER_SIGNATURE_SIZE; i++) {
+ uint8_t sig[ISC_PROXY2_HEADER_SIGNATURE_SIZE];
+ memmove(sig, ISC_PROXY2_HEADER_SIGNATURE,
+ ISC_PROXY2_HEADER_SIGNATURE_SIZE);
+ sig[i] = 0x0C; /* 0x0C cannot be found in the signature */
+ result = isc_proxy2_handler_push_data(handler, sig,
+ sizeof(sig));
+ assert_true(result == ISC_R_UNEXPECTED);
+ isc_proxy2_handler_clear(handler);
+ }
+
+ result = isc_proxy2_handler_push_data(handler,
+ ISC_PROXY2_HEADER_SIGNATURE,
+ ISC_PROXY2_HEADER_SIGNATURE_SIZE);
+ assert_true(result == ISC_R_NOMORE);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_bad_proto_version_command_test) {
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+ isc_result_t result;
+ uint8_t *pver_cmd = NULL;
+ uint8_t botched_header[sizeof(proxy_v2_header)] = { 0 };
+
+ memmove(botched_header, proxy_v2_header, sizeof(proxy_v2_header));
+
+ pver_cmd = &botched_header[ISC_PROXY2_HEADER_SIGNATURE_SIZE];
+
+ assert_true(*pver_cmd == 0x21);
+
+ *pver_cmd = 0x31; /* unexpected version (3) followed by PROXY command */
+
+ result = isc_proxy2_handler_push_data(handler, botched_header,
+ sizeof(botched_header));
+ assert_true(result == ISC_R_NOTIMPLEMENTED);
+
+ *pver_cmd = 0x22; /* version two followed by unexpected command (2) */
+
+ result = isc_proxy2_handler_push_data(handler, botched_header,
+ sizeof(botched_header));
+ assert_true(result == ISC_R_UNEXPECTED);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_bad_family_socktype_test) {
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+ isc_result_t result;
+ uint8_t *pfam = NULL;
+ uint8_t botched_header[sizeof(proxy_v2_header)] = { 0 };
+
+ memmove(botched_header, proxy_v2_header, sizeof(proxy_v2_header));
+
+ pfam = &botched_header[ISC_PROXY2_HEADER_SIGNATURE_SIZE + 1];
+
+ assert_true(*pfam == 0x11);
+
+ *pfam = 0x41; /* unexpected family (4) followed by SOCK_STREAM (1)*/
+
+ result = isc_proxy2_handler_push_data(handler, botched_header,
+ sizeof(botched_header));
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ *pfam = 0x13; /* AF_INET (1) followed by unexpected sock type (3) */
+
+ result = isc_proxy2_handler_push_data(handler, botched_header,
+ sizeof(botched_header));
+ assert_true(result == ISC_R_UNEXPECTED);
+}
+
+static inline void
+update_header_length(uint8_t *botched_header, uint16_t newlen) {
+ newlen = htons(newlen);
+ memmove(&botched_header[ISC_PROXY2_HEADER_SIGNATURE_SIZE + 2], &newlen,
+ sizeof(newlen));
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_bad_unexpected_not_enough_length_test) {
+ isc_proxy2_handler_t *handler = (isc_proxy2_handler_t *)*state;
+ isc_result_t result;
+ uint8_t botched_header[sizeof(proxy_v2_header)] = { 0 };
+
+ memmove(botched_header, proxy_v2_header, sizeof(proxy_v2_header));
+
+ update_header_length(botched_header, 0);
+ result = isc_proxy2_handler_push_data(handler, botched_header,
+ sizeof(botched_header));
+ assert_true(result == ISC_R_RANGE);
+
+ update_header_length(botched_header, 4); /* not enough */
+ result = isc_proxy2_handler_push_data(handler, botched_header,
+ sizeof(botched_header));
+ assert_true(result == ISC_R_RANGE);
+
+ update_header_length(botched_header, UINT16_MAX); /* no more */
+ result = isc_proxy2_handler_push_data(handler, botched_header,
+ sizeof(botched_header));
+ assert_true(result == ISC_R_NOMORE);
+ isc_proxy2_handler_clear(handler);
+}
+
+ISC_RUN_TEST_IMPL(proxyheader_tlv_data_test) {
+ isc_result_t result;
+ isc_buffer_t databuf = { 0 };
+ isc_buffer_t tlsbuf = { 0 };
+ uint8_t data[ISC_PROXY2_MAX_SIZE] = { 0 };
+ uint8_t tlsdata[ISC_PROXY2_MAX_SIZE] = { 0 };
+ uint8_t zerodata[0xff] = { 0 };
+ isc_region_t region = { 0 };
+ const char *alpn = "dot";
+ const char *tls_version = "TLSv1.3";
+ const char *tls_cn = "name.test";
+
+ isc_buffer_init(&databuf, (void *)data, sizeof(data));
+ isc_buffer_init(&tlsbuf, (void *)tlsdata, sizeof(tlsdata));
+
+ /* zero filled data is not fine */
+ region.base = zerodata;
+ region.length = sizeof(zerodata);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ /* crc32c must be 4 bytes long */
+ isc_buffer_clear(&databuf);
+ region.base = (uint8_t *)zerodata;
+ region.length = sizeof(zerodata);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_CRC32C,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_RANGE);
+
+ isc_buffer_clear(&databuf);
+ region.base = (uint8_t *)zerodata;
+ region.length = 4;
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_CRC32C,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ /* unique id must be <= 128 bytes long */
+ isc_buffer_clear(&databuf);
+ region.base = (uint8_t *)zerodata;
+ region.length = sizeof(zerodata);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_UNIQUE_ID,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_RANGE);
+
+ isc_buffer_clear(&databuf);
+ region.base = (uint8_t *)zerodata;
+ region.length = 128;
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_UNIQUE_ID,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ /* two noops is fine */
+ isc_buffer_clear(&databuf);
+ region = (isc_region_t){ 0 };
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_NOOP,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_NOOP,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ /* one ALPN tag is fine */
+ isc_buffer_clear(&databuf);
+ result = isc_proxy2_append_tlv_string(&databuf,
+ ISC_PROXY2_TLV_TYPE_ALPN, alpn);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ /* two ALPN tags is not fine */
+ result = isc_proxy2_append_tlv_string(&databuf,
+ ISC_PROXY2_TLV_TYPE_ALPN, alpn);
+ assert_true(result == ISC_R_SUCCESS);
+
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ /* empty TLS subheader is tolerable */
+ isc_buffer_clear(&databuf);
+ isc_buffer_clear(&tlsbuf);
+ result = isc_proxy2_make_tls_subheader(&tlsbuf, 0, false, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ /* empty TLS subheader with no TLS version while one is expected */
+ isc_buffer_clear(&databuf);
+ isc_buffer_clear(&tlsbuf);
+ result = isc_proxy2_make_tls_subheader(&tlsbuf, ISC_PROXY2_CLIENT_TLS,
+ false, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ /* TLS subheader with TLS version */
+ isc_buffer_clear(&databuf);
+ isc_buffer_clear(&tlsbuf);
+ result = isc_proxy2_make_tls_subheader(&tlsbuf, ISC_PROXY2_CLIENT_TLS,
+ false, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ region.length = sizeof(tls_version);
+ result = isc_proxy2_append_tlv_string(
+ &tlsbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION, tls_version);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_SUCCESS);
+
+ /* TLS subheader with multiple TLS versions is not fine */
+ isc_buffer_clear(&databuf);
+ isc_buffer_clear(&tlsbuf);
+ result = isc_proxy2_make_tls_subheader(&tlsbuf, ISC_PROXY2_CLIENT_TLS,
+ false, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv_string(
+ &tlsbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION, tls_version);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv(
+ &tlsbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ /* TLS subheader with unexpected TLS version */
+ isc_buffer_clear(&databuf);
+ isc_buffer_clear(&tlsbuf);
+ result = isc_proxy2_make_tls_subheader(&tlsbuf, 0, false, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv_string(
+ &tlsbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION, tls_version);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ /* TLS subheader with no CN while expected */
+ isc_buffer_clear(&databuf);
+ isc_buffer_clear(&tlsbuf);
+ result = isc_proxy2_make_tls_subheader(
+ &tlsbuf, ISC_PROXY2_CLIENT_TLS | ISC_PROXY2_CLIENT_CERT_CONN,
+ false, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv_string(
+ &tlsbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION, tls_version);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ /* TLS subheader with unexpected CN */
+ isc_buffer_clear(&databuf);
+ isc_buffer_clear(&tlsbuf);
+ result = isc_proxy2_make_tls_subheader(&tlsbuf, ISC_PROXY2_CLIENT_TLS,
+ false, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv_string(
+ &tlsbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_CN, tls_cn);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ /* TLS subheader with CN unexpected (because TLS flag is not set) */
+ isc_buffer_clear(&databuf);
+ isc_buffer_clear(&tlsbuf);
+ result = isc_proxy2_make_tls_subheader(
+ &tlsbuf,
+ ISC_PROXY2_CLIENT_CERT_CONN | ISC_PROXY2_CLIENT_CERT_SESS,
+ false, NULL);
+ assert_true(result == ISC_R_SUCCESS);
+ result = isc_proxy2_append_tlv_string(
+ &tlsbuf, ISC_PROXY2_TLV_SUBTYPE_TLS_CN, tls_cn);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&tlsbuf, ®ion);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_TLS,
+ ®ion);
+ assert_true(result == ISC_R_SUCCESS);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_UNEXPECTED);
+
+ /* botched TLV header */
+ isc_buffer_clear(&databuf);
+ region.base = (uint8_t *)zerodata;
+ region.length = sizeof(zerodata);
+ result = isc_proxy2_append_tlv(&databuf, ISC_PROXY2_TLV_TYPE_NOOP,
+ ®ion);
+ isc_buffer_subtract(&databuf, region.length / 2);
+ isc_buffer_usedregion(&databuf, ®ion);
+ result = isc_proxy2_tlv_data_verify(®ion);
+ assert_true(result == ISC_R_RANGE);
+}
+
+ISC_TEST_LIST_START
+ISC_TEST_ENTRY_CUSTOM(proxyheader_generic_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_generic_byte_by_byte_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_generic_torn_apart_randomly_test,
+ setup_test_proxy, teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_direct_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_detect_bad_signature_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_extra_data_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_max_size_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_make_header_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_rebuild_header_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_bad_header_signature_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_bad_proto_version_command_test,
+ setup_test_proxy, teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_bad_family_socktype_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_bad_unexpected_not_enough_length_test,
+ setup_test_proxy, teardown_test_proxy)
+ISC_TEST_ENTRY_CUSTOM(proxyheader_tlv_data_test, setup_test_proxy,
+ teardown_test_proxy)
+ISC_TEST_LIST_END
+
+ISC_TEST_MAIN