diff --git a/.gitignore b/.gitignore index 9c69b16cb..63a410c55 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ src/applications/bmqbrkr/etc/etc # 'sim_cpp11_features.pl' backups *.bak +/venv + diff --git a/CMakePresets.json b/CMakePresets.json index 6c26706ba..389fae03c 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -17,18 +17,42 @@ }, { "name": "macos-arm64-vcpkg", - "description": "VCPKG based configuration for building on arm-based MacOS", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "description": + "VCPKG based configuration for building on arm-based MacOS", + "toolchainFile": + "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "inherits": "base", "cacheVariables": { "VCPKG_INSTALL_OPTIONS": "--allow-unsupported", "FLEX_INCLUDE_DIR": "/opt/homebrew/opt/flex/include" } }, + { + "name": "macos-arm64-darwin", + "description": + "build-darwin.sh based configuration for building on arm-based MacOS", + "toolchainFile": + "${sourceDir}/thirdparty/bde-tools/BdeBuildSystem/toolchains/darwin/clang-default.cmake", + "inherits": "base", + "environment": { + "BREW_PKG_CONFIG_PATH": + "/opt/homebrew/lib/pkgconfig:/opt/homebrew/opt/zlib/lib/pkgconfig:/opt/homebrew/opt/googletest/lib/pkgconfig", + "PKG_CONFIG_PATH": + "${sourceDir}/install/lib/pkgconfig:$env{BREW_PKG_CONFIG_PATH}" + }, + "cacheVariables": { + "FLEX_ROOT": "/opt/homebrew/opt/flex", + "CMAKE_INSTALL_PREFIX": "${sourceDir}/install", + "CMAKE_PREFIX_PATH": + "${sourceDir}/install;${sourceDir}/thirdparty/bde-tools/BdeBuildSystem" + } + }, { "name": "macos-x64-vcpkg", - "description": "VCPKG based configuration for building on x86_64-based MacOS", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "description": + "VCPKG based configuration for building on x86_64-based MacOS", + "toolchainFile": + "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "inherits": "base", "cacheVariables": { "VCPKG_INSTALL_OPTIONS": "--allow-unsupported", @@ -37,8 +61,10 @@ }, { "name": "linux-x64-vcpkg", - "description": "VCPKG based configuration for building on x86_64-based Linux", - "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "description": + "VCPKG based configuration for building on x86_64-based Linux", + "toolchainFile": + "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", "inherits": "base" } ] diff --git a/bin/build-darwin.sh b/bin/build-darwin.sh index a088e4d5f..1c34519fb 100755 --- a/bin/build-darwin.sh +++ b/bin/build-darwin.sh @@ -33,19 +33,28 @@ mkdir -p "${DIR_THIRDPARTY}" DIR_BUILD="${DIR_BUILD:-${DIR_ROOT}/build}" mkdir -p "${DIR_BUILD}" -DIR_INSTALL="${DIR_INSTALL:-${DIR_ROOT}}" +DIR_INSTALL="${DIR_INSTALL:-${DIR_ROOT}/install}" mkdir -p "${DIR_INSTALL}" # :: Clone dependencies ::::::::::::::::::::::::::::::::::::::::::::::::::::::: if [ ! -d "${DIR_THIRDPARTY}/bde-tools" ]; then - git clone --depth 1 --branch 4.8.0.0 https://github.com/bloomberg/bde-tools "${DIR_THIRDPARTY}/bde-tools" + git clone --depth 1 https://github.com/bloomberg/bde-tools "${DIR_THIRDPARTY}/bde-tools" + pushd "${DIR_THIRDPARTY}/bde-tools" + git reset --hard 964f78d36577ef1643b9074412608df4850e5b33 # 4.17.0.0 + popd fi if [ ! -d "${DIR_THIRDPARTY}/bde" ]; then - git clone --depth 1 --branch 4.8.0.0 https://github.com/bloomberg/bde.git "${DIR_THIRDPARTY}/bde" + git clone --depth 1 https://github.com/bloomberg/bde.git "${DIR_THIRDPARTY}/bde" + pushd "${DIR_THIRDPARTY}/bde" + git reset --hard 4.18.0.0 # 4.18.0.0 + popd fi if [ ! -d "${DIR_THIRDPARTY}/ntf-core" ]; then - git clone --depth 1 --branch 2.4.2 https://github.com/bloomberg/ntf-core.git "${DIR_THIRDPARTY}/ntf-core" + git clone --depth 1 https://github.com/bloomberg/ntf-core.git "${DIR_THIRDPARTY}/ntf-core" + pushd "${DIR_THIRDPARTY}/ntf-core" + git reset --hard 2.5.4 + popd fi diff --git a/bin/build-ubuntu.sh b/bin/build-ubuntu.sh index 35933b3a2..80c1765e3 100755 --- a/bin/build-ubuntu.sh +++ b/bin/build-ubuntu.sh @@ -75,19 +75,28 @@ mkdir -p "${DIR_THIRDPARTY}" DIR_BUILD="${DIR_BUILD:-${DIR_ROOT}/build}" mkdir -p "${DIR_BUILD}" -DIR_INSTALL="${DIR_INSTALL:-${DIR_ROOT}}" +DIR_INSTALL="${DIR_INSTALL:-${DIR_ROOT}/install}" mkdir -p "${DIR_INSTALL}" # :: Clone dependencies ::::::::::::::::::::::::::::::::::::::::::::::::::::::: if [ ! -d "${DIR_THIRDPARTY}/bde-tools" ]; then - git clone --depth 1 --branch 4.8.0.0 https://github.com/bloomberg/bde-tools "${DIR_THIRDPARTY}/bde-tools" + git clone --depth 1 https://github.com/bloomberg/bde-tools "${DIR_THIRDPARTY}/bde-tools" + pushd "${DIR_THIRDPARTY}/bde-tools" + git reset --hard 964f78d36577ef1643b9074412608df4850e5b33 # 4.17.0.0 + popd fi if [ ! -d "${DIR_THIRDPARTY}/bde" ]; then - git clone --depth 1 --branch 4.8.0.0 https://github.com/bloomberg/bde.git "${DIR_THIRDPARTY}/bde" + git clone --depth 1 https://github.com/bloomberg/bde.git "${DIR_THIRDPARTY}/bde" + pushd "${DIR_THIRDPARTY}/bde" + git reset --hard ec094b4454738c311482fc54c3bde1d21c9f6893 # 4.18.0.0 + popd fi if [ ! -d "${DIR_THIRDPARTY}/ntf-core" ]; then - git clone --depth 1 --branch 2.4.2 https://github.com/bloomberg/ntf-core.git "${DIR_THIRDPARTY}/ntf-core" + git clone --depth 1 https://github.com/bloomberg/ntf-core.git "${DIR_THIRDPARTY}/ntf-core" + pushd "${DIR_THIRDPARTY}/ntf-core" + git reset --hard 2.5.4 + popd fi # prometheus-cpp and its dependency for the plugin if [ "${BUILD_PROMETHEUS}" == true ]; then diff --git a/bin/gen-tls-certs.sh b/bin/gen-tls-certs.sh new file mode 100755 index 000000000..f35fd44ee --- /dev/null +++ b/bin/gen-tls-certs.sh @@ -0,0 +1,127 @@ +#!/bin/bash +# +# +# This scripts generates: +# - root CA certificate +# - server certificate and keystore +# - client keys +# +# Based off of +# https://github.com/confluentinc/librdkafka/blob/master/tests/gen-ssl-certs.sh + +OP="$1" +CA_CERT="$2" +PFX="$3" +HOST="$4" + +C=NN +ST=NN +L=NN +O=NN +OU=NN +CN="$HOST" + + +# Password +PASS="secret" + +# Cert validity, in days +VALIDITY=10000 + +set -e + +export LC_ALL=C + +if [[ $OP == "ca" && -n "$CA_CERT" && -n "$3" ]]; then + CN="$3" + openssl req -new -x509 -keyout "${CA_CERT}.key" -out "${CA_CERT}" -days $VALIDITY -passin "pass:$PASS" -passout "pass:$PASS" < "${HOST_CERT_CONFIG_PATH}" +[req] +default_bits = 2048 +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no +[req_distinguished_name] +C = ${C} +ST = ${ST} +L = ${L} +O = ${O} +CN = ${CN} +[v3_req] +subjectAltName = @alt_names +[alt_names] +DNS.1 = ${CN} +DNS.2 = localhost. +EOF + + #Step 1 + echo "############ Generating key" + openssl genrsa -out "${HOST_PRIVATE_RSA_KEY_PATH}" 2048 + openssl pkcs8 -nocrypt -topk8 -v1 PBE-SHA1-RC4-128 -inform pem -outform pem -in "${HOST_PRIVATE_RSA_KEY_PATH}" -out "${HOST_PRIVATE_KEY_PATH}" + + #Step 2 + echo "############ Generate the CSR" + openssl req -nodes -new -extensions v3_req -sha256 -config "${HOST_CERT_CONFIG_PATH}" -key "${HOST_PRIVATE_KEY_PATH}" -out "${HOST_CSR_PATH}" + + #Step 3 + echo "############ Generate the cert" + openssl x509 -req -in "${HOST_CSR_PATH}" -CA "${CA_CERT}" -CAkey "${CA_CERT}.key" -CAcreateserial -out "${HOST_CERT_PATH}" -days ${VALIDITY} -sha256 -extensions v3_req -extfile "${HOST_CERT_CONFIG_PATH}" -passin "pass:${PASS}" + + cat "${HOST_CERT_PATH}" > "${HOST_CERT_CHAIN_PATH}" + + +elif [[ $OP == "client" && -n "$CA_CERT" && -n "$PFX" && -n "$CN" ]]; then + +# Standard OpenSSL keys + echo "############ Generating key" + openssl genrsa -nodes -passout "pass:${PASS}" -out "${PFX}client.key" 2048 + + echo "############ Generating request" + openssl req -passin "pass:${PASS}" -passout "pass:${PASS}" -key "${PFX}client.key" -new -out "${PFX}client.req" \ + < " + echo " $0 server|client " + echo "" + exit 1 +fi + diff --git a/docker/build_deps.sh b/docker/build_deps.sh index 8aa980d42..6aa05dd28 100755 --- a/docker/build_deps.sh +++ b/docker/build_deps.sh @@ -28,9 +28,9 @@ fetch_git() { } fetch_deps() { - fetch_git bloomberg bde-tools 4.8.0.0 - fetch_git bloomberg bde 4.8.0.0 - fetch_git bloomberg ntf-core 2.4.2 + fetch_git bloomberg bde-tools 4.13.0.0 + fetch_git bloomberg bde 4.18.0.0 + fetch_git bloomberg ntf-core 2.5.4 } configure() { diff --git a/docs/docs/features/tls.md b/docs/docs/features/tls.md new file mode 100644 index 000000000..8c4fea4e3 --- /dev/null +++ b/docs/docs/features/tls.md @@ -0,0 +1,12 @@ +# TLS in BlazingMQ + +BlazingMQ supports authenticating brokers using TLS. + + +## Generating Test Certs + +```sh +mkdir -p certs && cd certs +../bin/gen-tls-certs.sh ca ca-cert blazingmq +../bin/gen-tls-certs.sh server ca-cert broker_bmqc00_ bmqc00 +``` diff --git a/src/applications/bmqbrkr/etc/bmqbrkrcfg.json b/src/applications/bmqbrkr/etc/bmqbrkrcfg.json index 9741a62f8..a2a939a49 100644 --- a/src/applications/bmqbrkr/etc/bmqbrkrcfg.json +++ b/src/applications/bmqbrkr/etc/bmqbrkrcfg.json @@ -8,8 +8,8 @@ "rotationBytes": 268435456, "logfileFormat": "%d (%t) %s %F:%l %m\n\n", "consoleFormat": "%d (%t) %s %F:%l %m\n", - "loggingVerbosity": "INFO", - "consoleSeverityThreshold": "INFO", + "loggingVerbosity": "TRACE", + "consoleSeverityThreshold": "TRACE", "categories": [ "BMQBRKR:INFO:green", "BMQ*:INFO:green", @@ -88,11 +88,29 @@ "highWatermark": 1073741824, "nodeLowWatermark": 5242880, "nodeHighWatermark": 10485760, - "heartbeatIntervalMs": 3000 + "heartbeatIntervalMs": 3000, + "listeners": [ + { + "name": "TCPListener", + "port": 30114, + "tls": false + }, + { + "name": "TLSListener", + "port": 30115, + "tls": true + } + ] } }, "bmqconfConfig": { "cacheTTLSeconds": 30 + }, + "tlsConfig": { + "certificateAuthority": "/blazingmq/certs/ca-cert", + "certificate": "/blazingmq/certs/broker_host_cert.pem", + "key": "/blazingmq/certs/broker_private_key.pem", + "version": "" } } } diff --git a/src/applications/bmqtool/bmqtool.m.cpp b/src/applications/bmqtool/bmqtool.m.cpp index 122df768a..7ae123a5f 100644 --- a/src/applications/bmqtool/bmqtool.m.cpp +++ b/src/applications/bmqtool/bmqtool.m.cpp @@ -168,6 +168,18 @@ static bool parseArgs(Parameters* parameters, int argc, const char* argv[]) "address and port of the broker", balcl::TypeInfo(¶ms.broker()), balcl::OccurrenceInfo(params.broker())}, + {"tls-authority", + "tlsAuthority", + "Path to the certificate authority for TLS mode." + "The empty string value means that TLS is disabled, " + "non-empty string value means that TLS is enabled", + balcl::TypeInfo(¶ms.tlsAuthority()), + balcl::OccurrenceInfo(params.tlsAuthority())}, + {"tls-versions", + "tlsVersions", + "TLS protocol versions, has effect only in TLS mode", + balcl::TypeInfo(¶ms.tlsVersions()), + balcl::OccurrenceInfo(params.tlsVersions())}, {"q|queueuri", "uri", "URI of the queue (for auto/syschk modes)", diff --git a/src/applications/bmqtool/bmqtoolcmd.xsd b/src/applications/bmqtool/bmqtoolcmd.xsd index 0b43bad6a..2521f8cdc 100644 --- a/src/applications/bmqtool/bmqtoolcmd.xsd +++ b/src/applications/bmqtool/bmqtoolcmd.xsd @@ -223,6 +223,8 @@ + + diff --git a/src/applications/bmqtool/m_bmqtool_application.cpp b/src/applications/bmqtool/m_bmqtool_application.cpp index d7094212a..9621425c3 100644 --- a/src/applications/bmqtool/m_bmqtool_application.cpp +++ b/src/applications/bmqtool/m_bmqtool_application.cpp @@ -556,6 +556,11 @@ int Application::initialize() .setNumProcessingThreads(d_parameters_p->numProcessingThreads()) .configureEventQueue(1000, 10 * 1000); + if (!d_parameters_p->certificateAuthority().empty()) { + options.setTlsDetails(d_parameters_p->certificateAuthority(), + "TLSv1.3"); + } + // Create the session if (d_parameters_p->noSessionEventHandler()) { d_session_mp.load(new (*d_allocator_p) diff --git a/src/applications/bmqtool/m_bmqtool_messages.cpp b/src/applications/bmqtool/m_bmqtool_messages.cpp index 0b7eeac9b..ef4936447 100644 --- a/src/applications/bmqtool/m_bmqtool_messages.cpp +++ b/src/applications/bmqtool/m_bmqtool_messages.cpp @@ -4305,6 +4305,11 @@ const char CommandLineParameters::DEFAULT_INITIALIZER_MODE[] = "cli"; const char CommandLineParameters::DEFAULT_INITIALIZER_BROKER[] = "tcp://localhost:30114"; +const char CommandLineParameters::DEFAULT_INITIALIZER_TLS_AUTHORITY[] = ""; + +const char CommandLineParameters::DEFAULT_INITIALIZER_TLS_VERSIONS[] = + "TLSv1.3"; + const char CommandLineParameters::DEFAULT_INITIALIZER_QUEUE_URI[] = ""; const char CommandLineParameters::DEFAULT_INITIALIZER_QUEUE_FLAGS[] = ""; @@ -4365,6 +4370,16 @@ const bdlat_AttributeInfo CommandLineParameters::ATTRIBUTE_INFO_ARRAY[] = { sizeof("broker") - 1, "", bdlat_FormattingMode::e_TEXT}, + {ATTRIBUTE_ID_TLS_AUTHORITY, + "tlsAuthority", + sizeof("tlsAuthority") - 1, + "", + bdlat_FormattingMode::e_TEXT}, + {ATTRIBUTE_ID_TLS_VERSIONS, + "tlsVersions", + sizeof("tlsVersions") - 1, + "", + bdlat_FormattingMode::e_TEXT}, {ATTRIBUTE_ID_QUEUE_URI, "queueUri", sizeof("queueUri") - 1, @@ -4486,7 +4501,7 @@ const bdlat_AttributeInfo CommandLineParameters::ATTRIBUTE_INFO_ARRAY[] = { const bdlat_AttributeInfo* CommandLineParameters::lookupAttributeInfo(const char* name, int nameLength) { - for (int i = 0; i < 25; ++i) { + for (int i = 0; i < 27; ++i) { const bdlat_AttributeInfo& attributeInfo = CommandLineParameters::ATTRIBUTE_INFO_ARRAY[i]; @@ -4505,6 +4520,10 @@ const bdlat_AttributeInfo* CommandLineParameters::lookupAttributeInfo(int id) case ATTRIBUTE_ID_MODE: return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_MODE]; case ATTRIBUTE_ID_BROKER: return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_BROKER]; + case ATTRIBUTE_ID_TLS_AUTHORITY: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_AUTHORITY]; + case ATTRIBUTE_ID_TLS_VERSIONS: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_VERSIONS]; case ATTRIBUTE_ID_QUEUE_URI: return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_QUEUE_URI]; case ATTRIBUTE_ID_QUEUE_FLAGS: @@ -4563,6 +4582,8 @@ CommandLineParameters::CommandLineParameters(bslma::Allocator* basicAllocator) , d_messageProperties(basicAllocator) , d_mode(DEFAULT_INITIALIZER_MODE, basicAllocator) , d_broker(DEFAULT_INITIALIZER_BROKER, basicAllocator) +, d_tlsAuthority(DEFAULT_INITIALIZER_TLS_AUTHORITY, basicAllocator) +, d_tlsVersions(DEFAULT_INITIALIZER_TLS_VERSIONS, basicAllocator) , d_queueUri(DEFAULT_INITIALIZER_QUEUE_URI, basicAllocator) , d_queueFlags(DEFAULT_INITIALIZER_QUEUE_FLAGS, basicAllocator) , d_latency(DEFAULT_INITIALIZER_LATENCY, basicAllocator) @@ -4595,6 +4616,8 @@ CommandLineParameters::CommandLineParameters( , d_messageProperties(original.d_messageProperties, basicAllocator) , d_mode(original.d_mode, basicAllocator) , d_broker(original.d_broker, basicAllocator) +, d_tlsAuthority(original.d_tlsAuthority, basicAllocator) +, d_tlsVersions(original.d_tlsVersions, basicAllocator) , d_queueUri(original.d_queueUri, basicAllocator) , d_queueFlags(original.d_queueFlags, basicAllocator) , d_latency(original.d_latency, basicAllocator) @@ -4628,6 +4651,8 @@ CommandLineParameters::CommandLineParameters( d_messageProperties(bsl::move(original.d_messageProperties)), d_mode(bsl::move(original.d_mode)), d_broker(bsl::move(original.d_broker)), + d_tlsAuthority(bsl::move(original.d_tlsAuthority)), + d_tlsVersions(bsl::move(original.d_tlsVersions)), d_queueUri(bsl::move(original.d_queueUri)), d_queueFlags(bsl::move(original.d_queueFlags)), d_latency(bsl::move(original.d_latency)), @@ -4658,6 +4683,8 @@ CommandLineParameters::CommandLineParameters(CommandLineParameters&& original, , d_messageProperties(bsl::move(original.d_messageProperties), basicAllocator) , d_mode(bsl::move(original.d_mode), basicAllocator) , d_broker(bsl::move(original.d_broker), basicAllocator) +, d_tlsAuthority(bsl::move(original.d_tlsAuthority), basicAllocator) +, d_tlsVersions(bsl::move(original.d_tlsVersions), basicAllocator) , d_queueUri(bsl::move(original.d_queueUri), basicAllocator) , d_queueFlags(bsl::move(original.d_queueFlags), basicAllocator) , d_latency(bsl::move(original.d_latency), basicAllocator) @@ -4695,6 +4722,8 @@ CommandLineParameters::operator=(const CommandLineParameters& rhs) if (this != &rhs) { d_mode = rhs.d_mode; d_broker = rhs.d_broker; + d_tlsAuthority = rhs.d_tlsAuthority; + d_tlsVersions = rhs.d_tlsVersions; d_queueUri = rhs.d_queueUri; d_queueFlags = rhs.d_queueFlags; d_latency = rhs.d_latency; @@ -4731,6 +4760,8 @@ CommandLineParameters::operator=(CommandLineParameters&& rhs) if (this != &rhs) { d_mode = bsl::move(rhs.d_mode); d_broker = bsl::move(rhs.d_broker); + d_tlsAuthority = bsl::move(rhs.d_tlsAuthority); + d_tlsVersions = bsl::move(rhs.d_tlsVersions); d_queueUri = bsl::move(rhs.d_queueUri); d_queueFlags = bsl::move(rhs.d_queueFlags); d_latency = bsl::move(rhs.d_latency); @@ -4764,6 +4795,8 @@ void CommandLineParameters::reset() { d_mode = DEFAULT_INITIALIZER_MODE; d_broker = DEFAULT_INITIALIZER_BROKER; + d_tlsAuthority = DEFAULT_INITIALIZER_TLS_AUTHORITY; + d_tlsVersions = DEFAULT_INITIALIZER_TLS_VERSIONS; d_queueUri = DEFAULT_INITIALIZER_QUEUE_URI; d_queueFlags = DEFAULT_INITIALIZER_QUEUE_FLAGS; d_latency = DEFAULT_INITIALIZER_LATENCY; @@ -4800,6 +4833,8 @@ bsl::ostream& CommandLineParameters::print(bsl::ostream& stream, printer.start(); printer.printAttribute("mode", this->mode()); printer.printAttribute("broker", this->broker()); + printer.printAttribute("tlsAuthority", this->tlsAuthority()); + printer.printAttribute("tlsVersions", this->tlsVersions()); printer.printAttribute("queueUri", this->queueUri()); printer.printAttribute("queueFlags", this->queueFlags()); printer.printAttribute("latency", this->latency()); @@ -6815,6 +6850,13 @@ const char* Command::selectionName() const } // close package namespace } // close enterprise namespace -// GENERATED BY BLP_BAS_CODEGEN_2024.07.04.1 +// GENERATED BY BLP_BAS_CODEGEN_2024.07.16 // USING bas_codegen.pl -m msg --noAggregateConversion --noExternalization // --noIdent --package m_bmqtool --msgComponent messages bmqtoolcmd.xsd +// ---------------------------------------------------------------------------- +// NOTICE: +// Copyright 2024 Bloomberg Finance L.P. All rights reserved. +// Property of Bloomberg Finance L.P. (BFLP) +// This software is made available solely pursuant to the +// terms of a BFLP license agreement which governs its use. +// ------------------------------- END-OF-FILE -------------------------------- diff --git a/src/applications/bmqtool/m_bmqtool_messages.h b/src/applications/bmqtool/m_bmqtool_messages.h index f52046e41..e324edb77 100644 --- a/src/applications/bmqtool/m_bmqtool_messages.h +++ b/src/applications/bmqtool/m_bmqtool_messages.h @@ -1,4 +1,4 @@ -// Copyright 2014-2024 Bloomberg Finance L.P. +// Copyright 2024 Bloomberg Finance L.P. // SPDX-License-Identifier: Apache-2.0 // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -5196,6 +5196,8 @@ class CommandLineParameters { bsl::vector d_messageProperties; bsl::string d_mode; bsl::string d_broker; + bsl::string d_tlsAuthority; + bsl::string d_tlsVersions; bsl::string d_queueUri; bsl::string d_queueFlags; bsl::string d_latency; @@ -5228,59 +5230,63 @@ class CommandLineParameters { enum { ATTRIBUTE_ID_MODE = 0, ATTRIBUTE_ID_BROKER = 1, - ATTRIBUTE_ID_QUEUE_URI = 2, - ATTRIBUTE_ID_QUEUE_FLAGS = 3, - ATTRIBUTE_ID_LATENCY = 4, - ATTRIBUTE_ID_LATENCY_REPORT = 5, - ATTRIBUTE_ID_DUMP_MSG = 6, - ATTRIBUTE_ID_CONFIRM_MSG = 7, - ATTRIBUTE_ID_EVENT_SIZE = 8, - ATTRIBUTE_ID_MSG_SIZE = 9, - ATTRIBUTE_ID_POST_RATE = 10, - ATTRIBUTE_ID_EVENTS_COUNT = 11, - ATTRIBUTE_ID_MAX_UNCONFIRMED = 12, - ATTRIBUTE_ID_POST_INTERVAL = 13, - ATTRIBUTE_ID_VERBOSITY = 14, - ATTRIBUTE_ID_LOG_FORMAT = 15, - ATTRIBUTE_ID_MEMORY_DEBUG = 16, - ATTRIBUTE_ID_THREADS = 17, - ATTRIBUTE_ID_SHUTDOWN_GRACE = 18, - ATTRIBUTE_ID_NO_SESSION_EVENT_HANDLER = 19, - ATTRIBUTE_ID_STORAGE = 20, - ATTRIBUTE_ID_LOG = 21, - ATTRIBUTE_ID_SEQUENTIAL_MESSAGE_PATTERN = 22, - ATTRIBUTE_ID_MESSAGE_PROPERTIES = 23, - ATTRIBUTE_ID_SUBSCRIPTIONS = 24 + ATTRIBUTE_ID_TLS_AUTHORITY = 2, + ATTRIBUTE_ID_TLS_VERSIONS = 3, + ATTRIBUTE_ID_QUEUE_URI = 4, + ATTRIBUTE_ID_QUEUE_FLAGS = 5, + ATTRIBUTE_ID_LATENCY = 6, + ATTRIBUTE_ID_LATENCY_REPORT = 7, + ATTRIBUTE_ID_DUMP_MSG = 8, + ATTRIBUTE_ID_CONFIRM_MSG = 9, + ATTRIBUTE_ID_EVENT_SIZE = 10, + ATTRIBUTE_ID_MSG_SIZE = 11, + ATTRIBUTE_ID_POST_RATE = 12, + ATTRIBUTE_ID_EVENTS_COUNT = 13, + ATTRIBUTE_ID_MAX_UNCONFIRMED = 14, + ATTRIBUTE_ID_POST_INTERVAL = 15, + ATTRIBUTE_ID_VERBOSITY = 16, + ATTRIBUTE_ID_LOG_FORMAT = 17, + ATTRIBUTE_ID_MEMORY_DEBUG = 18, + ATTRIBUTE_ID_THREADS = 19, + ATTRIBUTE_ID_SHUTDOWN_GRACE = 20, + ATTRIBUTE_ID_NO_SESSION_EVENT_HANDLER = 21, + ATTRIBUTE_ID_STORAGE = 22, + ATTRIBUTE_ID_LOG = 23, + ATTRIBUTE_ID_SEQUENTIAL_MESSAGE_PATTERN = 24, + ATTRIBUTE_ID_MESSAGE_PROPERTIES = 25, + ATTRIBUTE_ID_SUBSCRIPTIONS = 26 }; - enum { NUM_ATTRIBUTES = 25 }; + enum { NUM_ATTRIBUTES = 27 }; enum { ATTRIBUTE_INDEX_MODE = 0, ATTRIBUTE_INDEX_BROKER = 1, - ATTRIBUTE_INDEX_QUEUE_URI = 2, - ATTRIBUTE_INDEX_QUEUE_FLAGS = 3, - ATTRIBUTE_INDEX_LATENCY = 4, - ATTRIBUTE_INDEX_LATENCY_REPORT = 5, - ATTRIBUTE_INDEX_DUMP_MSG = 6, - ATTRIBUTE_INDEX_CONFIRM_MSG = 7, - ATTRIBUTE_INDEX_EVENT_SIZE = 8, - ATTRIBUTE_INDEX_MSG_SIZE = 9, - ATTRIBUTE_INDEX_POST_RATE = 10, - ATTRIBUTE_INDEX_EVENTS_COUNT = 11, - ATTRIBUTE_INDEX_MAX_UNCONFIRMED = 12, - ATTRIBUTE_INDEX_POST_INTERVAL = 13, - ATTRIBUTE_INDEX_VERBOSITY = 14, - ATTRIBUTE_INDEX_LOG_FORMAT = 15, - ATTRIBUTE_INDEX_MEMORY_DEBUG = 16, - ATTRIBUTE_INDEX_THREADS = 17, - ATTRIBUTE_INDEX_SHUTDOWN_GRACE = 18, - ATTRIBUTE_INDEX_NO_SESSION_EVENT_HANDLER = 19, - ATTRIBUTE_INDEX_STORAGE = 20, - ATTRIBUTE_INDEX_LOG = 21, - ATTRIBUTE_INDEX_SEQUENTIAL_MESSAGE_PATTERN = 22, - ATTRIBUTE_INDEX_MESSAGE_PROPERTIES = 23, - ATTRIBUTE_INDEX_SUBSCRIPTIONS = 24 + ATTRIBUTE_INDEX_TLS_AUTHORITY = 2, + ATTRIBUTE_INDEX_TLS_VERSIONS = 3, + ATTRIBUTE_INDEX_QUEUE_URI = 4, + ATTRIBUTE_INDEX_QUEUE_FLAGS = 5, + ATTRIBUTE_INDEX_LATENCY = 6, + ATTRIBUTE_INDEX_LATENCY_REPORT = 7, + ATTRIBUTE_INDEX_DUMP_MSG = 8, + ATTRIBUTE_INDEX_CONFIRM_MSG = 9, + ATTRIBUTE_INDEX_EVENT_SIZE = 10, + ATTRIBUTE_INDEX_MSG_SIZE = 11, + ATTRIBUTE_INDEX_POST_RATE = 12, + ATTRIBUTE_INDEX_EVENTS_COUNT = 13, + ATTRIBUTE_INDEX_MAX_UNCONFIRMED = 14, + ATTRIBUTE_INDEX_POST_INTERVAL = 15, + ATTRIBUTE_INDEX_VERBOSITY = 16, + ATTRIBUTE_INDEX_LOG_FORMAT = 17, + ATTRIBUTE_INDEX_MEMORY_DEBUG = 18, + ATTRIBUTE_INDEX_THREADS = 19, + ATTRIBUTE_INDEX_SHUTDOWN_GRACE = 20, + ATTRIBUTE_INDEX_NO_SESSION_EVENT_HANDLER = 21, + ATTRIBUTE_INDEX_STORAGE = 22, + ATTRIBUTE_INDEX_LOG = 23, + ATTRIBUTE_INDEX_SEQUENTIAL_MESSAGE_PATTERN = 24, + ATTRIBUTE_INDEX_MESSAGE_PROPERTIES = 25, + ATTRIBUTE_INDEX_SUBSCRIPTIONS = 26 }; // CONSTANTS @@ -5290,6 +5296,10 @@ class CommandLineParameters { static const char DEFAULT_INITIALIZER_BROKER[]; + static const char DEFAULT_INITIALIZER_TLS_AUTHORITY[]; + + static const char DEFAULT_INITIALIZER_TLS_VERSIONS[]; + static const char DEFAULT_INITIALIZER_QUEUE_URI[]; static const char DEFAULT_INITIALIZER_QUEUE_FLAGS[]; @@ -5433,6 +5443,14 @@ class CommandLineParameters { // Return a reference to the modifiable "Broker" attribute of this // object. + bsl::string& tlsAuthority(); + // Return a reference to the modifiable "TlsAuthority" attribute of + // this object. + + bsl::string& tlsVersions(); + // Return a reference to the modifiable "TlsVersions" attribute of this + // object. + bsl::string& queueUri(); // Return a reference to the modifiable "QueueUri" attribute of this // object. @@ -5575,6 +5593,14 @@ class CommandLineParameters { // Return a reference offering non-modifiable access to the "Broker" // attribute of this object. + const bsl::string& tlsAuthority() const; + // Return a reference offering non-modifiable access to the + // "TlsAuthority" attribute of this object. + + const bsl::string& tlsVersions() const; + // Return a reference offering non-modifiable access to the + // "TlsVersions" attribute of this object. + const bsl::string& queueUri() const; // Return a reference offering non-modifiable access to the "QueueUri" // attribute of this object. @@ -10526,6 +10552,8 @@ void CommandLineParameters::hashAppendImpl( using bslh::hashAppend; hashAppend(hashAlgorithm, this->mode()); hashAppend(hashAlgorithm, this->broker()); + hashAppend(hashAlgorithm, this->tlsAuthority()); + hashAppend(hashAlgorithm, this->tlsVersions()); hashAppend(hashAlgorithm, this->queueUri()); hashAppend(hashAlgorithm, this->queueFlags()); hashAppend(hashAlgorithm, this->latency()); @@ -10555,6 +10583,8 @@ inline bool CommandLineParameters::isEqualTo(const CommandLineParameters& rhs) const { return this->mode() == rhs.mode() && this->broker() == rhs.broker() && + this->tlsAuthority() == rhs.tlsAuthority() && + this->tlsVersions() == rhs.tlsVersions() && this->queueUri() == rhs.queueUri() && this->queueFlags() == rhs.queueFlags() && this->latency() == rhs.latency() && @@ -10597,6 +10627,18 @@ int CommandLineParameters::manipulateAttributes(t_MANIPULATOR& manipulator) return ret; } + ret = manipulator(&d_tlsAuthority, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_AUTHORITY]); + if (ret) { + return ret; + } + + ret = manipulator(&d_tlsVersions, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_VERSIONS]); + if (ret) { + return ret; + } + ret = manipulator(&d_queueUri, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_QUEUE_URI]); if (ret) { @@ -10755,6 +10797,15 @@ int CommandLineParameters::manipulateAttribute(t_MANIPULATOR& manipulator, return manipulator(&d_broker, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_BROKER]); } + case ATTRIBUTE_ID_TLS_AUTHORITY: { + return manipulator( + &d_tlsAuthority, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_AUTHORITY]); + } + case ATTRIBUTE_ID_TLS_VERSIONS: { + return manipulator(&d_tlsVersions, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_VERSIONS]); + } case ATTRIBUTE_ID_QUEUE_URI: { return manipulator(&d_queueUri, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_QUEUE_URI]); @@ -10884,6 +10935,16 @@ inline bsl::string& CommandLineParameters::broker() return d_broker; } +inline bsl::string& CommandLineParameters::tlsAuthority() +{ + return d_tlsAuthority; +} + +inline bsl::string& CommandLineParameters::tlsVersions() +{ + return d_tlsVersions; +} + inline bsl::string& CommandLineParameters::queueUri() { return d_queueUri; @@ -11015,6 +11076,18 @@ int CommandLineParameters::accessAttributes(t_ACCESSOR& accessor) const return ret; } + ret = accessor(d_tlsAuthority, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_AUTHORITY]); + if (ret) { + return ret; + } + + ret = accessor(d_tlsVersions, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_VERSIONS]); + if (ret) { + return ret; + } + ret = accessor(d_queueUri, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_QUEUE_URI]); if (ret) { @@ -11165,6 +11238,14 @@ int CommandLineParameters::accessAttribute(t_ACCESSOR& accessor, int id) const return accessor(d_broker, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_BROKER]); } + case ATTRIBUTE_ID_TLS_AUTHORITY: { + return accessor(d_tlsAuthority, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_AUTHORITY]); + } + case ATTRIBUTE_ID_TLS_VERSIONS: { + return accessor(d_tlsVersions, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_VERSIONS]); + } case ATTRIBUTE_ID_QUEUE_URI: { return accessor(d_queueUri, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_QUEUE_URI]); @@ -11289,6 +11370,16 @@ inline const bsl::string& CommandLineParameters::broker() const return d_broker; } +inline const bsl::string& CommandLineParameters::tlsAuthority() const +{ + return d_tlsAuthority; +} + +inline const bsl::string& CommandLineParameters::tlsVersions() const +{ + return d_tlsVersions; +} + inline const bsl::string& CommandLineParameters::queueUri() const { return d_queueUri; @@ -12366,6 +12457,6 @@ inline bool Command::isUndefinedValue() const } // close enterprise namespace #endif -// GENERATED BY BLP_BAS_CODEGEN_2024.07.04.1 +// GENERATED BY BLP_BAS_CODEGEN_2024.07.16 // USING bas_codegen.pl -m msg --noAggregateConversion --noExternalization // --noIdent --package m_bmqtool --msgComponent messages bmqtoolcmd.xsd diff --git a/src/applications/bmqtool/m_bmqtool_parameters.cpp b/src/applications/bmqtool/m_bmqtool_parameters.cpp index d8ab2e5b6..7188051bc 100644 --- a/src/applications/bmqtool/m_bmqtool_parameters.cpp +++ b/src/applications/bmqtool/m_bmqtool_parameters.cpp @@ -404,6 +404,7 @@ bool Parameters::from(bsl::ostream& stream, setShutdownGrace(params.shutdownGrace()); setMessageProperties(params.messageProperties()); setSubscriptions(params.subscriptions()); + setCertificateAuthority(params.tlsAuthority()); return true; } diff --git a/src/applications/bmqtool/m_bmqtool_parameters.h b/src/applications/bmqtool/m_bmqtool_parameters.h index 9e0e5ddde..ce6c93219 100644 --- a/src/applications/bmqtool/m_bmqtool_parameters.h +++ b/src/applications/bmqtool/m_bmqtool_parameters.h @@ -272,6 +272,8 @@ class Parameters { // A name of a property to put auto-incremented values // in batch-posting mode. + bsl::string d_certificateAuthority; + public: // CREATORS @@ -307,6 +309,7 @@ class Parameters { setMessageProperties(const bsl::vector& value); Parameters& setSubscriptions(const bsl::vector& value); Parameters& setAutoIncrementedField(const bsl::string& value); + Parameters& setCertificateAuthority(const bsl::string& value); // Set the corresponding member to the specified 'value' and return a // reference offering modifiable access to this object. @@ -365,6 +368,7 @@ class Parameters { const bsl::vector& messageProperties() const; const bsl::vector& subscriptions() const; const bsl::string& autoIncrementedField() const; + const bsl::string& certificateAuthority() const; }; // FREE OPERATORS @@ -574,6 +578,13 @@ Parameters::setAutoIncrementedField(const bsl::string& value) return *this; } +inline Parameters& +Parameters::setCertificateAuthority(const bsl::string& value) +{ + d_certificateAuthority = value; + return *this; +} + // ACCESSORS inline ParametersMode::Value Parameters::mode() const { @@ -721,6 +732,11 @@ inline const bsl::string& Parameters::autoIncrementedField() const return d_autoIncrementedField; } +inline const bsl::string& Parameters::certificateAuthority() const +{ + return d_certificateAuthority; +} + } // close package namespace // -------------------------- diff --git a/src/groups/bmq/bmqex/bmqex_promise_cpp03.h b/src/groups/bmq/bmqex/bmqex_promise_cpp03.h index 717804d9f..67aba8e84 100644 --- a/src/groups/bmq/bmqex/bmqex_promise_cpp03.h +++ b/src/groups/bmq/bmqex/bmqex_promise_cpp03.h @@ -36,8 +36,13 @@ // regions of C++11 code, then this header contains no code and is not // '#include'd in the original header. // +<<<<<<< HEAD:src/groups/bmq/bmqex/bmqex_promise_cpp03.h // Generated on Thu Oct 17 16:05:31 2024 // Command line: sim_cpp11_features.pl bmqex_promise.h +======= +// Generated on Tue Mar 19 16:17:27 2024 +// Command line: sim_cpp11_features.pl bmqex_promise.h +>>>>>>> fa40504b6 (Add integration tests for TLS):src/groups/bmq/bmqex/bmqex_promise_cpp03.h #ifdef COMPILING_BMQEX_PROMISE_H diff --git a/src/groups/bmq/bmqimp/bmqimp_application.cpp b/src/groups/bmq/bmqimp/bmqimp_application.cpp index 4d6dd304d..46e9aa00c 100644 --- a/src/groups/bmq/bmqimp/bmqimp_application.cpp +++ b/src/groups/bmq/bmqimp/bmqimp_application.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -34,9 +35,6 @@ #include #include #include -#include -#include -#include // BDE #include @@ -65,6 +63,8 @@ #include #include +#include + namespace BloombergLP { namespace bmqimp { @@ -249,10 +249,12 @@ void Application::channelStateCallback( bdlf::PlaceHolders::_2, // numNeeded bdlf::PlaceHolders::_3, // blob channel)); + if (!st) { BALL_LOG_ERROR << "Could not read from channel:" << " [peer: " << channel->peerUri() - << ", status: " << status << "]"; + << ", status: " << st + << "]"; // #review st -> status? bug here before channel->close(); return; // RETURN } @@ -260,6 +262,7 @@ void Application::channelStateCallback( // Cancel the timeout event (if the handle is invalid, this will just // do nothing) d_scheduler.cancelEvent(&d_startTimeoutHandle); + } break; // BREAK case bmqio::ChannelFactoryEvent::e_CONNECT_ATTEMPT_FAILED: { BALL_LOG_DEBUG << "Failed an attempt to establish a session with '" @@ -317,6 +320,43 @@ void Application::brokerSessionStopped( BALL_LOG_INFO << "bmqimp::Application stop completed"; } +int Application::loadTlsConfig(bmqio::NtcChannelFactory* channelFactory, + const bsl::string& caPath) +{ + bmqio::NtcCertificateLoader certLoader = + channelFactory->createCertificateLoader(); + + int rc = 0; + if ((rc = d_certificateStore.loadCertificateAuthority(certLoader, + caPath))) { + return rc; + } + + ntca::EncryptionClientOptions encryptionClientOptions; + // Set the minimum version to TLS 1.3 + encryptionClientOptions.setMinMethod(ntca::EncryptionMethod::e_TLS_V1_3); + encryptionClientOptions.setMaxMethod(ntca::EncryptionMethod::e_TLS_V1_3); + // Enable server side authentication + encryptionClientOptions.setAuthentication( + ntca::EncryptionAuthentication::e_VERIFY); + + ntsa::Error err; + { + bsl::vector authorityData(&d_allocator); + err = d_certificateStore.certificateAuthority()->encode( + &authorityData); + if (err) { + return err.code(); + } + encryptionClientOptions.addAuthorityData(authorityData); + } + + err = channelFactory->createEncryptionClient(&d_encryptionClient_sp, + encryptionClientOptions); + + return err.code(); +} + bmqt::GenericResult::Enum Application::startChannel() { // executed by the FSM thread @@ -368,6 +408,11 @@ bmqt::GenericResult::Enum Application::startChannel() .setAttemptInterval(attemptInterval) .setAutoReconnect(true); + if (d_sessionOptions.isTlsSession()) { + loadTlsConfig(&d_channelFactory, + d_sessionOptions.certificateAuthority()); + } + d_negotiatedChannelFactory.connect( &status, &d_connectHandle_mp, @@ -378,6 +423,7 @@ bmqt::GenericResult::Enum Application::startChannel() bdlf::PlaceHolders::_1, // event bdlf::PlaceHolders::_2, // status bdlf::PlaceHolders::_3)); // channel + if (!status) { BALL_LOG_ERROR << "Failed to connect to broker at '" << d_sessionOptions.brokerUri() @@ -585,6 +631,7 @@ Application::Application( negotiationMessage, sessionOptions.connectTimeout(), &d_blobSpPool, + d_encryptionClient_sp, allocator), allocator) , d_connectHandle_mp() @@ -599,6 +646,7 @@ Application::Application( , d_statSnaphotTimerHandle() , d_nextStatDump(-1) , d_lastAllocatorSnapshot(0) +, d_encryptionClient_sp() { // NOTE: // o The persistent session pool must live longer than the brokerSession diff --git a/src/groups/bmq/bmqimp/bmqimp_application.h b/src/groups/bmq/bmqimp/bmqimp_application.h index d5d3b00da..1e696a7db 100644 --- a/src/groups/bmq/bmqimp/bmqimp_application.h +++ b/src/groups/bmq/bmqimp/bmqimp_application.h @@ -35,13 +35,13 @@ // Thread safe. // BMQ - #include #include #include #include #include +#include #include #include #include @@ -70,6 +70,22 @@ namespace BloombergLP { +namespace ntci { +class Upgradable; +class EncryptionServer; +class Upgradable; +} + +namespace ntca { +class UpgradeEvent; +} + +// TODO move TLS config loading into ntcchannelfactory + +namespace bmqio { +class NtcChannelFactory; +} + namespace bmqimp { // ================= @@ -158,6 +174,11 @@ class Application { // the snapshot was performed on the // Counting Allocators context + // todo review NTC symbols exposed here + bmqio::CertificateStore d_certificateStore; + + bsl::shared_ptr d_encryptionClient_sp; + private: // PRIVATE MANIPULATORS void onChannelDown(const bsl::string& peerUri, @@ -199,6 +220,9 @@ class Application { /// values. void printStats(bool isFinal); + int loadTlsConfig(bmqio::NtcChannelFactory* channelFactory, + const bsl::string& caPath); + /// To be invoked when the session is being started, in order to setup /// the network channel. bmqt::GenericResult::Enum startChannel(); diff --git a/src/groups/bmq/bmqimp/bmqimp_negotiatedchannelfactory.cpp b/src/groups/bmq/bmqimp/bmqimp_negotiatedchannelfactory.cpp index af83af556..20b88a861 100644 --- a/src/groups/bmq/bmqimp/bmqimp_negotiatedchannelfactory.cpp +++ b/src/groups/bmq/bmqimp/bmqimp_negotiatedchannelfactory.cpp @@ -16,13 +16,16 @@ // bmqimp_negotiatedchannelfactory.cpp -*-C++-*- #include -#include // BMQ #include #include #include +#include +#include #include +#include +#include #include #include #include @@ -34,10 +37,13 @@ #include #include #include +#include #include #include #include +#include + namespace BloombergLP { namespace bmqimp { @@ -56,6 +62,20 @@ enum RcEnum { rc_NEGOTIATION_FAILURE = -7 }; +bmqio::Channel* findParent(bmqio::Channel* channel) +{ + bmqio::Channel* self = channel; + bmqio::DecoratingChannelPartialImp* decoratedChannel = + dynamic_cast(self); + while (decoratedChannel != NULL) { + self = decoratedChannel->base(); + decoratedChannel = dynamic_cast( + self); + } + + return self; +} + } // close unnamed namespace // ------------------------------------ @@ -63,15 +83,17 @@ enum RcEnum { // ------------------------------------ NegotiatedChannelFactoryConfig::NegotiatedChannelFactoryConfig( - bmqio::ChannelFactory* base, - const bmqp_ctrlmsg::NegotiationMessage& negotiationMessage, - const bsls::TimeInterval& negotiationTimeout, - BlobSpPool* blobSpPool_p, - bslma::Allocator* basicAllocator) + bmqio::ChannelFactory* base, + const bmqp_ctrlmsg::NegotiationMessage& negotiationMessage, + const bsls::TimeInterval& negotiationTimeout, + BlobSpPool* blobSpPool_p, + const bsl::shared_ptr& encryptionClient, + bslma::Allocator* basicAllocator) : d_baseFactory_p(base) , d_negotiationMessage(negotiationMessage, basicAllocator) , d_negotiationTimeout(negotiationTimeout) , d_blobSpPool_p(blobSpPool_p) +, d_encryptionClient(encryptionClient) , d_allocator_p(bslma::Default::allocator(basicAllocator)) { // PRECONDITIONS @@ -85,6 +107,7 @@ NegotiatedChannelFactoryConfig::NegotiatedChannelFactoryConfig( , d_negotiationMessage(original.d_negotiationMessage, basicAllocator) , d_negotiationTimeout(original.d_negotiationTimeout) , d_blobSpPool_p(original.d_blobSpPool_p) +, d_encryptionClient(original.d_encryptionClient) , d_allocator_p(bslma::Default::allocator(basicAllocator)) { // NOTHING @@ -114,6 +137,70 @@ void NegotiatedChannelFactory::baseResultCallback( return; // RETURN } + if (isTlsSession()) { + negotiateWithTls(channel, userCb); + } + else { + negotiate(channel, userCb); + } +} + +bool NegotiatedChannelFactory::isTlsSession() const +{ + return d_config.d_encryptionClient.get().get() != NULL; +} + +void NegotiatedChannelFactory::negotiateWithTls( + const bsl::shared_ptr& channel, + const ResultCallback& userCb) const +{ + BSLS_ASSERT(channel); + + // ew + bmqio::Channel* parent = findParent(channel.get()); + bmqio::NtcChannel* alias = dynamic_cast(parent); + + if (alias) { + alias->upgrade(d_config.d_encryptionClient, + ntca::UpgradeOptions(), + bdlf::BindUtil::bindS( + allocator(), + &NegotiatedChannelFactory::negotiationWithTlsInit, + this, + channel, // Channel + bdlf::PlaceHolders::_1, // Upgradable + bdlf::PlaceHolders::_2, // Event + userCb // Complete callback + )); + } + else { + BALL_LOG_ERROR << "Unsupported channel implementation for TLS"; + bmqio::Status status(bmqio::StatusCategory::e_GENERIC_ERROR, + allocator()); + channel->close(status); + + return; // RETURN + } +} + +void NegotiatedChannelFactory::negotiationWithTlsInit( + const bsl::shared_ptr& channel, + BSLA_UNUSED const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& event, + const ResultCallback& userCb) const +{ + BSLS_ASSERT(channel); + + if (event.isError()) { + BALL_LOG_ERROR << "TLS upgrade error: " << event; + + bmqio::Status status(bmqio::StatusCategory::e_GENERIC_ERROR, + allocator()); + channel->close(status); + + return; + } + negotiate(channel, userCb); } diff --git a/src/groups/bmq/bmqimp/bmqimp_negotiatedchannelfactory.h b/src/groups/bmq/bmqimp/bmqimp_negotiatedchannelfactory.h index 05cbf2ad5..b20bf3692 100644 --- a/src/groups/bmq/bmqimp/bmqimp_negotiatedchannelfactory.h +++ b/src/groups/bmq/bmqimp/bmqimp_negotiatedchannelfactory.h @@ -50,6 +50,15 @@ namespace BloombergLP { +namespace ntca { +class UpgradeEvent; +} + +namespace ntci { +class Upgradable; +class EncryptionClient; +} + namespace bmqimp { // ==================================== @@ -61,6 +70,10 @@ class NegotiatedChannelFactoryConfig { public: // TYPES typedef bmqp::BlobPoolUtil::BlobSpPool BlobSpPool; + // #review: docs + typedef bsl::function& channel, + const bsl::function& cb)> + TlsUpgradeCallback; private: // PRIVATE DATA @@ -68,7 +81,9 @@ class NegotiatedChannelFactoryConfig { bmqp_ctrlmsg::NegotiationMessage d_negotiationMessage; bsls::TimeInterval d_negotiationTimeout; BlobSpPool* d_blobSpPool_p; - bslma::Allocator* d_allocator_p; + bsl::reference_wrapper > + d_encryptionClient; + bslma::Allocator* d_allocator_p; // FRIENDS friend class NegotiatedChannelFactory; @@ -79,16 +94,26 @@ class NegotiatedChannelFactoryConfig { bslma::UsesBslmaAllocator) // CREATORS + + /// @brief Create a configuration for building a NegotiatedChannelFactory. + /// If certificateAuthority is empty, a TLS session will not be created. + /// + /// @pre base != NULL + /// @pre bufferFactory != NULL NegotiatedChannelFactoryConfig( - bmqio::ChannelFactory* base, - const bmqp_ctrlmsg::NegotiationMessage& negotiationMessage, - const bsls::TimeInterval& negotiationTimeout, - BlobSpPool* blobSpPool_p, - bslma::Allocator* basicAllocator = 0); + bmqio::ChannelFactory* base, + const bmqp_ctrlmsg::NegotiationMessage& negotiationMessage, + const bsls::TimeInterval& negotiationTimeout, + BlobSpPool* blobSpPool_p, + const bsl::shared_ptr& encryptionClient, + bslma::Allocator* basicAllocator = 0); NegotiatedChannelFactoryConfig( const NegotiatedChannelFactoryConfig& original, bslma::Allocator* basicAllocator = 0); + + // ACCESSORS + bslma::Allocator* allocator() const; }; // ============================== @@ -154,6 +179,19 @@ class NegotiatedChannelFactory : public bmqio::ChannelFactory { const ResultCallback& cb, const bsl::shared_ptr& channel) const; + /// @pre channel != NULL + void negotiateWithTls(const bsl::shared_ptr& channel, + const ResultCallback& userCb) const; + + /// @pre channel != NULL + void + negotiationWithTlsInit(const bsl::shared_ptr& channel, + const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& event, + const ResultCallback& userCb) const; + + bool isTlsSession() const; + public: // CREATORS explicit NegotiatedChannelFactory(const Config& config, @@ -162,6 +200,9 @@ class NegotiatedChannelFactory : public bmqio::ChannelFactory { ~NegotiatedChannelFactory() BSLS_KEYWORD_OVERRIDE; public: + // ACCESSORS + bslma::Allocator* allocator() const; + // MANIPULATORS void listen(bmqio::Status* status, bslma::ManagedPtr* handle, @@ -174,6 +215,28 @@ class NegotiatedChannelFactory : public bmqio::ChannelFactory { const ResultCallback& cb) BSLS_KEYWORD_OVERRIDE; }; +// ============================================================================ +// INLINE DEFINITIONS +// ============================================================================ + +// ==================================== +// class NegotiatedChannelFactoryConfig +// ==================================== + +inline bslma::Allocator* NegotiatedChannelFactoryConfig::allocator() const +{ + return d_allocator_p; +} + +// ============================== +// class NegotiatedChannelFactory +// ============================== + +inline bslma::Allocator* NegotiatedChannelFactory::allocator() const +{ + return d_config.allocator(); +} + } // close package namespace } // close enterprise namespace diff --git a/src/groups/bmq/bmqio/bmqio_certificatestore.cpp b/src/groups/bmq/bmqio/bmqio_certificatestore.cpp new file mode 100644 index 000000000..3782799ab --- /dev/null +++ b/src/groups/bmq/bmqio/bmqio_certificatestore.cpp @@ -0,0 +1,192 @@ +// Copyright 2024 Bloomberg Finance L.P. +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// bmqio_certificatestore.cpp + +#include +BSLS_IDENT_RCSID(bmqio_certificatestore_h, "$Id$ $CSID$") + +#include + +// ntc +#include +#include +#include + +// bal +#include + +// bsl +#include +#include + +namespace { + +const char LOG_CATEGORY[] = "MQBNET.CERTIFICATESTORE"; + +} // close unnamed namespace + +BALL_LOG_SET_NAMESPACE_CATEGORY(LOG_CATEGORY) + +namespace BloombergLP { +namespace bmqio { + +CertificateLoader::~CertificateLoader() +{ +} + +NtcCertificateLoader::NtcCertificateLoader( + const bsl::shared_ptr& interface) +: d_interface_sp(interface) +{ + BSLS_ASSERT(interface); +} + +NtcCertificateLoader::NtcCertificateLoader( + bslmf::MovableRef other) +: d_interface_sp(bslmf::MovableRefUtil::move( + bslmf::MovableRefUtil::access(other).d_interface_sp)) +{ +} + +NtcCertificateLoader& +NtcCertificateLoader::operator=(bslmf::MovableRef other) +{ + if (&other == this) { + return *this; + } + + NtcCertificateLoader& otherRef = other; + d_interface_sp = bslmf::MovableRefUtil::move(otherRef.d_interface_sp); + + return *this; +} + +bmqvt::ValueOrError +NtcCertificateLoader::loadCertificateFromPath( + const bsl::string& certificatePath) +{ + bsl::shared_ptr certificate; + + // TODO Consider forwarding an allocator through this interface + ntsa::Error err = d_interface_sp->loadCertificate(&certificate, + certificatePath); + + bmqvt::ValueOrError result; + if (!err) { + result.makeValue(bslmf::MovableRefUtil::move(certificate)); + } + else { + result.makeError(bslmf::MovableRefUtil::move(err)); + } + + BSLS_ASSERT(!result.isUndefined()); + return result; +} + +bmqvt::ValueOrError +NtcCertificateLoader::loadKeyFromPath(const bsl::string& keyPath) +{ + bsl::shared_ptr key; + + ntsa::Error err = d_interface_sp->loadKey(&key, keyPath); + + bmqvt::ValueOrError result; + if (!err) { + result.makeValue(bslmf::MovableRefUtil::move(key)); + } + else { + result.makeError(err); + } + + return result; +} + +CertificateStore::CertificateStore(bslmf::MovableRef other) +: d_certificate_sp(bslmf::MovableRefUtil::move( + bslmf::MovableRefUtil::access(other).d_certificate_sp)) +, d_certificateAuthority_sp(bslmf::MovableRefUtil::move( + bslmf::MovableRefUtil::access(other).d_certificateAuthority_sp)) +, d_serverPrivateKey_sp(bslmf::MovableRefUtil::move( + bslmf::MovableRefUtil::access(other).d_serverPrivateKey_sp)) +{ +} + +CertificateStore& +CertificateStore::operator=(bslmf::MovableRef other) +{ + // TODO: The BDE docs show doing this but I'm not convinced it's correct + CertificateStore& store(other); + if (&store == this) { + return *this; + } + + d_certificate_sp = bslmf::MovableRefUtil::move(d_certificate_sp); + d_certificateAuthority_sp = bslmf::MovableRefUtil::move( + d_certificateAuthority_sp); + d_serverPrivateKey_sp = bslmf::MovableRefUtil::move(d_serverPrivateKey_sp); + + return *this; +} + +// #review: do we want to support partially initialized CertificateStore? +// for clients where only CA is used for example +int CertificateStore::loadCertificateAuthority(CertificateLoader& loader, + const bsl::string& caPath) +{ + bmqvt::ValueOrError + expectedCertificate = loader.loadCertificateFromPath(caPath); + if (expectedCertificate.isError()) { + const Error& error = expectedCertificate.error(); + BALL_LOG_ERROR << "While loading certificate authority rc: " << error + << ", path: " << caPath; + return error.code(); + } + + return 0; +} + +int CertificateStore::loadCertificate(CertificateLoader& loader, + const bsl::string& certPath) +{ + bmqvt::ValueOrError + expectedCertificate = loader.loadCertificateFromPath(certPath); + if (expectedCertificate.isError()) { + const Error& error = expectedCertificate.error(); + BALL_LOG_ERROR << "While loading certificate rc: " << error + << ", path: " << certPath; + return error.code(); + } + + return 0; +} + +int CertificateStore::loadKey(CertificateLoader& loader, + const bsl::string& keyPath) +{ + bmqvt::ValueOrError + expectedCertificate = loader.loadCertificateFromPath(keyPath); + if (expectedCertificate.isError()) { + const Error& error = expectedCertificate.error(); + BALL_LOG_ERROR << "While loading key rc: " << error + << ", path: " << keyPath; + return error.code(); + } + + return 0; +} + +} // close namespace bmqio +} // close namespace BloombergLP diff --git a/src/groups/bmq/bmqio/bmqio_certificatestore.h b/src/groups/bmq/bmqio/bmqio_certificatestore.h new file mode 100644 index 000000000..04dc69665 --- /dev/null +++ b/src/groups/bmq/bmqio/bmqio_certificatestore.h @@ -0,0 +1,200 @@ +// Copyright 2024 Bloomberg Finance L.P. +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// bmqio_certificatestore.h +#ifndef INCLUDED_BMQIO_CERTIFICATESTORE +#define INCLUDED_BMQIO_CERTIFICATESTORE + +#include +BSLS_IDENT_RCSID(bmqio_certificatestore_h, "$Id$ $CSID$") +BSLS_IDENT_PRAGMA_ONCE + +/// PURPOSE: Provide a way to access broker certificates and keys +/// +/// CLASSES: +/// bmqio::CertificateStore: A store manager for certificates and keys +/// +/// DESCRIPTION: This component provides a store object, +/// `bmqio::CertificateStore`, +/// to provide an easy way to load certificates and keys from the filesystem +/// into a single interface. + +// bmq +#include + +#include + +// bsl +#include +#include +#include + +namespace BloombergLP { + +namespace ntci { +class EncryptionCertificate; +class EncryptionKey; +class Interface; +} + +namespace ntsa { +class Error; +} + +namespace bmqio { + +class Error : public ntsa::Error { + public: + Error() + : ntsa::Error() + { + } + + Error(const ntsa::Error& error) + : ntsa::Error(error) + { + } +}; + +class CertificateLoader { + public: + typedef bsl::shared_ptr Certificate; + typedef bsl::shared_ptr Key; + + virtual ~CertificateLoader(); + + virtual bmqvt::ValueOrError + loadCertificateFromPath(const bsl::string& path) = 0; + + virtual bmqvt::ValueOrError + loadKeyFromPath(const bsl::string& path) = 0; +}; + +class NtcCertificateLoader : public CertificateLoader { + bsl::shared_ptr d_interface_sp; + + public: + /// @brief Construct a certificate loader from an ntci::Interface + /// + /// @pre This operation is undefined unless interface is not null. + NtcCertificateLoader(const bsl::shared_ptr& interface); + + NtcCertificateLoader(bslmf::MovableRef other); + NtcCertificateLoader& + operator=(bslmf::MovableRef other); + + bmqvt::ValueOrError + loadCertificateFromPath(const bsl::string& path) BSLS_KEYWORD_OVERRIDE; + + bmqvt::ValueOrError + loadKeyFromPath(const bsl::string& path) BSLS_KEYWORD_OVERRIDE; + + private: + // PRIVATE CONSTRUCTORS + NtcCertificateLoader(const NtcCertificateLoader& other); + NtcCertificateLoader& operator=(const NtcCertificateLoader& other); +}; + +/// Stores keys and certs from the TLS config +class CertificateStore { + public: + typedef bsl::shared_ptr Certificate; + typedef bsl::shared_ptr Key; + + private: + Certificate d_certificate_sp; + Certificate d_certificateAuthority_sp; + Key d_serverPrivateKey_sp; + + public: + CertificateStore(); + + CertificateStore(bslmf::MovableRef other); + + CertificateStore& operator=(bslmf::MovableRef other); + + /// @brief Load a PEM formatted certificate authority stored in the path + /// specified by `caPath`. + /// + /// If caPath is a valid paths to a PEM formatted file, load it + /// into memory and initialize certificateAuthority(). + /// + /// @returns Non-zero if loading certificates failed. + int loadCertificateAuthority(CertificateLoader& loader, + const bsl::string& caPath); + + /// @brief Load a PEM formatted certificate authority stored in the path + /// specified by `certPath`. + /// + /// If certPath is a valid paths to a PEM formatted file, load it + /// into memory and initialize certificate(). + /// + /// @returns Non-zero if loading certificates failed. + int loadCertificate(CertificateLoader& loader, + const bsl::string& certPath); + + /// @brief Load a PEM formatted key stored in the path specified by + /// `keyPath`. + /// + /// If keyPath is a valid paths to a PEM formatted file, load it + /// into memory and initialize key(). + /// + /// @returns Non-zero if loading keys failed. + int loadKey(CertificateLoader& interface, const bsl::string& keyPath); + + /// @brief Get a handle to the loaded certificate. + /// + /// @returns A possibly null handle to the stored certificate. + Certificate certificate() const; + + /// @brief Get a handle to the loaded certificate authority. + /// + /// @returns A possibly null handle to the stored certificate authority. + Certificate certificateAuthority() const; + + /// @brief Get a handle to the loaded key. + /// + /// @returns A possibly null handle to the stored key. + Key key() const; +}; + +// ============================================================================ +// INLINE FUNCTION DEFINITIONS +// ============================================================================ + +inline CertificateStore::CertificateStore() +{ + // = default +} + +inline CertificateStore::Certificate CertificateStore::certificate() const +{ + return d_certificate_sp; +} + +inline CertificateStore::Certificate +CertificateStore::certificateAuthority() const +{ + return d_certificateAuthority_sp; +} + +inline CertificateStore::Key CertificateStore::key() const +{ + return d_serverPrivateKey_sp; +} + +} // close namespace bmqio +} // close namespace BloombergLP +#endif diff --git a/src/groups/bmq/bmqio/bmqio_certificatestore.t.cpp b/src/groups/bmq/bmqio/bmqio_certificatestore.t.cpp new file mode 100644 index 000000000..ac5f4d899 --- /dev/null +++ b/src/groups/bmq/bmqio/bmqio_certificatestore.t.cpp @@ -0,0 +1,149 @@ +// Copyright 2023 Bloomberg Finance L.P. +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// bmqio_certificatestore.t.cpp -*-C++-*- +#include + +// TEST DRIVER +#include + +// gmock +#include + +// NTC +#include +#include +#include +#include +#include + +// BDE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// CONVENIENCE +using namespace BloombergLP; +using namespace bmqio; + +// ============================================================================ +// TEST HELPERS UTILITY +// ---------------------------------------------------------------------------- + +namespace { + +bsl::shared_ptr makeInterface( + bsl::allocator allocator = bsl::allocator()) +{ + bsl::shared_ptr blobBufferFactory = + bsl::allocate_shared(allocator, + 0xFFFF); + + return ntcf::System::createInterface( + ntca::InterfaceConfig(), + bslmf::MovableRefUtil::move(blobBufferFactory), + bslma::AllocatorUtil::adapt(allocator)); +} + +} // close unnamed namespace + +// ============================================================================ +// TESTS +// ---------------------------------------------------------------------------- + +class NtcCertificateLoaderTest : public bmqtst::Test { + protected: + bsl::shared_ptr d_interface; + + NtcCertificateLoaderTest() + : d_interface(makeInterface(bmqtst::TestHelperUtil::allocator())) + { + } +}; + +BMQTST_TEST_F(NtcCertificateLoaderTest, BreathingTest) +{ + NtcCertificateLoader certLoader(d_interface); + CertificateLoader* parent = &certLoader; + (void)parent; +} + +class CertificateStoreTest : protected bmqtst::Test { + public: + CertificateStoreTest() {} +}; + +BMQTST_TEST_F(CertificateStoreTest, BreathingTest) +{ + CertificateStore store; + BMQTST_ASSERT(!store.certificate()); + BMQTST_ASSERT(!store.certificateAuthority()); + BMQTST_ASSERT(!store.key()); +} + +class MockCertificateLoader : public CertificateLoader { + public: + MOCK_METHOD((bmqvt::ValueOrError), + loadCertificateFromPath, + (const bsl::string& path)); + + MOCK_METHOD((bmqvt::ValueOrError), + loadKeyFromPath, + (const bsl::string& path)); +}; + +BMQTST_TEST_F(CertificateStoreTest, CertificateLoad) +{ + using namespace ::testing; + + typedef bmqvt::ValueOrError + Expected; + + Expected result(bmqtst::TestHelperUtil::allocator()); + result.makeError(); + MockCertificateLoader loader; + EXPECT_CALL(loader, loadCertificateFromPath(_)) + .WillRepeatedly(Return(result)); + + CertificateStore store; + bsl::string certPath("fake.crt", bmqtst::TestHelperUtil::allocator()); + int rc = store.loadCertificate(loader, certPath); + BMQTST_ASSERT_EQ(0, rc); +} + +// ======================================================================== +// MAIN +// ------------------------------------------------------------------------ + +int main(int argc, char* argv[]) +{ + TEST_PROLOG(bmqtst::TestHelper::e_DEFAULT); + + bmqtst::runTest(_testCase); + + // gmock uses the default allocator, so we only check global allocator + // usage + TEST_EPILOG(bmqtst::TestHelper::e_CHECK_GBL_ALLOC); +} diff --git a/src/groups/bmq/bmqio/bmqio_ntcchannel.cpp b/src/groups/bmq/bmqio/bmqio_ntcchannel.cpp index 70991db4f..e42eb435a 100644 --- a/src/groups/bmq/bmqio/bmqio_ntcchannel.cpp +++ b/src/groups/bmq/bmqio/bmqio_ntcchannel.cpp @@ -18,9 +18,13 @@ #include +#include #include // NTF +#include +#include +#include #include // BDE @@ -32,8 +36,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -521,6 +528,8 @@ bool NtcReadQueue::empty() const // class NtcChannel // ---------------- +// PRIVATE ACCESSORS + // PRIVATE MANIPULATORS void NtcChannel::processConnect( const bsl::shared_ptr& connector, @@ -548,6 +557,12 @@ void NtcChannel::processConnect( d_peerUri = d_streamSocket_sp->remoteEndpoint().text(); + if (d_encryptionClient_sp) { + this->upgrade(d_encryptionClient_sp, + ntca::UpgradeOptions(), + d_upgradeCallback); + } + lock.release()->unlock(); if (resultCallback) { @@ -962,6 +977,32 @@ NtcChannel::NtcChannel( , d_watermarkSignaler(basicAllocator) , d_closeSignaler(basicAllocator) , d_resultCallback(bsl::allocator_arg, basicAllocator, resultCallback) +, d_encryptionClient_sp() +, d_upgradeCallback(bsl::allocator_arg, basicAllocator) +, d_allocator_p(bslma::Default::allocator(basicAllocator)) +{ +} + +NtcChannel::NtcChannel( + const bsl::shared_ptr& interface, + const bmqio::ChannelFactory::ResultCallback& resultCallback, + const ntci::UpgradeFunction& upgradeCallback, + bslma::Allocator* basicAllocator) +: d_mutex() +, d_interface_sp(interface) +, d_streamSocket_sp() +, d_readQueue(basicAllocator) +, d_readCache(basicAllocator) +, d_channelId(0) +, d_peerUri(basicAllocator) +, d_state(e_STATE_DEFAULT) +, d_options(basicAllocator) +, d_properties(basicAllocator) +, d_watermarkSignaler(basicAllocator) +, d_closeSignaler(basicAllocator) +, d_resultCallback(bsl::allocator_arg, basicAllocator, resultCallback) +, d_encryptionClient_sp() +, d_upgradeCallback(bsl::allocator_arg, basicAllocator, upgradeCallback) , d_allocator_p(bslma::Default::allocator(basicAllocator)) { } @@ -1367,6 +1408,62 @@ void NtcChannel::setWriteQueueHighWatermark(int highWatermark) } } +void NtcChannel::processUpgrade( + const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& upgradeEvent, + const ntci::UpgradeFunction& cb) +{ + BALL_LOG_DEBUG << "Received upgrade event: " << upgradeEvent; + if (upgradeEvent.type() == ntca::UpgradeEventType::e_COMPLETE) { + if (cb) { + cb(upgradable, upgradeEvent); + } + } +} + +void NtcChannel::upgrade( + const bsl::shared_ptr& encryptionServer, + const ntca::UpgradeOptions& options, + const ntci::UpgradeFunction& upgradeCallback) +{ + BSLS_ASSERT(encryptionServer); + + d_streamSocket_sp->upgrade( + encryptionServer, + options, + d_streamSocket_sp->createUpgradeCallback( + bdlf::BindUtil::bindS(d_allocator_p, + &NtcChannel::processUpgrade, + this, + bdlf::PlaceHolders::_1, + bdlf::PlaceHolders::_2, + upgradeCallback), + d_allocator_p)); +} + +void NtcChannel::upgrade( + const bsl::shared_ptr& encryptionClient, + const ntca::UpgradeOptions& options, + const ntci::UpgradeFunction& upgradeCallback) +{ + BSLS_ASSERT(encryptionClient); + BSLS_ASSERT(d_streamSocket_sp); + + BALL_LOG_INFO << "Upgrading connection"; + + d_streamSocket_sp->upgrade( + encryptionClient, + options, + d_streamSocket_sp->createUpgradeCallback( + bdlf::BindUtil::bindS(d_allocator_p, + &NtcChannel::processUpgrade, + this, + bdlf::PlaceHolders::_1, + bdlf::PlaceHolders::_2, + upgradeCallback), + d_allocator_p)); +} + // ACCESSORS int NtcChannel::channelId() const { @@ -1513,6 +1610,37 @@ void NtcListener::processAccept( } } +void NtcListener::processUpgrade( + const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& upgradeEvent) +{ + if (upgradeEvent.type() == ntca::UpgradeEventType::e_COMPLETE) { + if (d_upgradeCallback) { + d_upgradeCallback(upgradable, upgradeEvent); + } + } + else { + bslmt::LockGuard lock(&d_mutex); + bsl::shared_ptr self = this->shared_from_this(); + + BMQIO_NTCLISTENER_LOG_ACCEPT_FAILED(this, + d_listenerSocket_sp, + upgradeEvent); + bmqio::Status status; + NtcChannelUtil::fail(&status, + bmqio::StatusCategory::e_CONNECTION, + "upgrade", + upgradeEvent.context().error()); + + BMQIO_NTCLISTENER_LOG_CLOSING(this, d_listenerSocket_sp); + + d_state = e_STATE_CLOSING; + + d_listenerSocket_sp->close( + bdlf::BindUtil::bind(&NtcListener::processClose, self, status)); + } +} + void NtcListener::processClose(const bmqio::Status& status) { bslmt::LockGuard lock(&d_mutex); @@ -1550,6 +1678,7 @@ NtcListener::NtcListener( , d_properties(basicAllocator) , d_closeSignaler(basicAllocator) , d_resultCallback(bsl::allocator_arg, basicAllocator, resultCallback) +, d_encryptionServer_sp() , d_allocator_p(bslma::Default::allocator(basicAllocator)) { } diff --git a/src/groups/bmq/bmqio/bmqio_ntcchannel.h b/src/groups/bmq/bmqio/bmqio_ntcchannel.h index b0ca02a63..c9447c7c6 100644 --- a/src/groups/bmq/bmqio/bmqio_ntcchannel.h +++ b/src/groups/bmq/bmqio/bmqio_ntcchannel.h @@ -34,6 +34,7 @@ #include // NTC +#include #include #include @@ -46,11 +47,15 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include namespace BloombergLP { namespace bmqio { @@ -210,7 +215,9 @@ class NtcChannel : public bmqio::Channel, bdlmt::Signaler d_watermarkSignaler; bdlmt::Signaler d_closeSignaler; bmqio::ChannelFactory::ResultCallback d_resultCallback; - bslma::Allocator* d_allocator_p; + bsl::shared_ptr d_encryptionClient_sp; + ntci::UpgradeFunction d_upgradeCallback; + bslma::Allocator* d_allocator_p; private: // NOT IMPLEMENTED @@ -218,6 +225,8 @@ class NtcChannel : public bmqio::Channel, NtcChannel& operator=(const NtcChannel&) BSLS_KEYWORD_DELETED; private: + // PRIVATE ACCESSORS + // PRIVATE MANIPULATORS /// Process the connection by the specified `connector` according to @@ -283,6 +292,11 @@ class NtcChannel : public bmqio::Channel, /// Process the closure of the socket. void processClose(const bmqio::Status& status); + /// @brief Process the upgradeServer of this socket to TLS + void processUpgrade(const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& upgradeEvent, + const ntci::UpgradeFunction& cb); + public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(NtcChannel, bslma::UsesBslmaAllocator) @@ -298,6 +312,16 @@ class NtcChannel : public bmqio::Channel, const bmqio::ChannelFactory::ResultCallback& resultCallback, bslma::Allocator* basicAllocator = 0); + /// Create a new channel implemented by the specified `interface`. + /// Optionally specify a `basicAllocator` used to supply memory. Initialize + /// this channel's upgrade callback with `upgradeCallback`. If + /// 'basicAllocator is 0, the currently installed default allocator is + /// used. + NtcChannel(const bsl::shared_ptr& interface, + const bmqio::ChannelFactory::ResultCallback& resultCallback, + const ntci::UpgradeFunction& upgradeCallback, + bslma::Allocator* basicAllocator = 0); + /// Destroy this object. ~NtcChannel() BSLS_KEYWORD_OVERRIDE; @@ -407,6 +431,22 @@ class NtcChannel : public bmqio::Channel, /// Set the write queue high watermark to the specified `highWatermark`. void setWriteQueueHighWatermark(int highWatermark); + /// Assume the TLS server role and begin upgrading the socket from + /// being unencrypted to being encrypted with TLS. Invoke the specified + /// `upgradeCallback` when the socket has completed upgrading to TLS. + void + upgrade(const bsl::shared_ptr& encryptionServer, + const ntca::UpgradeOptions& options, + const ntci::UpgradeFunction& upgradeCallback); + + /// Assume the TLS client role and begin upgrading the socket from + /// being unencrypted to being encrypted with TLS. Invoke the specified + /// `upgradeCallback` when the socket has completed upgrading to TLS. + void + upgrade(const bsl::shared_ptr& encryptionClient, + const ntca::UpgradeOptions& options, + const ntci::UpgradeFunction& upgradeCallback); + // ACCESSORS /// Return the channel ID. @@ -442,6 +482,16 @@ class NtcChannel : public bmqio::Channel, struct NtcChannelUtil { // CLASS METHODS + /// \brief Return a reference providing const access to the name of the + /// property used to define the encryption client for liseners. + /// This property must be a `bsl::shared_ptr`. + static bsl::string_view encryptionClientProperty(); + + /// \brief Return a reference providing const access to the name of the + /// property used to define the callback on a TLS upgrade. + /// This property must be a `bsl::shared_ptr`. + static bsl::string_view upgradeCallbackProperty(); + /// Load into the specified `status`, if defined, the description of /// the specified `error` assigned to the specified `category` that /// was detected when performing the specified `operation`. @@ -488,7 +538,9 @@ class NtcListener : public bmqio::ChannelFactoryOperationHandle, bmqvt::PropertyBag d_properties; bdlmt::Signaler d_closeSignaler; bmqio::ChannelFactory::ResultCallback d_resultCallback; - bslma::Allocator* d_allocator_p; + bsl::shared_ptr d_encryptionServer_sp; + ntci::UpgradeFunction d_upgradeCallback; + bslma::Allocator* d_allocator_p; private: // NOT IMPLEMENTED @@ -507,6 +559,10 @@ class NtcListener : public bmqio::ChannelFactoryOperationHandle, /// Process the closure of the socket. void processClose(const bmqio::Status& status); + /// Process the upgrade of a socket + void processUpgrade(const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& upgradeEvent); + public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(NtcListener, bslma::UsesBslmaAllocator) @@ -518,10 +574,9 @@ class NtcListener : public bmqio::ChannelFactoryOperationHandle, /// Optionally specified a `basicAllocator` used to supply memory. If /// `basicAllocator` is 0, the currently installed default allocator is /// used. - explicit NtcListener( - const bsl::shared_ptr& interface, - const bmqio::ChannelFactory::ResultCallback& resultCallback, - bslma::Allocator* basicAllocator = 0); + NtcListener(const bsl::shared_ptr& interface, + const bmqio::ChannelFactory::ResultCallback& resultCallback, + bslma::Allocator* basicAllocator = 0); /// Destroy this object. ~NtcListener() BSLS_KEYWORD_OVERRIDE; @@ -586,6 +641,16 @@ struct NtcListenerUtil { /// port to which the listening socket is bound. static bslstl::StringRef listenPortProperty(); + /// \brief Return a reference providing const access to the name of the + /// property used to define the encryption server for liseners. + /// This property must be a `bsl::shared_ptr`. + static bsl::string_view encryptionServerProperty(); + + /// \brief Return a reference providing const access to the name of the + /// property used to define the callback on a TLS upgrade. + /// This property must be a `bsl::shared_ptr`. + static bsl::string_view upgradeCallbackProperty(); + /// Load into the specified `status`, if defined, the description of /// the specified `error` assigned to the specified `category` that /// was detected when performing the specified `operation`. diff --git a/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.cpp b/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.cpp index 6a2ea6977..dd1ce0a5c 100644 --- a/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.cpp +++ b/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.cpp @@ -17,8 +17,13 @@ #include #include + +// BMQ +#include + // NTF #include +#include #include // BDE @@ -178,6 +183,15 @@ void NtcChannelFactory::processChannelClosed(int handle) } } +void NtcChannelFactory::processUpgrade( + const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& event, + const UpgradeCallback& onUpgrade) +{ + bslmt::LockGuard lock(&d_stateMutex); // LOCKED + onUpgrade(upgradable, event); +} + // CREATORS NtcChannelFactory::NtcChannelFactory( @@ -237,7 +251,6 @@ NtcChannelFactory::~NtcChannelFactory() BSLS_ASSERT_OPT(d_channels.length() == 0); BSLS_ASSERT_OPT(d_createSignaler.slotCount() == 0); BSLS_ASSERT_OPT(d_limitSignaler.slotCount() == 0); - BSLS_ASSERT_OPT(!d_interface_sp); } // MANIPULATORS @@ -456,6 +469,29 @@ int NtcChannelFactory::lookupChannel( return d_channels.find(channelId, result); } +ntsa::Error NtcChannelFactory::createEncryptionServer( + bsl::shared_ptr* result, + const ntca::EncryptionServerOptions& options) +{ + return d_interface_sp->createEncryptionServer(result, + options, + d_allocator_p); +} + +ntsa::Error NtcChannelFactory::createEncryptionClient( + bsl::shared_ptr* result, + const ntca::EncryptionClientOptions& options) +{ + return d_interface_sp->createEncryptionClient(result, + options, + d_allocator_p); +} + +NtcCertificateLoader NtcChannelFactory::createCertificateLoader() +{ + return NtcCertificateLoader(d_interface_sp); +} + // ---------------------------- // struct NtcChannelFactoryUtil // ---------------------------- diff --git a/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.h b/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.h index b5bcd1687..69c2373e6 100644 --- a/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.h +++ b/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.h @@ -29,6 +29,7 @@ // that implements the 'bmqio::ChannelFactory' protocol to produce and manage // 'bmqio::NtcChannel' objects that implement the 'bmqio::Channel' protocol. +#include #include #include #include @@ -37,6 +38,7 @@ // NTF #include #include +#include // BDE #include @@ -77,6 +79,8 @@ class NtcChannelFactory : public bmqio::ChannelFactory { /// been reached. typedef bsl::function LimitFn; + typedef ntci::UpgradeFunction UpgradeCallback; + private: // PRIVATE TYPES @@ -150,6 +154,17 @@ class NtcChannelFactory : public bmqio::ChannelFactory { /// `handle`. void processChannelClosed(int handle); + /// Process a TLS upgrade + void processUpgrade(const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& event, + const UpgradeCallback& callback); + + /// Upgrade the channel to a TLS connection as a listener. + void upgradeListener(bmqio::NtcChannel* channel); + + /// Upgrade the channel to a TLS connection as a client. + void upgradeChannel(bmqio::NtcChannel* channel); + public: // PUBLIC TYPES @@ -245,6 +260,29 @@ class NtcChannelFactory : public bmqio::ChannelFactory { /// during the execution of this function will be included. template void visitChannels(VISITOR& visitor); + + /// @brief Create an encryption server using this channel factory's + /// interface. + /// + /// Load into the specified `result` a new encryption server with the + /// specified `options`. Optionally specify a `basicAllocator` used to + /// supply memory. Return the error. + ntsa::Error + createEncryptionServer(bsl::shared_ptr* result, + const ntca::EncryptionServerOptions& options); + + /// @brief Create an encryption server using this channel factory's + /// interface. + /// + /// Load into the specified `result` a new encryption client with the + /// specified `options`. Optionally specify a `basicAllocator` used to + /// supply memory. Return the error. + ntsa::Error + createEncryptionClient(bsl::shared_ptr* result, + const ntca::EncryptionClientOptions& options); + + /// @brief Create a certificate loader based on the underlying interface. + NtcCertificateLoader createCertificateLoader(); }; // ============================ diff --git a/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.t.cpp b/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.t.cpp index b25f1a5a1..685c6e6ec 100644 --- a/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.t.cpp +++ b/src/groups/bmq/bmqio/bmqio_ntcchannelfactory.t.cpp @@ -16,9 +16,18 @@ // bmqio_ntcchannelfactory.t.cpp -*-C++-*- #include +#include #include +#include +#include #include +#include +#include +#include +#include +#include +#include #include #include @@ -26,18 +35,27 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include +#include #include #include #include #include +#include #include +#include // CONVENIENCE using namespace BloombergLP; @@ -80,6 +98,131 @@ static bool ballFilter(const bsl::string& messageSubstring, .isEmpty(); } +namespace { + +typedef bsl::pair, + bsl::shared_ptr > + EncryptionPair; + +/// Return an encryption server started with fake TLS certificate configuration +EncryptionPair makeEncryption(ntci::Interface* interface, + bslma::Allocator* allocator = 0) +{ + ntsa::Error error; + + // Generate the certificates and private keys of the server and the + // certificate authority (CA) that issues the server's certificate. In + // a production scenario, the CA certificate, server certificate, and + // server private key are typically loaded from a secure location on disk. + + bsl::shared_ptr authorityCertificate; + bsl::shared_ptr authorityPrivateKey; + + bsl::shared_ptr serverCertificate; + bsl::shared_ptr serverPrivateKey; + + { + // Generate the certificate and private key of a trusted authority. + + ntsa::DistinguishedName authorityIdentity(allocator); + authorityIdentity["CN"] = "Authority"; + authorityIdentity["O"] = "Bloomberg LP"; + + error = interface->generateKey(&authorityPrivateKey, + ntca::EncryptionKeyOptions(), + allocator); + BMQTST_ASSERT(!error); + + ntca::EncryptionCertificateOptions authorityCertificateOptions; + authorityCertificateOptions.setAuthority(true); + + error = interface->generateCertificate(&authorityCertificate, + authorityIdentity, + authorityPrivateKey, + authorityCertificateOptions, + allocator); + BMQTST_ASSERT(!error); + + // Generate the certificate and private key of the server, signed + // by the trusted authority. + + ntsa::DistinguishedName serverIdentity(allocator); + serverIdentity["CN"] = "Server"; + serverIdentity["O"] = "Bloomberg LP"; + + error = interface->generateKey(&serverPrivateKey, + ntca::EncryptionKeyOptions(), + allocator); + BMQTST_ASSERT(!error); + + error = interface->generateCertificate( + &serverCertificate, + serverIdentity, + serverPrivateKey, + authorityCertificate, + authorityPrivateKey, + ntca::EncryptionCertificateOptions(), + allocator); + BMQTST_ASSERT(!error); + } + + // Create an encryption server and configure the encryption server to + // accept upgrades to TLS 1.3 and higher, to cryptographically identify + // itself using the server certificate previously generated, to encrypt + // data using the server private key previously generated, and to not + // require identification from the client. + + ntca::EncryptionServerOptions encryptionServerOptions; + encryptionServerOptions.setMinMethod(ntca::EncryptionMethod::e_TLS_V1_3); + encryptionServerOptions.setMaxMethod(ntca::EncryptionMethod::e_TLS_V1_3); + encryptionServerOptions.setAuthentication( + ntca::EncryptionAuthentication::e_NONE); + + { + bsl::vector identityData(allocator); + serverCertificate->encode(&identityData); + encryptionServerOptions.setIdentityData(identityData); + } + + { + bsl::vector privateKeyData(allocator); + serverPrivateKey->encode(&privateKeyData); + encryptionServerOptions.setPrivateKeyData(privateKeyData); + } + + bsl::shared_ptr encryptionServer; + error = interface->createEncryptionServer(&encryptionServer, + encryptionServerOptions, + allocator); + BSLS_ASSERT_OPT(!error); + + // Create an encryption client and configure the encryption client to + // request upgrades using TLS 1.2 require identification from the server, + // and to trust the certificate authority previously generated to verify + // the authenticity of the server. + + ntca::EncryptionClientOptions encryptionClientOptions; + encryptionClientOptions.setMinMethod(ntca::EncryptionMethod::e_TLS_V1_3); + encryptionClientOptions.setMaxMethod(ntca::EncryptionMethod::e_TLS_V1_3); + encryptionClientOptions.setAuthentication( + ntca::EncryptionAuthentication::e_VERIFY); + + { + bsl::vector authorityData(allocator); + authorityCertificate->encode(&authorityData); + encryptionClientOptions.addAuthorityData(authorityData); + } + + bsl::shared_ptr encryptionClient; + error = interface->createEncryptionClient(&encryptionClient, + encryptionClientOptions, + allocator); + BSLS_ASSERT_OPT(!error); + + return bsl::make_pair(encryptionServer, encryptionClient); +} +} + // CONSTANTS static const bslstl::StringRef k_BALL_OBSERVER_NAME = "testDriverObserver"; static const ChannelFactoryEvent::Enum CFE_CHANNEL_UP = @@ -183,6 +326,38 @@ struct Tester_ResultCbInfo { } }; +// ========================== +// class Tester_UpgradeCbInfo +// ========================== + +/// Arguments used in a call to a ChannelFactory::ResultCallback. +struct Tester_UpgradeCbInfo { + // DATA + bsl::shared_ptr d_upgradable; + ntca::UpgradeEvent d_event; + + // TRAITS + BSLMF_NESTED_TRAIT_DECLARATION(Tester_UpgradeCbInfo, + bslma::UsesBslmaAllocator) + + // CREATORS + Tester_UpgradeCbInfo(bslma::Allocator* basicAllocator = 0) + : d_upgradable() + , d_event(basicAllocator) + { + // NOTHING + } + + Tester_UpgradeCbInfo( + BSLA_MAYBE_UNUSED const Tester_UpgradeCbInfo& original, + bslma::Allocator* basicAllocator = 0) + : d_upgradable() + , d_event(basicAllocator) + { + // NOTHING + } +}; + // ======================= // class Tester_HandleInfo // ======================= @@ -193,6 +368,7 @@ struct Tester_HandleInfo { bsl::shared_ptr d_handle; bsl::deque d_resultCbCalls; int d_listenPort; + bsl::deque d_upgradeCbCalls; // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(Tester_HandleInfo, @@ -203,6 +379,7 @@ struct Tester_HandleInfo { : d_handle() , d_resultCbCalls(basicAllocator) , d_listenPort(-1) + , d_upgradeCbCalls(basicAllocator) { // NOTHING } @@ -212,6 +389,7 @@ struct Tester_HandleInfo { : d_handle(original.d_handle) , d_resultCbCalls(original.d_resultCbCalls, basicAllocator) , d_listenPort(original.d_listenPort) + , d_upgradeCbCalls(original.d_upgradeCbCalls, basicAllocator) { // NOTHING } @@ -235,6 +413,14 @@ struct Tester_ChannelVisitor { void operator()(const bsl::shared_ptr&) { ++d_numChannels; } }; +// =========================== +// struct Tester_Options +// =========================== + +struct Tester_ProtocolOptions { + enum Enum { k_PLAINTEXT, k_TLS }; +}; + // ============ // class Tester // ============ @@ -251,21 +437,31 @@ class Tester { typedef Tester_ChannelInfo ChannelInfo; typedef Tester_HandleInfo HandleInfo; typedef Tester_ResultCbInfo ResultCbInfo; + typedef Tester_UpgradeCbInfo UpgradeCbInfo; typedef bsl::map ChannelMap; typedef bsl::map HandleMap; typedef bsl::shared_ptr NtcChannelPtr; typedef bsl::deque PreCreateCbCallList; + public: + // TYPES + typedef Tester_ProtocolOptions ProtocolOptions; + + private: // DATA - bslma::Allocator* d_allocator_p; - bdlbb::PooledBlobBufferFactory d_blobBufferFactory; - bsl::shared_ptr d_ballObserver; - ChannelMap d_channelMap; - HandleMap d_handleMap; - PreCreateCbCallList d_preCreateCbCalls; - bool d_setPreCreateCb; - bslma::ManagedPtr d_object; - bslmt::Mutex d_mutex; + bslma::Allocator* d_allocator_p; + bdlbb::PooledBlobBufferFactory d_blobBufferFactory; + bsl::shared_ptr d_ballObserver; + ChannelMap d_channelMap; + HandleMap d_handleMap; + PreCreateCbCallList d_preCreateCbCalls; + bool d_setPreCreateCb; + bslma::ManagedPtr d_object; + bslmt::Mutex d_mutex; + bsl::shared_ptr d_interface; + bool d_setTls; + bsl::shared_ptr d_encryptionServer; + bsl::shared_ptr d_encryptionClient; // PRIVATE MANIPULATORS @@ -273,7 +469,13 @@ class Tester { void resultCb(const bsl::string& handleName, ChannelFactoryEvent::Enum event, const Status& status, - const bsl::shared_ptr& channel); + const bsl::shared_ptr& channel, + ProtocolOptions::Enum testOptions); + + /// UpgradeCb passed to `upgrade` + void upgradeCb(const bsl::string& handleName, + const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& event); /// `channelPreCreationCb` passed to the NtcChannelFactory, if we're /// asked to pass one @@ -301,6 +503,9 @@ class Tester { /// Destroy the object being tested and reset all supporting objects. void destroy(); + void upgrade(const bsl::string& handleName, + const bsl::shared_ptr& channel); + // NOT IMPLEMENTED Tester(const Tester&); Tester& operator=(const Tester&); @@ -320,6 +525,11 @@ class Tester { /// this is `false`. void setPreCreateCb(bool value); + /// Set whether the socket will use TLS on the `NtcChannelFactory` + /// the next time `init` is called to the specified `value`. By default + /// this is `false`. + void setTls(bool value); + /// (Re-)create the object being tested and reset the state of any /// supporting objects. void init(int line); @@ -330,7 +540,8 @@ class Tester { void listen(int line, const bslstl::StringRef& handleName, const bslstl::StringRef& endpoint, - StatusCategory::Enum resultStatus = StatusCategory::e_SUCCESS); + StatusCategory::Enum resultStatus = StatusCategory::e_SUCCESS, + ProtocolOptions::Enum options = ProtocolOptions::k_PLAINTEXT); /// Connect to the specified `endpointOrServer` and assign the /// resulting handle to the specified `handleName`, verifying that the @@ -345,12 +556,14 @@ class Tester { const bslstl::StringRef& handleName, const bslstl::StringRef& endpointOrServer, const bmqio::ConnectOptions& options, - StatusCategory::Enum resultStatus = StatusCategory::e_SUCCESS); + StatusCategory::Enum resultStatus = StatusCategory::e_SUCCESS, + ProtocolOptions::Enum testOptions = ProtocolOptions::k_PLAINTEXT); void connect(int line, const bslstl::StringRef& handleName, const bslstl::StringRef& endpointOrServer, - StatusCategory::Enum resultStatus = StatusCategory::e_SUCCESS); + StatusCategory::Enum resultStatus = StatusCategory::e_SUCCESS, + ProtocolOptions::Enum testOptions = ProtocolOptions::k_PLAINTEXT); /// Call `visitChannels` and verify that the visitor was invoked with /// the specified `numChannels` channels. @@ -420,6 +633,11 @@ class Tester { /// few ms before doing the check. void checkNoResultCallback(int line, const bslstl::StringRef& handleName); + /// Make sure there are no unchecked calls to the UpgradeCb associated + /// with the specified `handleName`. This function will wait for a + /// few ms before doing the check. + void checkNoUpgradeCallback(int line, const bslstl::StringRef& handleName); + /// Check whether the channel with the specified `channelName` has been /// closed or not (depending on the specified `closed`). This function /// will wait for up to 5s for the call to be received before failing @@ -461,6 +679,13 @@ class Tester { /// specified `expected`. void checkNumPreCreateCbCalls(int line, int expected); + /// Check the oldest unchecked call to the UpgradeCallback associated + /// with the handle with the specified `handleName`, and verify that + /// it's a successful `e_COMPLETE` event, and assign the channel the + /// specified `channelName`. This function will wait for up to 5s for + /// the call to be received before failing the check. + void checkUpgradeCallback(int line, const bslstl::StringRef& handleName); + /// Return a reference providing modifiable access to the object being /// tested. NtcChannelFactory& object(); @@ -474,7 +699,8 @@ class Tester { void Tester::resultCb(const bsl::string& handleName, ChannelFactoryEvent::Enum event, const Status& status, - const bsl::shared_ptr& channel) + const bsl::shared_ptr& channel, + ProtocolOptions::Enum testOptions) { bslmt::LockGuard guard(&d_mutex); // LOCK HandleInfo& info = d_handleMap[handleName]; @@ -484,6 +710,54 @@ void Tester::resultCb(const bsl::string& handleName, cbInfo.d_event = event; cbInfo.d_status = status; cbInfo.d_channel = channel; + + if (testOptions == ProtocolOptions::k_TLS) { + upgrade(handleName, channel); + } +} + +void Tester::upgrade(const bsl::string& handleName, + const bsl::shared_ptr& channel) +{ + HandleInfo& info = d_handleMap[handleName]; + + // Set TLS properties + NtcChannelFactory::UpgradeCallback upgradeCb( + bsl::allocator_arg, + d_allocator_p, + bdlf::BindUtil::bindS(d_allocator_p, + &Tester::upgradeCb, + this, + handleName, + bdlf::PlaceHolders::_1, + bdlf::PlaceHolders::_2)); + + bsl::shared_ptr alias = + bsl::dynamic_pointer_cast(channel); + + BMQTST_ASSERT_D("Attempted to upgrade a non ntc channel", alias); + + if (info.d_listenPort > 0) { + // Listener + alias->upgrade(d_encryptionServer, ntca::UpgradeOptions(), upgradeCb); + } + else { + // Client + alias->upgrade(d_encryptionClient, ntca::UpgradeOptions(), upgradeCb); + } +} + +void Tester::upgradeCb(const bsl::string& handleName, + const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& event) +{ + bslmt::LockGuard guard(&d_mutex); // LOCK + HandleInfo& info = d_handleMap[handleName]; + + info.d_upgradeCbCalls.emplace_back(); + UpgradeCbInfo& cbInfo = info.d_upgradeCbCalls.back(); + cbInfo.d_upgradable = upgradable; + cbInfo.d_event = event; } void Tester::preCreationCb( @@ -539,6 +813,9 @@ void Tester::channelWatermarkCb(const bsl::string& channelName, void Tester::destroy() { + d_encryptionClient.reset(); + d_encryptionServer.reset(); + d_interface.reset(); d_preCreateCbCalls.clear(); if (d_object) { @@ -564,6 +841,10 @@ Tester::Tester(bslma::Allocator* basicAllocator) , d_setPreCreateCb(false) , d_object() , d_mutex() +, d_interface() +, d_setTls(false) +, d_encryptionServer() +, d_encryptionClient() { } @@ -578,6 +859,11 @@ void Tester::setPreCreateCb(bool value) d_setPreCreateCb = value; } +void Tester::setTls(bool value) +{ + d_setTls = value; +} + void Tester::init(int line) { destroy(); @@ -585,9 +871,17 @@ void Tester::init(int line) ntca::InterfaceConfig interfaceConfig; interfaceConfig.setThreadName("test"); - d_object.load(new (*d_allocator_p) NtcChannelFactory(interfaceConfig, - &d_blobBufferFactory, - d_allocator_p), + bsl::shared_ptr blobBufferFactory_sp( + &d_blobBufferFactory, + bslstl::SharedPtrNilDeleter(), + d_allocator_p); + + d_interface = ntcf::System::createInterface(interfaceConfig, + blobBufferFactory_sp, + d_allocator_p); + + d_object.load(new (*d_allocator_p) + NtcChannelFactory(d_interface, d_allocator_p), d_allocator_p); if (d_setPreCreateCb) { @@ -599,12 +893,17 @@ void Tester::init(int line) int ret = d_object->start(); BMQTST_ASSERT_EQ_D(line, ret, 0); + + bdlb::PairUtil::tie(d_encryptionServer, + d_encryptionClient) = makeEncryption(d_interface.get(), + d_allocator_p); } void Tester::listen(int line, const bslstl::StringRef& handleName, const bslstl::StringRef& endpoint, - StatusCategory::Enum resultStatus) + StatusCategory::Enum resultStatus, + ProtocolOptions::Enum testOptions) { bsl::string handleNameStr(handleName); @@ -626,7 +925,8 @@ void Tester::listen(int line, handleNameStr, bdlf::PlaceHolders::_1, bdlf::PlaceHolders::_2, - bdlf::PlaceHolders::_3)); + bdlf::PlaceHolders::_3, + testOptions)); Status status; d_object->listen(&status, &opHandle, options, resultCb); @@ -650,7 +950,8 @@ void Tester::connect(int line, const bslstl::StringRef& handleName, const bslstl::StringRef& endpointOrServer, const bmqio::ConnectOptions& options, - StatusCategory::Enum resultStatus) + StatusCategory::Enum resultStatus, + ProtocolOptions::Enum testOptions) { bsl::string handleNameStr(handleName, bmqtst::TestHelperUtil::allocator()); @@ -685,7 +986,8 @@ void Tester::connect(int line, handleNameStr, bdlf::PlaceHolders::_1, bdlf::PlaceHolders::_2, - bdlf::PlaceHolders::_3)); + bdlf::PlaceHolders::_3, + testOptions)); Status status; d_object->connect(&status, &opHandle, reqOptions, resultCb); @@ -701,13 +1003,15 @@ void Tester::connect(int line, void Tester::connect(int line, const bslstl::StringRef& handleName, const bslstl::StringRef& endpointOrServer, - StatusCategory::Enum resultStatus) + StatusCategory::Enum resultStatus, + ProtocolOptions::Enum testOptions) { connect(line, handleName, endpointOrServer, bmqio::ConnectOptions(), - resultStatus); + resultStatus, + testOptions); } void Tester::callVisitChannels(int line, int numChannels) @@ -932,6 +1236,16 @@ void Tester::checkNoResultCallback(int line, BMQTST_ASSERT_D(line, info.d_resultCbCalls.empty()); } +void Tester::checkNoUpgradeCallback(int line, + const bslstl::StringRef& handleName) +{ + bslmt::ThreadUtil::microSleep(5000); + + bslmt::LockGuard guard(&d_mutex); // LOCK + HandleInfo& info = d_handleMap[handleName]; + BMQTST_ASSERT_D(line, info.d_upgradeCbCalls.empty()); +} + void Tester::checkChannelClose(int line, const bslstl::StringRef& channelName, bool closed) @@ -1043,6 +1357,33 @@ void Tester::checkNumPreCreateCbCalls(int line, int expected) expected); } +void Tester::checkUpgradeCallback(int line, + const bslstl::StringRef& handleName) +{ + bslmt::LockGuard guard(&d_mutex); // LOCK + + bsls::Types::Int64 startTime = bsls::TimeUtil::getTimer(); + + HandleInfo& info = d_handleMap[handleName]; + + while (info.d_upgradeCbCalls.empty() && + (bsls::TimeUtil::getTimer() - startTime) < + 5 * bdlt::TimeUnitRatio::k_NS_PER_S) { + bslmt::LockGuardUnlock unlockGuard(&d_mutex); + bslmt::ThreadUtil::microSleep(1000); + } + + if (info.d_upgradeCbCalls.empty()) { + BMQTST_ASSERT_D("line: " << line << ", no upgrade events received", + false); + return; // RETURN + } + + UpgradeCbInfo& cbInfo = info.d_upgradeCbCalls.front(); + BMQTST_ASSERT_D(line, cbInfo.d_event.isComplete()); + info.d_upgradeCbCalls.pop_front(); +} + NtcChannelFactory& Tester::object() { return *d_object; @@ -1051,6 +1392,110 @@ NtcChannelFactory& Tester::object() // ============================================================================ // TESTS // ---------------------------------------------------------------------------- +static void test8_tlsClientFailsOnPlaintextServer() +// ------------------------------------------------------------------------ +// PRE CREATION CB TEST +// +// Concerns: +// a) An attempt to connect to a plaintext server with a TLS channel will fail +// b) An attempt to connect to a TLS server with a plaintext channel will fail +// c) A failed attempt to connect from a plaintext client doesn't cause a TLS +// listener to close +// ------------------------------------------------------------------------ +{ + bmqtst::TestHelper::printTestName("Upgrade Channel Cb Test"); + + Tester t(bmqtst::TestHelperUtil::allocator()); + + // Concern 'a' + t.init(L_); + + t.listen(L_, + "listenHandle0", + "127.0.0.1:5000", + StatusCategory::e_SUCCESS, + Tester::ProtocolOptions::k_PLAINTEXT); + t.connect(L_, + "connectHandle0", + "listenHandle0", + StatusCategory::e_SUCCESS, + Tester::ProtocolOptions::k_TLS); + + t.checkResultCallback(L_, "listenHandle0", "listenChannel0"); + t.checkResultCallback(L_, "connectHandle0", "connectChannel0"); + + t.checkNoUpgradeCallback(L_, "listenHandle0"); + t.checkNoUpgradeCallback(L_, "connectHandle0"); + + t.checkChannelClose(L_, "listenChannel0", false); + + // Concern 'b' + t.listen(L_, + "listenHandle1", + "127.0.0.1:5001", + StatusCategory::e_SUCCESS, + Tester::ProtocolOptions::k_TLS); + t.connect(L_, + "connectHandle1", + "listenHandle1", + StatusCategory::e_SUCCESS, + Tester::ProtocolOptions::k_PLAINTEXT); + + t.checkResultCallback(L_, "listenHandle1", "listenChannel1"); + t.checkResultCallback(L_, "connectHandle1", "connectChannel1"); + + t.checkNoUpgradeCallback(L_, "listenHandle1"); + t.checkNoUpgradeCallback(L_, "connectHandle1"); + + // Concern 'c' + t.checkChannelClose(L_, "listenChannel1", false); +} + +static void test7_upgradeChannelTest() +// ------------------------------------------------------------------------ +// PRE CREATION CB TEST +// +// Concerns: +// a) 'upgradeChannelCb' is called for every channel upgraded +// ------------------------------------------------------------------------ +{ + bmqtst::TestHelper::printTestName("Upgrade Channel Cb Test"); + + Tester t(bmqtst::TestHelperUtil::allocator()); + + // Concern 'a' + t.init(L_); + + // Create a TLS channel + t.listen(L_, + "listenHandle", + "127.0.0.1:5000", + StatusCategory::e_SUCCESS, + Tester::ProtocolOptions::k_TLS); + t.connect(L_, + "connectHandle", + "listenHandle", + StatusCategory::e_SUCCESS, + Tester::ProtocolOptions::k_TLS); + t.connect(L_, + "connectHandle2", + "listenHandle", + StatusCategory::e_SUCCESS, + Tester::ProtocolOptions::k_TLS); + + t.checkResultCallback(L_, "listenHandle", "listenChannel"); + t.checkResultCallback(L_, "connectHandle", "connectChannel"); + t.checkResultCallback(L_, "connectHandle2", "connectChannel2"); + + // Upgrade TLS connection + t.checkUpgradeCallback(L_, "listenHandle"); + t.checkUpgradeCallback(L_, "connectHandle"); + t.checkUpgradeCallback(L_, "connectHandle2"); + + t.writeChannel(L_, "listenChannel", "abcdef"); + t.readChannel(L_, "connectChannel", "abcdef"); +} + static void test6_preCreationCbTest() // ------------------------------------------------------------------------ // PRE CREATION CB TEST @@ -1250,7 +1695,7 @@ static void test1_breathingTest() t.init(L_); // Listen and connect work - t.listen(L_, "listenHandle", "127.0.0.1:0"); + t.listen(L_, "listenHandle", "127.0.0.1:5000"); t.connect(L_, "connectHandle", "listenHandle"); t.checkResultCallback(L_, "listenHandle", "listenChannel"); @@ -1287,7 +1732,8 @@ int main(int argc, char* argv[]) { TEST_PROLOG(bmqtst::TestHelper::e_DEFAULT); - // ntcf::System::initialize(); + bmqtst::TestHelperUtil::verbosityLevel() = 1; + ntcf::SystemGuard systemGuard(ntscfg::Signal::e_PIPE); switch (_testCase) { case 0: @@ -1297,13 +1743,13 @@ int main(int argc, char* argv[]) case 4: test4_cancelHandleTest(); break; case 5: test5_visitChannelsTest(); break; case 6: test6_preCreationCbTest(); break; + case 7: test7_upgradeChannelTest(); break; + case 8: test8_tlsClientFailsOnPlaintextServer(); break; default: { cerr << "WARNING: CASE '" << _testCase << "' NOT FOUND." << endl; bmqtst::TestHelperUtil::testStatus() = -1; } break; } - // ntcf::System::exit(); - TEST_EPILOG(0); } diff --git a/src/groups/bmq/bmqio/package/bmqio.mem b/src/groups/bmq/bmqio/package/bmqio.mem index 6aa0127bc..d9c300ac7 100644 --- a/src/groups/bmq/bmqio/package/bmqio.mem +++ b/src/groups/bmq/bmqio/package/bmqio.mem @@ -1,4 +1,5 @@ bmqio_basechannelpartialimp +bmqio_certificatestore bmqio_channel bmqio_channelfactory bmqio_channelutil diff --git a/src/groups/bmq/bmqsys/bmqsys_time.t.cpp b/src/groups/bmq/bmqsys/bmqsys_time.t.cpp index 72580a26d..b7530f394 100644 --- a/src/groups/bmq/bmqsys/bmqsys_time.t.cpp +++ b/src/groups/bmq/bmqsys/bmqsys_time.t.cpp @@ -424,6 +424,3 @@ int main(int argc, char* argv[]) TEST_EPILOG(bmqtst::TestHelper::e_CHECK_DEF_GBL_ALLOC); } - -// ---------------------------------------------------------------------------- -// NOTICE: \ No newline at end of file diff --git a/src/groups/bmq/bmqt/bmqt_sessionoptions.cpp b/src/groups/bmq/bmqt/bmqt_sessionoptions.cpp index e05312a3d..daf5e6a4c 100644 --- a/src/groups/bmq/bmqt/bmqt_sessionoptions.cpp +++ b/src/groups/bmq/bmqt/bmqt_sessionoptions.cpp @@ -18,12 +18,68 @@ #include // BDE +#include +#include #include #include +// BMQ +#include + namespace BloombergLP { namespace bmqt { +// ====================== +// struct ProtocolVersion +// ====================== + +bsl::ostream& ProtocolVersion::print(bsl::ostream& stream, + ProtocolVersion::Value value, + int level, + int spacesPerLevel) +{ + if (stream.bad()) { + return stream; // RETURN + } + + bslim::Printer printer(&stream, level, spacesPerLevel); + printer.start(); + printer.printValue(ProtocolVersion::toAscii(value)); + printer.end(); + + return stream; +} + +const char* ProtocolVersion::toAscii(ProtocolVersion::Value value) +{ + // Use openssl compatible string representation + switch (value) { + case e_TLS1_2: return "TLSv1.2"; + case e_TLS1_3: return "TLSv1.3"; + default: return "(* UNKNOWN *)"; + } +} + +bool ProtocolVersion::fromAscii(ProtocolVersion::Value* out, + const bslstl::StringRef& str) +{ +#define CHECKVALUE(M) \ + if (bdlb::String::areEqualCaseless(toAscii(ProtocolVersion::e_##M), \ + str.data(), \ + str.length())) { \ + *out = ProtocolVersion::e_##M; \ + return true; \ + } + + CHECKVALUE(TLS1_2); + CHECKVALUE(TLS1_3); + + // Invalid string + return false; + +#undef CHECKVALUE +} + // -------------------- // class SessionOptions // -------------------- @@ -45,6 +101,8 @@ SessionOptions::SessionOptions(bslma::Allocator* allocator) , d_eventQueueLowWatermark(50) , d_eventQueueHighWatermark(2 * 1000) , d_eventQueueSize(-1) // DEPRECATED: will be removed in future release +, d_certificateAuthority(allocator) +, d_protocolVersions(allocator) , d_hostHealthMonitor_sp(NULL) , d_dtContext_sp(NULL) , d_dtTracer_sp(NULL) @@ -68,6 +126,8 @@ SessionOptions::SessionOptions(const SessionOptions& other, , d_eventQueueLowWatermark(other.eventQueueLowWatermark()) , d_eventQueueHighWatermark(other.eventQueueHighWatermark()) , d_eventQueueSize(-1) // DEPRECATED: will be removed in future release +, d_certificateAuthority(other.certificateAuthority()) +, d_protocolVersions(other.protocolVersions()) , d_hostHealthMonitor_sp(other.hostHealthMonitor()) , d_dtContext_sp(other.traceContext()) , d_dtTracer_sp(other.tracer()) @@ -75,6 +135,37 @@ SessionOptions::SessionOptions(const SessionOptions& other, // NOTHING } +SessionOptions& +SessionOptions::setTlsDetails(const bslstl::StringRef& certificateAuthority, + const bslstl::StringRef& versions) +{ + // todo check file permissions? + // todo permissions cross platform? + d_certificateAuthority = certificateAuthority; + d_protocolVersions.clear(); + + bsl::vector vs = + bmqu::StringUtil::strTokenizeRef(versions, ", \t"); + for (size_t i = 0; i < vs.size(); i++) { + ProtocolVersion::Value version; + + if (ProtocolVersion::fromAscii(&version, vs[i])) { + d_protocolVersions.insert(version); + } + else { + // todo why do we use assertions everywhere here instead of + // exceptions? + BSLS_ASSERT_SAFE(false && "Unrecognized protocol version"); + } + } + + BSLS_ASSERT_SAFE( + bdls::FilesystemUtil::exists(certificateAuthority) && + "Certificate authority file doesn't exist on provided path"); + + return *this; +} + bsl::ostream& SessionOptions::print(bsl::ostream& stream, int level, int spacesPerLevel) const @@ -108,6 +199,8 @@ bsl::ostream& SessionOptions::print(bsl::ostream& stream, printer.printAttribute("hasHostHealthMonitor", d_hostHealthMonitor_sp != NULL); printer.printAttribute("hasDistributedTracing", d_dtTracer_sp != NULL); + printer.printAttribute("certificateAuthority", d_certificateAuthority); + printer.printAttribute("protocolVersions", d_protocolVersions); printer.end(); return stream; diff --git a/src/groups/bmq/bmqt/bmqt_sessionoptions.h b/src/groups/bmq/bmqt/bmqt_sessionoptions.h index e311ccd1a..18638284c 100644 --- a/src/groups/bmq/bmqt/bmqt_sessionoptions.h +++ b/src/groups/bmq/bmqt/bmqt_sessionoptions.h @@ -129,6 +129,7 @@ #include #include #include +#include #include #include #include @@ -152,6 +153,30 @@ class HostHealthMonitor; namespace bmqt { +// todo move to bmqio/ntc +// ====================== +// struct ProtocolVersion +// ====================== + +struct ProtocolVersion { + enum Value { + // Q?: do not mention deprecated TLS versions at all? + e_TLS1_2, + e_TLS1_3 + }; + + // CLASS METHODS + static bsl::ostream& print(bsl::ostream& stream, + ProtocolVersion::Value value, + int level = 0, + int spacesPerLevel = 4); + + static const char* toAscii(ProtocolVersion::Value value); + + static bool fromAscii(ProtocolVersion::Value* out, + const bslstl::StringRef& str); +}; + // ==================== // class SessionOptions // ==================== @@ -216,6 +241,11 @@ class SessionOptions { /// future release of libbmq. int d_eventQueueSize; + bsl::string d_certificateAuthority; + + bsl::unordered_set d_protocolVersions; + // Supported TLS versions. + bsl::shared_ptr d_hostHealthMonitor_sp; bsl::shared_ptr d_dtContext_sp; @@ -304,6 +334,13 @@ class SessionOptions { /// The behavior is undefined unless `lowWatermark < highWatermark`. SessionOptions& configureEventQueue(int lowWatermark, int highWatermark); + /// Set the TLS certificate authority to the specified + /// 'certificateAuthority'. Set the TLS protocol versions to the specified + /// 'versions'. Return this SessionOptions object reference. + SessionOptions& + setTlsDetails(const bslstl::StringRef& certificateAuthority, + const bslstl::StringRef& versions); + // ACCESSORS /// Get the broker URI. @@ -350,6 +387,15 @@ class SessionOptions { int eventQueueLowWatermark() const; int eventQueueHighWatermark() const; + /// @brief Get the configured certificate authority file path. + const bsl::string& certificateAuthority() const; + + /// @brief Get the configured supported TLS protocol versions. + const bsl::unordered_set& protocolVersions() const; + + /// @brief Return true if this session will be configured for TLS. + bool isTlsSession() const; + /// DEPRECATED: This parameter is no longer relevant and will be removed /// in future release of libbmq. int eventQueueSize() const; @@ -439,7 +485,7 @@ SessionOptions::setStatsDumpInterval(const bsls::TimeInterval& value) "value must be a multiple of 30s"); BSLS_ASSERT_OPT(value.seconds() < bdlt::TimeUnitRatio::k_SECONDS_PER_HOUR && - "value must be < 10 min"); + "value must be < 60 min"); d_statsDumpInterval = value; return *this; @@ -601,6 +647,22 @@ inline int SessionOptions::eventQueueHighWatermark() const return d_eventQueueHighWatermark; } +inline const bsl::string& SessionOptions::certificateAuthority() const +{ + return d_certificateAuthority; +} + +inline const bsl::unordered_set& +SessionOptions::protocolVersions() const +{ + return d_protocolVersions; +} + +inline bool SessionOptions::isTlsSession() const +{ + return !protocolVersions().empty(); +} + inline int SessionOptions::eventQueueSize() const { return d_eventQueueSize; @@ -628,7 +690,9 @@ inline bool bmqt::operator==(const bmqt::SessionOptions& lhs, lhs.eventQueueHighWatermark() == rhs.eventQueueHighWatermark() && lhs.hostHealthMonitor() == rhs.hostHealthMonitor() && lhs.traceContext() == rhs.traceContext() && - lhs.tracer() == rhs.tracer(); + lhs.tracer() == rhs.tracer() && + lhs.certificateAuthority() == rhs.certificateAuthority() && + lhs.protocolVersions() == rhs.protocolVersions(); } inline bool bmqt::operator!=(const bmqt::SessionOptions& lhs, @@ -647,7 +711,9 @@ inline bool bmqt::operator!=(const bmqt::SessionOptions& lhs, lhs.eventQueueHighWatermark() != rhs.eventQueueHighWatermark() || lhs.hostHealthMonitor() != rhs.hostHealthMonitor() || lhs.traceContext() != rhs.traceContext() || - lhs.tracer() != rhs.tracer(); + lhs.tracer() != rhs.tracer() || + lhs.certificateAuthority() != rhs.certificateAuthority() || + lhs.protocolVersions() != rhs.protocolVersions(); } inline bsl::ostream& bmqt::operator<<(bsl::ostream& stream, diff --git a/src/groups/bmq/bmqt/bmqt_sessionoptions.t.cpp b/src/groups/bmq/bmqt/bmqt_sessionoptions.t.cpp index d823ad0e6..082182b1f 100644 --- a/src/groups/bmq/bmqt/bmqt_sessionoptions.t.cpp +++ b/src/groups/bmq/bmqt/bmqt_sessionoptions.t.cpp @@ -62,7 +62,8 @@ static void test2_printTest() "openQueueTimeout = 300 configureQueueTimeout = 300 " "closeQueueTimeout = 300 eventQueueLowWatermark = 50 " "eventQueueHighWatermark = 2000 hasHostHealthMonitor = false " - "hasDistributedTracing = false ]"; + "hasDistributedTracing = false certificateAuthority = \"\" " + "protocolVersions = [ ] ]"; bmqtst::TestHelper::printTestName("PRINT"); PV("Testing print"); bmqu::MemOutStream stream(bmqtst::TestHelperUtil::allocator()); diff --git a/src/groups/bmq/bmqtst/bmqtst_testhelper.h b/src/groups/bmq/bmqtst/bmqtst_testhelper.h index 40b76b7b6..d45c1ad51 100644 --- a/src/groups/bmq/bmqtst/bmqtst_testhelper.h +++ b/src/groups/bmq/bmqtst/bmqtst_testhelper.h @@ -695,7 +695,7 @@ static void run() \ { \ FIXTURE##NAME test; \ - bmqtst::TestHelper::printTestName(#NAME); \ + bmqtst::TestHelper::printTestName(#FIXTURE "::" #NAME); \ test.SetUp(); \ test.body(); \ test.TearDown(); \ diff --git a/src/groups/bmq/group/bmq.t.dep b/src/groups/bmq/group/bmq.t.dep index 6e06777fa..e08139174 100644 --- a/src/groups/bmq/group/bmq.t.dep +++ b/src/groups/bmq/group/bmq.t.dep @@ -1,3 +1,4 @@ benchmark bmq mqb +gmock diff --git a/src/groups/mqb/mqba/mqba_application.cpp b/src/groups/mqb/mqba/mqba_application.cpp index dee58e76e..12a74e945 100644 --- a/src/groups/mqb/mqba/mqba_application.cpp +++ b/src/groups/mqb/mqba/mqba_application.cpp @@ -17,6 +17,7 @@ #include #include + // MQB #include #include diff --git a/src/groups/mqb/mqbcfg/mqbcfg.xsd b/src/groups/mqb/mqbcfg/mqbcfg.xsd index 7f88de83a..5ce0ed200 100644 --- a/src/groups/mqb/mqbcfg/mqbcfg.xsd +++ b/src/groups/mqb/mqbcfg/mqbcfg.xsd @@ -87,6 +87,7 @@ configureStream......: send new ConfigureStream instead of old ConfigureQueue advertiseSubscriptions.: temporarily control use of ConfigureStream in SDK routeCommandTimeoutMs: maximum amount of time to wait for a routed command's response + tlsConfig............: optional configuation for TLS @@ -110,6 +111,7 @@ + @@ -284,11 +286,14 @@ A name to associate this listener to. port.................: The port this listener will accept connections on. + tls..................: + Use TLS on this interface. + @@ -305,6 +310,30 @@ + + + + certificateAuthority.: + A path to the FILE, containing concatenation of known certificates + the server can use to reference as its certificate store. + certificate..........: + A path to the FILE, containing the certificate the broker will use + to identify itself to other clients. + key..................: + A path to the FILE, containing the private key that the broker uses + to read the certificate. + versions.............: + A string with a comma-separated list of supported protocol versions. + + + + + + + + + + diff --git a/src/groups/mqb/mqbcfg/mqbcfg_messages.cpp b/src/groups/mqb/mqbcfg/mqbcfg_messages.cpp index d208db048..117140aa1 100644 --- a/src/groups/mqb/mqbcfg/mqbcfg_messages.cpp +++ b/src/groups/mqb/mqbcfg/mqbcfg_messages.cpp @@ -12,6 +12,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + // mqbcfg_messages.cpp *DO NOT EDIT* @generated -*-C++-*- #include @@ -2417,6 +2418,8 @@ bsl::ostream& TcpClusterNodeConnection::print(bsl::ostream& stream, const char TcpInterfaceListener::CLASS_NAME[] = "TcpInterfaceListener"; +const bool TcpInterfaceListener::DEFAULT_INITIALIZER_TLS = false; + const bdlat_AttributeInfo TcpInterfaceListener::ATTRIBUTE_INFO_ARRAY[] = { {ATTRIBUTE_ID_NAME, "name", @@ -2427,14 +2430,19 @@ const bdlat_AttributeInfo TcpInterfaceListener::ATTRIBUTE_INFO_ARRAY[] = { "port", sizeof("port") - 1, "", - bdlat_FormattingMode::e_DEC}}; + bdlat_FormattingMode::e_DEC}, + {ATTRIBUTE_ID_TLS, + "tls", + sizeof("tls") - 1, + "", + bdlat_FormattingMode::e_TEXT}}; // CLASS METHODS const bdlat_AttributeInfo* TcpInterfaceListener::lookupAttributeInfo(const char* name, int nameLength) { - for (int i = 0; i < 2; ++i) { + for (int i = 0; i < 3; ++i) { const bdlat_AttributeInfo& attributeInfo = TcpInterfaceListener::ATTRIBUTE_INFO_ARRAY[i]; @@ -2452,6 +2460,7 @@ const bdlat_AttributeInfo* TcpInterfaceListener::lookupAttributeInfo(int id) switch (id) { case ATTRIBUTE_ID_NAME: return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_NAME]; case ATTRIBUTE_ID_PORT: return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_PORT]; + case ATTRIBUTE_ID_TLS: return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS]; default: return 0; } } @@ -2461,6 +2470,7 @@ const bdlat_AttributeInfo* TcpInterfaceListener::lookupAttributeInfo(int id) TcpInterfaceListener::TcpInterfaceListener(bslma::Allocator* basicAllocator) : d_name(basicAllocator) , d_port() +, d_tls(DEFAULT_INITIALIZER_TLS) { } @@ -2469,6 +2479,7 @@ TcpInterfaceListener::TcpInterfaceListener( bslma::Allocator* basicAllocator) : d_name(original.d_name, basicAllocator) , d_port(original.d_port) +, d_tls(original.d_tls) { } @@ -2476,7 +2487,8 @@ TcpInterfaceListener::TcpInterfaceListener( defined(BSLS_COMPILERFEATURES_SUPPORT_NOEXCEPT) TcpInterfaceListener::TcpInterfaceListener(TcpInterfaceListener&& original) noexcept : d_name(bsl::move(original.d_name)), - d_port(bsl::move(original.d_port)) + d_port(bsl::move(original.d_port)), + d_tls(bsl::move(original.d_tls)) { } @@ -2484,6 +2496,7 @@ TcpInterfaceListener::TcpInterfaceListener(TcpInterfaceListener&& original, bslma::Allocator* basicAllocator) : d_name(bsl::move(original.d_name), basicAllocator) , d_port(bsl::move(original.d_port)) +, d_tls(bsl::move(original.d_tls)) { } #endif @@ -2500,6 +2513,7 @@ TcpInterfaceListener::operator=(const TcpInterfaceListener& rhs) if (this != &rhs) { d_name = rhs.d_name; d_port = rhs.d_port; + d_tls = rhs.d_tls; } return *this; @@ -2513,6 +2527,7 @@ TcpInterfaceListener::operator=(TcpInterfaceListener&& rhs) if (this != &rhs) { d_name = bsl::move(rhs.d_name); d_port = bsl::move(rhs.d_port); + d_tls = bsl::move(rhs.d_tls); } return *this; @@ -2523,6 +2538,7 @@ void TcpInterfaceListener::reset() { bdlat_ValueTypeFunctions::reset(&d_name); bdlat_ValueTypeFunctions::reset(&d_port); + d_tls = DEFAULT_INITIALIZER_TLS; } // ACCESSORS @@ -2535,6 +2551,165 @@ bsl::ostream& TcpInterfaceListener::print(bsl::ostream& stream, printer.start(); printer.printAttribute("name", this->name()); printer.printAttribute("port", this->port()); + printer.printAttribute("tls", this->tls()); + printer.end(); + return stream; +} + +// --------------- +// class TlsConfig +// --------------- + +// CONSTANTS + +const char TlsConfig::CLASS_NAME[] = "TlsConfig"; + +const bdlat_AttributeInfo TlsConfig::ATTRIBUTE_INFO_ARRAY[] = { + {ATTRIBUTE_ID_CERTIFICATE_AUTHORITY, + "certificateAuthority", + sizeof("certificateAuthority") - 1, + "", + bdlat_FormattingMode::e_TEXT}, + {ATTRIBUTE_ID_CERTIFICATE, + "certificate", + sizeof("certificate") - 1, + "", + bdlat_FormattingMode::e_TEXT}, + {ATTRIBUTE_ID_KEY, + "key", + sizeof("key") - 1, + "", + bdlat_FormattingMode::e_TEXT}, + {ATTRIBUTE_ID_VERSIONS, + "versions", + sizeof("versions") - 1, + "", + bdlat_FormattingMode::e_TEXT}}; + +// CLASS METHODS + +const bdlat_AttributeInfo* TlsConfig::lookupAttributeInfo(const char* name, + int nameLength) +{ + for (int i = 0; i < 4; ++i) { + const bdlat_AttributeInfo& attributeInfo = + TlsConfig::ATTRIBUTE_INFO_ARRAY[i]; + + if (nameLength == attributeInfo.d_nameLength && + 0 == bsl::memcmp(attributeInfo.d_name_p, name, nameLength)) { + return &attributeInfo; + } + } + + return 0; +} + +const bdlat_AttributeInfo* TlsConfig::lookupAttributeInfo(int id) +{ + switch (id) { + case ATTRIBUTE_ID_CERTIFICATE_AUTHORITY: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE_AUTHORITY]; + case ATTRIBUTE_ID_CERTIFICATE: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE]; + case ATTRIBUTE_ID_KEY: return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_KEY]; + case ATTRIBUTE_ID_VERSIONS: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_VERSIONS]; + default: return 0; + } +} + +// CREATORS + +TlsConfig::TlsConfig(bslma::Allocator* basicAllocator) +: d_certificateAuthority(basicAllocator) +, d_certificate(basicAllocator) +, d_key(basicAllocator) +, d_versions(basicAllocator) +{ +} + +TlsConfig::TlsConfig(const TlsConfig& original, + bslma::Allocator* basicAllocator) +: d_certificateAuthority(original.d_certificateAuthority, basicAllocator) +, d_certificate(original.d_certificate, basicAllocator) +, d_key(original.d_key, basicAllocator) +, d_versions(original.d_versions, basicAllocator) +{ +} + +#if defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) && \ + defined(BSLS_COMPILERFEATURES_SUPPORT_NOEXCEPT) +TlsConfig::TlsConfig(TlsConfig&& original) noexcept +: d_certificateAuthority(bsl::move(original.d_certificateAuthority)), + d_certificate(bsl::move(original.d_certificate)), + d_key(bsl::move(original.d_key)), + d_versions(bsl::move(original.d_versions)) +{ +} + +TlsConfig::TlsConfig(TlsConfig&& original, bslma::Allocator* basicAllocator) +: d_certificateAuthority(bsl::move(original.d_certificateAuthority), + basicAllocator) +, d_certificate(bsl::move(original.d_certificate), basicAllocator) +, d_key(bsl::move(original.d_key), basicAllocator) +, d_versions(bsl::move(original.d_versions), basicAllocator) +{ +} +#endif + +TlsConfig::~TlsConfig() +{ +} + +// MANIPULATORS + +TlsConfig& TlsConfig::operator=(const TlsConfig& rhs) +{ + if (this != &rhs) { + d_certificateAuthority = rhs.d_certificateAuthority; + d_certificate = rhs.d_certificate; + d_key = rhs.d_key; + d_versions = rhs.d_versions; + } + + return *this; +} + +#if defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) && \ + defined(BSLS_COMPILERFEATURES_SUPPORT_NOEXCEPT) +TlsConfig& TlsConfig::operator=(TlsConfig&& rhs) +{ + if (this != &rhs) { + d_certificateAuthority = bsl::move(rhs.d_certificateAuthority); + d_certificate = bsl::move(rhs.d_certificate); + d_key = bsl::move(rhs.d_key); + d_versions = bsl::move(rhs.d_versions); + } + + return *this; +} +#endif + +void TlsConfig::reset() +{ + bdlat_ValueTypeFunctions::reset(&d_certificateAuthority); + bdlat_ValueTypeFunctions::reset(&d_certificate); + bdlat_ValueTypeFunctions::reset(&d_key); + bdlat_ValueTypeFunctions::reset(&d_versions); +} + +// ACCESSORS + +bsl::ostream& +TlsConfig::print(bsl::ostream& stream, int level, int spacesPerLevel) const +{ + bslim::Printer printer(&stream, level, spacesPerLevel); + printer.start(); + printer.printAttribute("certificateAuthority", + this->certificateAuthority()); + printer.printAttribute("certificate", this->certificate()); + printer.printAttribute("key", this->key()); + printer.printAttribute("versions", this->versions()); printer.end(); return stream; } @@ -5533,14 +5708,19 @@ const bdlat_AttributeInfo AppConfig::ATTRIBUTE_INFO_ARRAY[] = { "routeCommandTimeoutMs", sizeof("routeCommandTimeoutMs") - 1, "", - bdlat_FormattingMode::e_DEC}}; + bdlat_FormattingMode::e_DEC}, + {ATTRIBUTE_ID_TLS_CONFIG, + "tlsConfig", + sizeof("tlsConfig") - 1, + "", + bdlat_FormattingMode::e_DEFAULT}}; // CLASS METHODS const bdlat_AttributeInfo* AppConfig::lookupAttributeInfo(const char* name, int nameLength) { - for (int i = 0; i < 19; ++i) { + for (int i = 0; i < 20; ++i) { const bdlat_AttributeInfo& attributeInfo = AppConfig::ATTRIBUTE_INFO_ARRAY[i]; @@ -5594,6 +5774,8 @@ const bdlat_AttributeInfo* AppConfig::lookupAttributeInfo(int id) return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ADVERTISE_SUBSCRIPTIONS]; case ATTRIBUTE_ID_ROUTE_COMMAND_TIMEOUT_MS: return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ROUTE_COMMAND_TIMEOUT_MS]; + case ATTRIBUTE_ID_TLS_CONFIG: + return &ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_CONFIG]; default: return 0; } } @@ -5608,6 +5790,7 @@ AppConfig::AppConfig(bslma::Allocator* basicAllocator) , d_hostDataCenter(basicAllocator) , d_latencyMonitorDomain(DEFAULT_INITIALIZER_LATENCY_MONITOR_DOMAIN, basicAllocator) +, d_tlsConfig(basicAllocator) , d_stats(basicAllocator) , d_plugins(basicAllocator) , d_networkInterfaces(basicAllocator) @@ -5632,6 +5815,7 @@ AppConfig::AppConfig(const AppConfig& original, , d_hostTags(original.d_hostTags, basicAllocator) , d_hostDataCenter(original.d_hostDataCenter, basicAllocator) , d_latencyMonitorDomain(original.d_latencyMonitorDomain, basicAllocator) +, d_tlsConfig(original.d_tlsConfig, basicAllocator) , d_stats(original.d_stats, basicAllocator) , d_plugins(original.d_plugins, basicAllocator) , d_networkInterfaces(original.d_networkInterfaces, basicAllocator) @@ -5657,6 +5841,7 @@ AppConfig::AppConfig(AppConfig&& original) noexcept d_hostTags(bsl::move(original.d_hostTags)), d_hostDataCenter(bsl::move(original.d_hostDataCenter)), d_latencyMonitorDomain(bsl::move(original.d_latencyMonitorDomain)), + d_tlsConfig(bsl::move(original.d_tlsConfig)), d_stats(bsl::move(original.d_stats)), d_plugins(bsl::move(original.d_plugins)), d_networkInterfaces(bsl::move(original.d_networkInterfaces)), @@ -5682,6 +5867,7 @@ AppConfig::AppConfig(AppConfig&& original, bslma::Allocator* basicAllocator) , d_hostDataCenter(bsl::move(original.d_hostDataCenter), basicAllocator) , d_latencyMonitorDomain(bsl::move(original.d_latencyMonitorDomain), basicAllocator) +, d_tlsConfig(bsl::move(original.d_tlsConfig), basicAllocator) , d_stats(bsl::move(original.d_stats), basicAllocator) , d_plugins(bsl::move(original.d_plugins), basicAllocator) , d_networkInterfaces(bsl::move(original.d_networkInterfaces), basicAllocator) @@ -5727,6 +5913,7 @@ AppConfig& AppConfig::operator=(const AppConfig& rhs) d_configureStream = rhs.d_configureStream; d_advertiseSubscriptions = rhs.d_advertiseSubscriptions; d_routeCommandTimeoutMs = rhs.d_routeCommandTimeoutMs; + d_tlsConfig = rhs.d_tlsConfig; } return *this; @@ -5756,6 +5943,7 @@ AppConfig& AppConfig::operator=(AppConfig&& rhs) d_configureStream = bsl::move(rhs.d_configureStream); d_advertiseSubscriptions = bsl::move(rhs.d_advertiseSubscriptions); d_routeCommandTimeoutMs = bsl::move(rhs.d_routeCommandTimeoutMs); + d_tlsConfig = bsl::move(rhs.d_tlsConfig); } return *this; @@ -5783,6 +5971,7 @@ void AppConfig::reset() d_configureStream = DEFAULT_INITIALIZER_CONFIGURE_STREAM; d_advertiseSubscriptions = DEFAULT_INITIALIZER_ADVERTISE_SUBSCRIPTIONS; d_routeCommandTimeoutMs = DEFAULT_INITIALIZER_ROUTE_COMMAND_TIMEOUT_MS; + bdlat_ValueTypeFunctions::reset(&d_tlsConfig); } // ACCESSORS @@ -5814,6 +6003,7 @@ AppConfig::print(bsl::ostream& stream, int level, int spacesPerLevel) const this->advertiseSubscriptions()); printer.printAttribute("routeCommandTimeoutMs", this->routeCommandTimeoutMs()); + printer.printAttribute("tlsConfig", this->tlsConfig()); printer.end(); return stream; } @@ -6131,6 +6321,6 @@ Configuration::print(bsl::ostream& stream, int level, int spacesPerLevel) const } // close package namespace } // close enterprise namespace -// GENERATED BY BLP_BAS_CODEGEN_2024.10.17 +// GENERATED BY @BLP_BAS_CODEGEN_VERSION@ // USING bas_codegen.pl -m msg --noAggregateConversion --noExternalization // --noIdent --package mqbcfg --msgComponent messages mqbcfg.xsd diff --git a/src/groups/mqb/mqbcfg/mqbcfg_messages.h b/src/groups/mqb/mqbcfg/mqbcfg_messages.h index 69918e232..41f2eafe5 100644 --- a/src/groups/mqb/mqbcfg/mqbcfg_messages.h +++ b/src/groups/mqb/mqbcfg/mqbcfg_messages.h @@ -12,6 +12,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + // mqbcfg_messages.h *DO NOT EDIT* @generated -*-C++-*- #ifndef INCLUDED_MQBCFG_MESSAGES #define INCLUDED_MQBCFG_MESSAGES @@ -107,6 +108,9 @@ namespace mqbcfg { class TcpInterfaceListener; } namespace mqbcfg { +class TlsConfig; +} +namespace mqbcfg { class VirtualClusterInformation; } namespace mqbcfg { @@ -4277,23 +4281,38 @@ class TcpInterfaceListener { // listener. // name.................: A name to associate this listener to. // port.................: The port this listener will accept connections - // on. + // on. tls..................: Use TLS on this interface. // INSTANCE DATA bsl::string d_name; int d_port; + bool d_tls; + + // PRIVATE ACCESSORS + template + void hashAppendImpl(t_HASH_ALGORITHM& hashAlgorithm) const; public: // TYPES - enum { ATTRIBUTE_ID_NAME = 0, ATTRIBUTE_ID_PORT = 1 }; + enum { + ATTRIBUTE_ID_NAME = 0, + ATTRIBUTE_ID_PORT = 1, + ATTRIBUTE_ID_TLS = 2 + }; - enum { NUM_ATTRIBUTES = 2 }; + enum { NUM_ATTRIBUTES = 3 }; - enum { ATTRIBUTE_INDEX_NAME = 0, ATTRIBUTE_INDEX_PORT = 1 }; + enum { + ATTRIBUTE_INDEX_NAME = 0, + ATTRIBUTE_INDEX_PORT = 1, + ATTRIBUTE_INDEX_TLS = 2 + }; // CONSTANTS static const char CLASS_NAME[]; + static const bool DEFAULT_INITIALIZER_TLS; + static const bdlat_AttributeInfo ATTRIBUTE_INFO_ARRAY[]; public: @@ -4395,6 +4414,9 @@ class TcpInterfaceListener { // Return a reference to the modifiable "Port" attribute of this // object. + bool& tls(); + // Return a reference to the modifiable "Tls" attribute of this object. + // ACCESSORS bsl::ostream& print(bsl::ostream& stream, int level = 0, int spacesPerLevel = 4) const; @@ -4445,6 +4467,9 @@ class TcpInterfaceListener { int port() const; // Return the value of the "Port" attribute of this object. + bool tls() const; + // Return the value of the "Tls" attribute of this object. + // HIDDEN FRIENDS friend bool operator==(const TcpInterfaceListener& lhs, const TcpInterfaceListener& rhs) @@ -4452,7 +4477,8 @@ class TcpInterfaceListener { // have the same value, and 'false' otherwise. Two attribute objects // have the same value if each respective attribute has the same value. { - return lhs.name() == rhs.name() && lhs.port() == rhs.port(); + return lhs.name() == rhs.name() && lhs.port() == rhs.port() && + lhs.tls() == rhs.tls(); } friend bool operator!=(const TcpInterfaceListener& lhs, @@ -4478,9 +4504,7 @@ class TcpInterfaceListener { // effectively provides a 'bsl::hash' specialization for // 'TcpInterfaceListener'. { - using bslh::hashAppend; - hashAppend(hashAlg, object.name()); - hashAppend(hashAlg, object.port()); + object.hashAppendImpl(hashAlg); } }; @@ -4493,6 +4517,257 @@ BDLAT_DECL_SEQUENCE_WITH_ALLOCATOR_BITWISEMOVEABLE_TRAITS( namespace mqbcfg { +// =============== +// class TlsConfig +// =============== + +class TlsConfig { + // certificateAuthority.: A path to the FILE, containing concatenation of + // known certificates the server can use to reference as its certificate + // store. certificate..........: A path to the FILE, containing the + // certificate the broker will use to identify itself to other clients. + // key..................: A path to the FILE, containing the private key + // that the broker uses to read the certificate. versions.............: A + // string with a comma-separated list of supported protocol versions. + + // INSTANCE DATA + bsl::string d_certificateAuthority; + bsl::string d_certificate; + bsl::string d_key; + bsl::string d_versions; + + // PRIVATE ACCESSORS + template + void hashAppendImpl(t_HASH_ALGORITHM& hashAlgorithm) const; + + bool isEqualTo(const TlsConfig& rhs) const; + + public: + // TYPES + enum { + ATTRIBUTE_ID_CERTIFICATE_AUTHORITY = 0, + ATTRIBUTE_ID_CERTIFICATE = 1, + ATTRIBUTE_ID_KEY = 2, + ATTRIBUTE_ID_VERSIONS = 3 + }; + + enum { NUM_ATTRIBUTES = 4 }; + + enum { + ATTRIBUTE_INDEX_CERTIFICATE_AUTHORITY = 0, + ATTRIBUTE_INDEX_CERTIFICATE = 1, + ATTRIBUTE_INDEX_KEY = 2, + ATTRIBUTE_INDEX_VERSIONS = 3 + }; + + // CONSTANTS + static const char CLASS_NAME[]; + + static const bdlat_AttributeInfo ATTRIBUTE_INFO_ARRAY[]; + + public: + // CLASS METHODS + static const bdlat_AttributeInfo* lookupAttributeInfo(int id); + // Return attribute information for the attribute indicated by the + // specified 'id' if the attribute exists, and 0 otherwise. + + static const bdlat_AttributeInfo* lookupAttributeInfo(const char* name, + int nameLength); + // Return attribute information for the attribute indicated by the + // specified 'name' of the specified 'nameLength' if the attribute + // exists, and 0 otherwise. + + // CREATORS + explicit TlsConfig(bslma::Allocator* basicAllocator = 0); + // Create an object of type 'TlsConfig' having the default value. Use + // the optionally specified 'basicAllocator' to supply memory. If + // 'basicAllocator' is 0, the currently installed default allocator is + // used. + + TlsConfig(const TlsConfig& original, bslma::Allocator* basicAllocator = 0); + // Create an object of type 'TlsConfig' having the value of the + // specified 'original' object. Use the optionally specified + // 'basicAllocator' to supply memory. If 'basicAllocator' is 0, the + // currently installed default allocator is used. + +#if defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) && \ + defined(BSLS_COMPILERFEATURES_SUPPORT_NOEXCEPT) + TlsConfig(TlsConfig&& original) noexcept; + // Create an object of type 'TlsConfig' having the value of the + // specified 'original' object. After performing this action, the + // 'original' object will be left in a valid, but unspecified state. + + TlsConfig(TlsConfig&& original, bslma::Allocator* basicAllocator); + // Create an object of type 'TlsConfig' having the value of the + // specified 'original' object. After performing this action, the + // 'original' object will be left in a valid, but unspecified state. + // Use the optionally specified 'basicAllocator' to supply memory. If + // 'basicAllocator' is 0, the currently installed default allocator is + // used. +#endif + + ~TlsConfig(); + // Destroy this object. + + // MANIPULATORS + TlsConfig& operator=(const TlsConfig& rhs); + // Assign to this object the value of the specified 'rhs' object. + +#if defined(BSLS_COMPILERFEATURES_SUPPORT_RVALUE_REFERENCES) && \ + defined(BSLS_COMPILERFEATURES_SUPPORT_NOEXCEPT) + TlsConfig& operator=(TlsConfig&& rhs); + // Assign to this object the value of the specified 'rhs' object. + // After performing this action, the 'rhs' object will be left in a + // valid, but unspecified state. +#endif + + void reset(); + // Reset this object to the default value (i.e., its value upon + // default construction). + + template + int manipulateAttributes(t_MANIPULATOR& manipulator); + // Invoke the specified 'manipulator' sequentially on the address of + // each (modifiable) attribute of this object, supplying 'manipulator' + // with the corresponding attribute information structure until such + // invocation returns a non-zero value. Return the value from the + // last invocation of 'manipulator' (i.e., the invocation that + // terminated the sequence). + + template + int manipulateAttribute(t_MANIPULATOR& manipulator, int id); + // Invoke the specified 'manipulator' on the address of + // the (modifiable) attribute indicated by the specified 'id', + // supplying 'manipulator' with the corresponding attribute + // information structure. Return the value returned from the + // invocation of 'manipulator' if 'id' identifies an attribute of this + // class, and -1 otherwise. + + template + int manipulateAttribute(t_MANIPULATOR& manipulator, + const char* name, + int nameLength); + // Invoke the specified 'manipulator' on the address of + // the (modifiable) attribute indicated by the specified 'name' of the + // specified 'nameLength', supplying 'manipulator' with the + // corresponding attribute information structure. Return the value + // returned from the invocation of 'manipulator' if 'name' identifies + // an attribute of this class, and -1 otherwise. + + bsl::string& certificateAuthority(); + // Return a reference to the modifiable "CertificateAuthority" + // attribute of this object. + + bsl::string& certificate(); + // Return a reference to the modifiable "Certificate" attribute of this + // object. + + bsl::string& key(); + // Return a reference to the modifiable "Key" attribute of this object. + + bsl::string& versions(); + // Return a reference to the modifiable "Versions" attribute of this + // object. + + // ACCESSORS + bsl::ostream& + print(bsl::ostream& stream, int level = 0, int spacesPerLevel = 4) const; + // Format this object to the specified output 'stream' at the + // optionally specified indentation 'level' and return a reference to + // the modifiable 'stream'. If 'level' is specified, optionally + // specify 'spacesPerLevel', the number of spaces per indentation level + // for this and all of its nested objects. Each line is indented by + // the absolute value of 'level * spacesPerLevel'. If 'level' is + // negative, suppress indentation of the first line. If + // 'spacesPerLevel' is negative, suppress line breaks and format the + // entire output on one line. If 'stream' is initially invalid, this + // operation has no effect. Note that a trailing newline is provided + // in multiline mode only. + + template + int accessAttributes(t_ACCESSOR& accessor) const; + // Invoke the specified 'accessor' sequentially on each + // (non-modifiable) attribute of this object, supplying 'accessor' + // with the corresponding attribute information structure until such + // invocation returns a non-zero value. Return the value from the + // last invocation of 'accessor' (i.e., the invocation that terminated + // the sequence). + + template + int accessAttribute(t_ACCESSOR& accessor, int id) const; + // Invoke the specified 'accessor' on the (non-modifiable) attribute + // of this object indicated by the specified 'id', supplying 'accessor' + // with the corresponding attribute information structure. Return the + // value returned from the invocation of 'accessor' if 'id' identifies + // an attribute of this class, and -1 otherwise. + + template + int accessAttribute(t_ACCESSOR& accessor, + const char* name, + int nameLength) const; + // Invoke the specified 'accessor' on the (non-modifiable) attribute + // of this object indicated by the specified 'name' of the specified + // 'nameLength', supplying 'accessor' with the corresponding attribute + // information structure. Return the value returned from the + // invocation of 'accessor' if 'name' identifies an attribute of this + // class, and -1 otherwise. + + const bsl::string& certificateAuthority() const; + // Return a reference offering non-modifiable access to the + // "CertificateAuthority" attribute of this object. + + const bsl::string& certificate() const; + // Return a reference offering non-modifiable access to the + // "Certificate" attribute of this object. + + const bsl::string& key() const; + // Return a reference offering non-modifiable access to the "Key" + // attribute of this object. + + const bsl::string& versions() const; + // Return a reference offering non-modifiable access to the "Versions" + // attribute of this object. + + // HIDDEN FRIENDS + friend bool operator==(const TlsConfig& lhs, const TlsConfig& rhs) + // Return 'true' if the specified 'lhs' and 'rhs' attribute objects + // have the same value, and 'false' otherwise. Two attribute objects + // have the same value if each respective attribute has the same value. + { + return lhs.isEqualTo(rhs); + } + + friend bool operator!=(const TlsConfig& lhs, const TlsConfig& rhs) + // Returns '!(lhs == rhs)' + { + return !(lhs == rhs); + } + + friend bsl::ostream& operator<<(bsl::ostream& stream, const TlsConfig& rhs) + // Format the specified 'rhs' to the specified output 'stream' and + // return a reference to the modifiable 'stream'. + { + return rhs.print(stream, 0, -1); + } + + template + friend void hashAppend(t_HASH_ALGORITHM& hashAlg, const TlsConfig& object) + // Pass the specified 'object' to the specified 'hashAlg'. This + // function integrates with the 'bslh' modular hashing system and + // effectively provides a 'bsl::hash' specialization for 'TlsConfig'. + { + object.hashAppendImpl(hashAlg); + } +}; + +} // close package namespace + +// TRAITS + +BDLAT_DECL_SEQUENCE_WITH_ALLOCATOR_BITWISEMOVEABLE_TRAITS(mqbcfg::TlsConfig) + +namespace mqbcfg { + // =============================== // class VirtualClusterInformation // =============================== @@ -8676,28 +8951,30 @@ class AppConfig { // configureStream......: send new ConfigureStream instead of old // ConfigureQueue advertiseSubscriptions.: temporarily control use of // ConfigureStream in SDK routeCommandTimeoutMs: maximum amount of time to - // wait for a routed command's response + // wait for a routed command's response tlsConfig............: optional + // configuation for TLS // INSTANCE DATA - bsl::string d_brokerInstanceName; - bsl::string d_etcDir; - bsl::string d_hostName; - bsl::string d_hostTags; - bsl::string d_hostDataCenter; - bsl::string d_latencyMonitorDomain; - StatsConfig d_stats; - Plugins d_plugins; - NetworkInterfaces d_networkInterfaces; - MessagePropertiesV2 d_messagePropertiesV2; - DispatcherConfig d_dispatcherConfig; - BmqconfConfig d_bmqconfConfig; - int d_brokerVersion; - int d_configVersion; - int d_logsObserverMaxSize; - int d_routeCommandTimeoutMs; - bool d_isRunningOnDev; - bool d_configureStream; - bool d_advertiseSubscriptions; + bsl::string d_brokerInstanceName; + bsl::string d_etcDir; + bsl::string d_hostName; + bsl::string d_hostTags; + bsl::string d_hostDataCenter; + bsl::string d_latencyMonitorDomain; + bdlb::NullableValue d_tlsConfig; + StatsConfig d_stats; + Plugins d_plugins; + NetworkInterfaces d_networkInterfaces; + MessagePropertiesV2 d_messagePropertiesV2; + DispatcherConfig d_dispatcherConfig; + BmqconfConfig d_bmqconfConfig; + int d_brokerVersion; + int d_configVersion; + int d_logsObserverMaxSize; + int d_routeCommandTimeoutMs; + bool d_isRunningOnDev; + bool d_configureStream; + bool d_advertiseSubscriptions; // PRIVATE ACCESSORS template @@ -8726,10 +9003,11 @@ class AppConfig { ATTRIBUTE_ID_MESSAGE_PROPERTIES_V2 = 15, ATTRIBUTE_ID_CONFIGURE_STREAM = 16, ATTRIBUTE_ID_ADVERTISE_SUBSCRIPTIONS = 17, - ATTRIBUTE_ID_ROUTE_COMMAND_TIMEOUT_MS = 18 + ATTRIBUTE_ID_ROUTE_COMMAND_TIMEOUT_MS = 18, + ATTRIBUTE_ID_TLS_CONFIG = 19 }; - enum { NUM_ATTRIBUTES = 19 }; + enum { NUM_ATTRIBUTES = 20 }; enum { ATTRIBUTE_INDEX_BROKER_INSTANCE_NAME = 0, @@ -8750,7 +9028,8 @@ class AppConfig { ATTRIBUTE_INDEX_MESSAGE_PROPERTIES_V2 = 15, ATTRIBUTE_INDEX_CONFIGURE_STREAM = 16, ATTRIBUTE_INDEX_ADVERTISE_SUBSCRIPTIONS = 17, - ATTRIBUTE_INDEX_ROUTE_COMMAND_TIMEOUT_MS = 18 + ATTRIBUTE_INDEX_ROUTE_COMMAND_TIMEOUT_MS = 18, + ATTRIBUTE_INDEX_TLS_CONFIG = 19 }; // CONSTANTS @@ -8931,6 +9210,10 @@ class AppConfig { // Return a reference to the modifiable "RouteCommandTimeoutMs" // attribute of this object. + bdlb::NullableValue& tlsConfig(); + // Return a reference to the modifiable "TlsConfig" attribute of this + // object. + // ACCESSORS bsl::ostream& print(bsl::ostream& stream, int level = 0, int spacesPerLevel = 4) const; @@ -9046,6 +9329,10 @@ class AppConfig { // Return the value of the "RouteCommandTimeoutMs" attribute of this // object. + const bdlb::NullableValue& tlsConfig() const; + // Return a reference offering non-modifiable access to the "TlsConfig" + // attribute of this object. + // HIDDEN FRIENDS friend bool operator==(const AppConfig& lhs, const AppConfig& rhs) // Return 'true' if the specified 'lhs' and 'rhs' attribute objects @@ -13401,6 +13688,17 @@ inline const bsl::string& TcpClusterNodeConnection::endpoint() const // class TcpInterfaceListener // -------------------------- +// PRIVATE ACCESSORS +template +void TcpInterfaceListener::hashAppendImpl( + t_HASH_ALGORITHM& hashAlgorithm) const +{ + using bslh::hashAppend; + hashAppend(hashAlgorithm, this->name()); + hashAppend(hashAlgorithm, this->port()); + hashAppend(hashAlgorithm, this->tls()); +} + // CLASS METHODS // MANIPULATORS template @@ -13418,6 +13716,11 @@ int TcpInterfaceListener::manipulateAttributes(t_MANIPULATOR& manipulator) return ret; } + ret = manipulator(&d_tls, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS]); + if (ret) { + return ret; + } + return 0; } @@ -13436,6 +13739,9 @@ int TcpInterfaceListener::manipulateAttribute(t_MANIPULATOR& manipulator, return manipulator(&d_port, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_PORT]); } + case ATTRIBUTE_ID_TLS: { + return manipulator(&d_tls, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS]); + } default: return NOT_FOUND; } } @@ -13466,6 +13772,11 @@ inline int& TcpInterfaceListener::port() return d_port; } +inline bool& TcpInterfaceListener::tls() +{ + return d_tls; +} + // ACCESSORS template int TcpInterfaceListener::accessAttributes(t_ACCESSOR& accessor) const @@ -13482,6 +13793,11 @@ int TcpInterfaceListener::accessAttributes(t_ACCESSOR& accessor) const return ret; } + ret = accessor(d_tls, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS]); + if (ret) { + return ret; + } + return 0; } @@ -13497,6 +13813,9 @@ int TcpInterfaceListener::accessAttribute(t_ACCESSOR& accessor, int id) const case ATTRIBUTE_ID_PORT: { return accessor(d_port, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_PORT]); } + case ATTRIBUTE_ID_TLS: { + return accessor(d_tls, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS]); + } default: return NOT_FOUND; } } @@ -13527,6 +13846,223 @@ inline int TcpInterfaceListener::port() const return d_port; } +inline bool TcpInterfaceListener::tls() const +{ + return d_tls; +} + +// --------------- +// class TlsConfig +// --------------- + +// PRIVATE ACCESSORS +template +void TlsConfig::hashAppendImpl(t_HASH_ALGORITHM& hashAlgorithm) const +{ + using bslh::hashAppend; + hashAppend(hashAlgorithm, this->certificateAuthority()); + hashAppend(hashAlgorithm, this->certificate()); + hashAppend(hashAlgorithm, this->key()); + hashAppend(hashAlgorithm, this->versions()); +} + +inline bool TlsConfig::isEqualTo(const TlsConfig& rhs) const +{ + return this->certificateAuthority() == rhs.certificateAuthority() && + this->certificate() == rhs.certificate() && + this->key() == rhs.key() && this->versions() == rhs.versions(); +} + +// CLASS METHODS +// MANIPULATORS +template +int TlsConfig::manipulateAttributes(t_MANIPULATOR& manipulator) +{ + int ret; + + ret = manipulator( + &d_certificateAuthority, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE_AUTHORITY]); + if (ret) { + return ret; + } + + ret = manipulator(&d_certificate, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE]); + if (ret) { + return ret; + } + + ret = manipulator(&d_key, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_KEY]); + if (ret) { + return ret; + } + + ret = manipulator(&d_versions, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_VERSIONS]); + if (ret) { + return ret; + } + + return 0; +} + +template +int TlsConfig::manipulateAttribute(t_MANIPULATOR& manipulator, int id) +{ + enum { NOT_FOUND = -1 }; + + switch (id) { + case ATTRIBUTE_ID_CERTIFICATE_AUTHORITY: { + return manipulator( + &d_certificateAuthority, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE_AUTHORITY]); + } + case ATTRIBUTE_ID_CERTIFICATE: { + return manipulator(&d_certificate, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE]); + } + case ATTRIBUTE_ID_KEY: { + return manipulator(&d_key, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_KEY]); + } + case ATTRIBUTE_ID_VERSIONS: { + return manipulator(&d_versions, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_VERSIONS]); + } + default: return NOT_FOUND; + } +} + +template +int TlsConfig::manipulateAttribute(t_MANIPULATOR& manipulator, + const char* name, + int nameLength) +{ + enum { NOT_FOUND = -1 }; + + const bdlat_AttributeInfo* attributeInfo = lookupAttributeInfo(name, + nameLength); + if (0 == attributeInfo) { + return NOT_FOUND; + } + + return manipulateAttribute(manipulator, attributeInfo->d_id); +} + +inline bsl::string& TlsConfig::certificateAuthority() +{ + return d_certificateAuthority; +} + +inline bsl::string& TlsConfig::certificate() +{ + return d_certificate; +} + +inline bsl::string& TlsConfig::key() +{ + return d_key; +} + +inline bsl::string& TlsConfig::versions() +{ + return d_versions; +} + +// ACCESSORS +template +int TlsConfig::accessAttributes(t_ACCESSOR& accessor) const +{ + int ret; + + ret = accessor( + d_certificateAuthority, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE_AUTHORITY]); + if (ret) { + return ret; + } + + ret = accessor(d_certificate, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE]); + if (ret) { + return ret; + } + + ret = accessor(d_key, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_KEY]); + if (ret) { + return ret; + } + + ret = accessor(d_versions, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_VERSIONS]); + if (ret) { + return ret; + } + + return 0; +} + +template +int TlsConfig::accessAttribute(t_ACCESSOR& accessor, int id) const +{ + enum { NOT_FOUND = -1 }; + + switch (id) { + case ATTRIBUTE_ID_CERTIFICATE_AUTHORITY: { + return accessor( + d_certificateAuthority, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE_AUTHORITY]); + } + case ATTRIBUTE_ID_CERTIFICATE: { + return accessor(d_certificate, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_CERTIFICATE]); + } + case ATTRIBUTE_ID_KEY: { + return accessor(d_key, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_KEY]); + } + case ATTRIBUTE_ID_VERSIONS: { + return accessor(d_versions, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_VERSIONS]); + } + default: return NOT_FOUND; + } +} + +template +int TlsConfig::accessAttribute(t_ACCESSOR& accessor, + const char* name, + int nameLength) const +{ + enum { NOT_FOUND = -1 }; + + const bdlat_AttributeInfo* attributeInfo = lookupAttributeInfo(name, + nameLength); + if (0 == attributeInfo) { + return NOT_FOUND; + } + + return accessAttribute(accessor, attributeInfo->d_id); +} + +inline const bsl::string& TlsConfig::certificateAuthority() const +{ + return d_certificateAuthority; +} + +inline const bsl::string& TlsConfig::certificate() const +{ + return d_certificate; +} + +inline const bsl::string& TlsConfig::key() const +{ + return d_key; +} + +inline const bsl::string& TlsConfig::versions() const +{ + return d_versions; +} + // ------------------------------- // class VirtualClusterInformation // ------------------------------- @@ -17456,6 +17992,7 @@ void AppConfig::hashAppendImpl(t_HASH_ALGORITHM& hashAlgorithm) const hashAppend(hashAlgorithm, this->configureStream()); hashAppend(hashAlgorithm, this->advertiseSubscriptions()); hashAppend(hashAlgorithm, this->routeCommandTimeoutMs()); + hashAppend(hashAlgorithm, this->tlsConfig()); } inline bool AppConfig::isEqualTo(const AppConfig& rhs) const @@ -17478,7 +18015,8 @@ inline bool AppConfig::isEqualTo(const AppConfig& rhs) const this->messagePropertiesV2() == rhs.messagePropertiesV2() && this->configureStream() == rhs.configureStream() && this->advertiseSubscriptions() == rhs.advertiseSubscriptions() && - this->routeCommandTimeoutMs() == rhs.routeCommandTimeoutMs(); + this->routeCommandTimeoutMs() == rhs.routeCommandTimeoutMs() && + this->tlsConfig() == rhs.tlsConfig(); } // CLASS METHODS @@ -17608,6 +18146,12 @@ int AppConfig::manipulateAttributes(t_MANIPULATOR& manipulator) return ret; } + ret = manipulator(&d_tlsConfig, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_CONFIG]); + if (ret) { + return ret; + } + return 0; } @@ -17707,6 +18251,10 @@ int AppConfig::manipulateAttribute(t_MANIPULATOR& manipulator, int id) &d_routeCommandTimeoutMs, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ROUTE_COMMAND_TIMEOUT_MS]); } + case ATTRIBUTE_ID_TLS_CONFIG: { + return manipulator(&d_tlsConfig, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_CONFIG]); + } default: return NOT_FOUND; } } @@ -17822,6 +18370,11 @@ inline int& AppConfig::routeCommandTimeoutMs() return d_routeCommandTimeoutMs; } +inline bdlb::NullableValue& AppConfig::tlsConfig() +{ + return d_tlsConfig; +} + // ACCESSORS template int AppConfig::accessAttributes(t_ACCESSOR& accessor) const @@ -17944,6 +18497,12 @@ int AppConfig::accessAttributes(t_ACCESSOR& accessor) const return ret; } + ret = accessor(d_tlsConfig, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_CONFIG]); + if (ret) { + return ret; + } + return 0; } @@ -18039,6 +18598,10 @@ int AppConfig::accessAttribute(t_ACCESSOR& accessor, int id) const d_routeCommandTimeoutMs, ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_ROUTE_COMMAND_TIMEOUT_MS]); } + case ATTRIBUTE_ID_TLS_CONFIG: { + return accessor(d_tlsConfig, + ATTRIBUTE_INFO_ARRAY[ATTRIBUTE_INDEX_TLS_CONFIG]); + } default: return NOT_FOUND; } } @@ -18154,6 +18717,11 @@ inline int AppConfig::routeCommandTimeoutMs() const return d_routeCommandTimeoutMs; } +inline const bdlb::NullableValue& AppConfig::tlsConfig() const +{ + return d_tlsConfig; +} + // ------------------------ // class ClustersDefinition // ------------------------ @@ -18561,13 +19129,6 @@ inline const AppConfig& Configuration::appConfig() const } // close enterprise namespace #endif -// GENERATED BY BLP_BAS_CODEGEN_2024.10.17 +// GENERATED BY @BLP_BAS_CODEGEN_VERSION@ // USING bas_codegen.pl -m msg --noAggregateConversion --noExternalization // --noIdent --package mqbcfg --msgComponent messages mqbcfg.xsd -// ---------------------------------------------------------------------------- -// NOTICE: -// Copyright 2024 Bloomberg Finance L.P. All rights reserved. -// Property of Bloomberg Finance L.P. (BFLP) -// This software is made available solely pursuant to the -// terms of a BFLP license agreement which governs its use. -// ------------------------------- END-OF-FILE -------------------------------- diff --git a/src/groups/mqb/mqbnet/mqbnet_tcpsessionfactory.cpp b/src/groups/mqb/mqbnet/mqbnet_tcpsessionfactory.cpp index 3f5bb7e5d..2a10c4ec5 100644 --- a/src/groups/mqb/mqbnet/mqbnet_tcpsessionfactory.cpp +++ b/src/groups/mqb/mqbnet/mqbnet_tcpsessionfactory.cpp @@ -14,6 +14,8 @@ // limitations under the License. // mqbnet_tcpsessionfactory.cpp -*-C++-*- +#include +#include #include #include @@ -55,6 +57,7 @@ // BDE #include +#include #include #include #include @@ -65,7 +68,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -295,6 +300,12 @@ struct TCPSessionFactory_OperationContext { // that will be set for the // 'NegotiatorContext::resultState' passed to the // 'Negotiator::negotiate'. + + /// True if the session is TLS enabled. + bool d_isTls; + + /// Name for the TCP interface the operation is associated with. + bsl::string d_interfaceName; }; // ----------------------- @@ -709,28 +720,45 @@ void TCPSessionFactory::channelStateCallback( d_allocator_p); channel->close(closeStatus); } + else if (context->d_isTls) { + BALL_LOG_DEBUG << "TLS upgrade is in process"; + + bsl::shared_ptr statAlias = + bsl::dynamic_pointer_cast(channel); + BSLS_ASSERT(statAlias); + BSLS_ASSERT(statAlias->base()); + + bmqio::ResolvingChannelFactory_Channel* resolveAlias = + dynamic_cast( + statAlias->base()); + BSLS_ASSERT(statAlias); + BSLS_ASSERT(statAlias->base()); + + bmqio::NtcChannel* alias = dynamic_cast( + resolveAlias->base()); + + if (alias) { + alias->upgrade(d_encryptionServer_sp, + ntca::UpgradeOptions(), + bdlf::BindUtil::bindS( + d_allocator_p, + &TCPSessionFactory::negotiationWithTlsInit, + this, + channel, + context, + bdlf::PlaceHolders::_1, + bdlf::PlaceHolders::_2)); + } + else { + BALL_LOG_ERROR << "No alias"; + bmqio::Status closeStatus( + bmqio::StatusCategory::e_GENERIC_ERROR, + d_allocator_p); + channel->close(closeStatus); + } + } else { - { // Save begin session timestamp - // TODO: it's possible to store this timestamp directly in one - // of the bmqio::Channel implementations, so we don't need a - // mutex synchronization for them at all. - bslmt::LockGuard guard(&d_mutex); // LOCK - d_timestampMap[channel.get()] = - bmqsys::Time::highResolutionTimer(); - } // close mutex lock guard // UNLOCK - - // Keep track of active channels, for logging purposes - ++d_nbActiveChannels; - - // Register as observer of the channel to get the 'onClose' - channel->onClose(bdlf::BindUtil::bindS( - d_allocator_p, - &TCPSessionFactory::onClose, - this, - channel, - bdlf::PlaceHolders::_1 /* bmqio::Status */)); - - negotiate(channel, context); + negotiationInit(channel, context); } } break; case bmqio::ChannelFactoryEvent::e_CONNECT_ATTEMPT_FAILED: { @@ -749,6 +777,50 @@ void TCPSessionFactory::channelStateCallback( } } +void TCPSessionFactory::negotiationWithTlsInit( + const bsl::shared_ptr& channel, + const bsl::shared_ptr& context, + BSLA_MAYBE_UNUSED const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& event) +{ + if (event.isError()) { + BALL_LOG_ERROR << "Received error during TLS negotiation: " << event; + bmqio::Status status(bmqio::StatusCategory::e_GENERIC_ERROR, + d_allocator_p); + channel->close(status); + + return; // RETURN + } + + negotiationInit(channel, context); +} + +void TCPSessionFactory::negotiationInit( + bsl::shared_ptr channel, + bsl::shared_ptr context) +{ + { // Save begin session timestamp + // TODO: it's possible to store this timestamp directly in one + // of the bmqio::Channel implementations, so we don't need a + // mutex synchronization for them at all. + bslmt::LockGuard guard(&d_mutex); // LOCK + d_timestampMap[channel.get()] = bmqsys::Time::highResolutionTimer(); + } // close mutex lock guard // UNLOCK + + // Keep track of active channels, for logging purposes + ++d_nbActiveChannels; + + // Register as observer of the channel to get the 'onClose' + channel->onClose( + bdlf::BindUtil::bindS(d_allocator_p, + &TCPSessionFactory::onClose, + this, + channel, + bdlf::PlaceHolders::_1 /* bmqio::Status */)); + + negotiate(channel, context); +} + void TCPSessionFactory::onClose(const bsl::shared_ptr& channel, const bmqio::Status& status) { @@ -775,16 +847,16 @@ void TCPSessionFactory::onClose(const bsl::shared_ptr& channel, if (!channelInfo) { // We register to the close event as soon as the channel is up; // however, we insert in the d_channels only upon successful - // negotiation; therefore a failed to negotiate channel (like during - // intrusion testing) would trigger this trace. + // negotiation; therefore a failed to negotiate channel (like + // during intrusion testing) would trigger this trace. BALL_LOG_INFO << "#TCP_UNEXPECTED_STATE " - << "TCPSessionFactory '" << d_config.name() + << "TCPSessionFactory '" << d_threadName << "': OnClose channel for an unknown channel '" << channel.get() << "', " << d_nbActiveChannels << " active channels, status: " << status; } else { - BALL_LOG_INFO << "TCPSessionFactory '" << d_config.name() + BALL_LOG_INFO << "TCPSessionFactory '" << d_threadName << "': OnClose channel [session: '" << channelInfo->d_session_sp->description() << "', channel: '" << channel.get() << "', " @@ -795,12 +867,12 @@ void TCPSessionFactory::onClose(const bsl::shared_ptr& channel, if (channelInfo->d_maxMissedHeartbeat != 0 && d_heartbeatSchedulerActive) { // NOTE: When shutting down, we don't care about heartbeat - // verifying the channel, therefore, as an optimization to - // avoid the one-by-one disable for each channel (as they all - // will get closed at this time), the 'stop()' sequence - // cancels the recurring event and wait before closing the - // channels, so we don't need to 'disableHeartbeat' in this - // case. + // verifying the channel, therefore, as an optimization + // to avoid the one-by-one disable for each channel (as + // they all will get closed at this time), the 'stop()' + // sequence cancels the recurring event and wait before + // closing the channels, so we don't need to + // 'disableHeartbeat' in this case. d_scheduler_p->scheduleEvent( bsls::TimeInterval(0), bdlf::BindUtil::bind(&TCPSessionFactory::disableHeartbeat, @@ -829,20 +901,21 @@ void TCPSessionFactory::onHeartbeatSchedulerEvent() ChannelInfo* info = it->second; // Always proactively send a sporadic heartbeat response message to - // notify remote peer of the 'good functioning' of that unidirectional - // part of the channel. + // notify remote peer of the 'good functioning' of that + // unidirectional part of the channel. // /// NOTE ///---- - // - this is necessary in the scenario where broker (A) is sending a - // huge amount of data to its peer (B), and (B) is just reading, not - // sending anything; therefore (A) will try to send heartbeat - // requests, which will be queued behind the data, and not being - // delivered in time. + // - this is necessary in the scenario where broker (A) is sending + // a + // huge amount of data to its peer (B), and (B) is just reading, + // not sending anything; therefore (A) will try to send + // heartbeat requests, which will be queued behind the data, and + // not being delivered in time. // - sending a 'heartbeatRsp' unconditionally make it sound // superfluous to also do the remaining of this method (i.e., - // sending 'heartbeatReq' in case we haven't received any data from - // the remote peer), but we still do it as it's a very low + // sending 'heartbeatReq' in case we haven't received any data + // from the remote peer), but we still do it as it's a very low // insignificant overhead that can be helpful to ensure good // detection of any one-way TCP issue. // @@ -864,8 +937,8 @@ void TCPSessionFactory::onHeartbeatSchedulerEvent() // Perform 'incoming' traffic channel monitoring if (BSLS_PERFORMANCEHINT_PREDICT_LIKELY( info->d_packetReceived.loadRelaxed() != 0)) { - // A packet was received on the channel since the last heartbeat - // check, simply reset the associated counters. + // A packet was received on the channel since the last + // heartbeat check, simply reset the associated counters. info->d_packetReceived.storeRelaxed(0); info->d_missedHeartbeatCounter = 0; continue; // CONTINUE @@ -874,7 +947,7 @@ void TCPSessionFactory::onHeartbeatSchedulerEvent() BSLS_PERFORMANCEHINT_UNLIKELY_HINT; if (++info->d_missedHeartbeatCounter == info->d_maxMissedHeartbeat) { BALL_LOG_WARN << "#TCP_DEAD_CHANNEL " - << "TCPSessionFactory '" << d_config.name() << "'" + << "TCPSessionFactory '" << d_threadName << "'" << ": Closing unresponsive channel after " << static_cast(info->d_maxMissedHeartbeat) << " missed heartbeats [session: '" @@ -887,8 +960,9 @@ void TCPSessionFactory::onHeartbeatSchedulerEvent() // Send heartbeat info->d_channel_p->write(0, // status bmqp::ProtocolUtil::heartbeatReqBlob()); - // We explicitly ignore any failure as failure implies issues with - // the channel, which is what the heartbeat is trying to expose. + // We explicitly ignore any failure as failure implies issues + // with the channel, which is what the heartbeat is trying to + // expose. } } } @@ -905,7 +979,7 @@ void TCPSessionFactory::disableHeartbeat( { // executed by the *SCHEDULER* thread - BALL_LOG_INFO << "Disabling TCPSessionFactory '" << d_config.name() + BALL_LOG_INFO << "Disabling TCPSessionFactory '" << d_threadName << "' Heartbeat"; d_heartbeatChannels.erase(channelInfo->d_channel_p); @@ -974,6 +1048,8 @@ TCPSessionFactory::TCPSessionFactory( , d_isListening(false) , d_listenContexts(allocator) , d_timestampMap(allocator) +, d_certificateStore() +, d_encryptionServer_sp() , d_allocator_p(allocator) { // PRECONDITIONS @@ -1041,6 +1117,31 @@ void TCPSessionFactory::cancelListeners() d_listenContexts.clear(); } +int TCPSessionFactory::loadTlsConfig(bmqio::NtcChannelFactory* channelFactory, + const mqbcfg::TlsConfig& tlsConfig) +{ + ntca::EncryptionServerOptions encryptionServerOptions; + // Set the minimum version to TLS 1.3 + encryptionServerOptions.setMinMethod(ntca::EncryptionMethod::e_TLS_V1_3); + encryptionServerOptions.setMaxMethod(ntca::EncryptionMethod::e_TLS_V1_3); + // Disable client side authentication (mTLS) + encryptionServerOptions.setAuthentication( + ntca::EncryptionAuthentication::e_NONE); + + encryptionServerOptions.setIdentityFile(tlsConfig.certificate()); + encryptionServerOptions.setPrivateKeyFile(tlsConfig.key()); + encryptionServerOptions.setAuthorityDirectory( + tlsConfig.certificateAuthority()); + + const ntsa::Error err = channelFactory->createEncryptionServer( + &d_encryptionServer_sp, + encryptionServerOptions); + if (err) { + BALL_LOG_ERROR << "While creating encryption server: " << err; + } + return err.code(); +} + int TCPSessionFactory::start(bsl::ostream& errorDescription) { // PRECONDITIONS @@ -1069,9 +1170,17 @@ int TCPSessionFactory::start(bsl::ostream& errorDescription) d_allocator_p), d_allocator_p); - channelFactory->onCreate(bdlf::BindUtil::bind(&ntcChannelPreCreation, - bdlf::PlaceHolders::_1, - bdlf::PlaceHolders::_2)); + const mqbcfg::AppConfig& appConfig = mqbcfg::BrokerConfig::get(); + if (appConfig.tlsConfig().has_value()) { + rc = loadTlsConfig(channelFactory.get(), + appConfig.tlsConfig().value()); + if (rc != 0) { + errorDescription << "Failed to load the TLS configuration " + << "TCPSessionFactory '" << d_config.name() + << "' [rc: " << rc << "]"; + return rc; + } + } rc = channelFactory->start(); if (rc != 0) { @@ -1081,6 +1190,14 @@ int TCPSessionFactory::start(bsl::ostream& errorDescription) return rc; // RETURN } + bdlb::ScopeExitAny ntcChannelFactoryScopeGuard( + bdlf::BindUtil::bind(&bmqio::NtcChannelFactory::stop, + channelFactory.get())); + + channelFactory->onCreate(bdlf::BindUtil::bind(&ntcChannelPreCreation, + bdlf::PlaceHolders::_1, + bdlf::PlaceHolders::_2)); + d_tcpChannelFactory_mp = channelFactory; bdlb::ScopeExitAny tcpScopeGuard( @@ -1181,6 +1298,7 @@ int TCPSessionFactory::start(bsl::ostream& errorDescription) reconnectingScopeGuard.release(); tcpScopeGuard.release(); + ntcChannelFactoryScopeGuard.release(); return 0; } @@ -1238,11 +1356,11 @@ void TCPSessionFactory::stopListening() cancelListeners(); // NOTE: This is done here as a temporary workaround until channels are - // properly stopped (see 'mqba::Application::stop'), because in the - // current shutdown sequence, we 'stopListening()' and then + // properly stopped (see 'mqba::Application::stop'), because in + // the current shutdown sequence, we 'stopListening()' and then // explicitly close each channel one by one in application layer, - // instead of calling 'stop()'; therefore this would not allow the - // optimization to 'bypass' the one-by-one disablement. + // instead of calling 'stop()'; therefore this would not allow + // the optimization to 'bypass' the one-by-one disablement. if (d_heartbeatSchedulerActive) { d_heartbeatSchedulerActive = false; d_scheduler_p->cancelEventAndWait(&d_heartbeatSchedulerHandle); @@ -1290,7 +1408,8 @@ void TCPSessionFactory::closeClients() << "timed out while waiting for clients to close" << ", remaining clients: " << d_nbOpenClients; - // Invalidate the remaining sessions before stopping all Dispatchers. + // Invalidate the remaining sessions before stopping all + // Dispatchers. for (size_t i = 0; i < clients.size(); ++i) { bsl::shared_ptr session = clients[i].lock(); @@ -1317,18 +1436,19 @@ void TCPSessionFactory::stop() << d_nbSessions << " alive sessions]"; // Cancel the heartbeat scheduler event; note that - // 'd_heartbeatSchedulerActive' must be set to false prior to this cancel - // event, so that 'onClose' of the channels will not try to uselessly - // 'disableHeartbeat' on each channel, one-by-one. + // 'd_heartbeatSchedulerActive' must be set to false prior to this + // cancel event, so that 'onClose' of the channels will not try to + // uselessly 'disableHeartbeat' on each channel, one-by-one. if (d_heartbeatSchedulerActive) { d_heartbeatSchedulerActive = false; d_scheduler_p->cancelEventAndWait(&d_heartbeatSchedulerHandle); d_heartbeatChannels.clear(); } - // NOTE: We don't need to manually call 'teardown' on any active session in - // the 'd_channels' map: calling 'stop' on the channel factory will - // invoke the 'onClose' for every sessions. + // NOTE: We don't need to manually call 'teardown' on any active + // session in + // the 'd_channels' map: calling 'stop' on the channel factory + // will invoke the 'onClose' for every sessions. // STOP d_resolutionContext.stop(); @@ -1368,8 +1488,8 @@ void TCPSessionFactory::stop() d_mutex.unlock(); // DESTROY - // We destroy the channel factories here for symmetry since it's created in - // 'start'. + // We destroy the channel factories here for symmetry since it's + // created in 'start'. if (d_statChannelFactory_mp) { d_statChannelFactory_mp.clear(); } @@ -1406,6 +1526,8 @@ int TCPSessionFactory::listen(const mqbcfg::TcpInterfaceListener& listener, context->d_resultCb = resultCallback; context->d_isIncoming = true; context->d_resultState_p = 0; + context->d_isTls = listener.tls(); + context->d_interfaceName = listener.name(); bdlma::LocalSequentialAllocator<64> localAlloc(d_allocator_p); bmqu::MemOutStream endpoint(&localAlloc); @@ -1414,7 +1536,7 @@ int TCPSessionFactory::listen(const mqbcfg::TcpInterfaceListener& listener, listenOptions.setEndpoint(endpoint.str()); bslma::ManagedPtr listeningHandle_mp; - bmqio::Status status; + bmqio::Status status; d_statChannelFactory_mp->listen( &status, &listeningHandle_mp, @@ -1456,6 +1578,7 @@ int TCPSessionFactory::connect(const bslstl::StringRef& endpoint, context->d_resultCb = resultCallback; context->d_isIncoming = false; context->d_resultState_p = resultState; + context->d_isTls = false; if (negotiationUserData) { context->d_negotiationUserData_sp = *negotiationUserData; diff --git a/src/groups/mqb/mqbnet/mqbnet_tcpsessionfactory.h b/src/groups/mqb/mqbnet/mqbnet_tcpsessionfactory.h index efdb77f8e..dd58754a1 100644 --- a/src/groups/mqb/mqbnet/mqbnet_tcpsessionfactory.h +++ b/src/groups/mqb/mqbnet/mqbnet_tcpsessionfactory.h @@ -78,12 +78,12 @@ // stale connection will be dropped after a time of ']12;16]' seconds. // MQB - #include #include #include #include +#include #include #include #include @@ -109,9 +109,26 @@ #include #include #include +#include namespace BloombergLP { +namespace ntci { +class Upgradable; +class EncryptionServer; +class Upgradable; +} + +namespace ntca { +class UpgradeEvent; +} + +// TODO move TLS config loading into ntcchannelfactory + +namespace bmqio { +class NtcChannelFactory; +} + namespace mqbnet { // FORWARD DECLARATION @@ -322,6 +339,21 @@ class TCPSessionFactory { StatChannelFactoryMp d_statChannelFactory_mp; + bool d_useTls; + // Whether TLS enabled or not + + // #review NTC symbols exposed here + bsl::shared_ptr d_certificate_sp; + // TLS: the certificate the broker will use to identify itself + // to other clients. + + bsl::shared_ptr d_certificateAuthority_sp; + // TLS: A concatenation of known certificates the server can use + // to reference as its certificate store. + + bsl::shared_ptr d_key_sp; + // TLS: private key that the broker uses to read the certificate. + bsl::string d_threadName; // Name to use for the IO threads @@ -410,6 +442,12 @@ class TCPSessionFactory { TimestampMap d_timestampMap; // Map of HiRes timestamp of the session beginning per channel. + // TODO: review NTC symbols exposed here + bmqio::CertificateStore d_certificateStore; + + /// The encryption server used to authenticate incoming connections + bsl::shared_ptr d_encryptionServer_sp; + bslma::Allocator* d_allocator_p; // Allocator to use @@ -530,6 +568,29 @@ class TCPSessionFactory { /// Cancel any open listener operations and clear them out. void cancelListeners(); + /// Prepare the channel for negotiation. Handles TLS related connection + /// information. + void + negotiationWithTlsInit(const bsl::shared_ptr& channel, + const bsl::shared_ptr& context, + const bsl::shared_ptr& upgradable, + const ntca::UpgradeEvent& event); + + /// Prepare the channel for negotiation. + void negotiationInit(bsl::shared_ptr channel, + bsl::shared_ptr context); + + /// Check that the TCP interfaces are valid. + /// + /// We require the following: + /// - The names of each network interface is unique + /// - The ports of each network interface is unqiue + int validateNetworkInterfaces() const; + + /// Load TLS configuration from the file system based on the config. + int loadTlsConfig(bmqio::NtcChannelFactory* channelFactory, + const mqbcfg::TlsConfig& tlsConfig); + private: // NOT IMPLEMENTED diff --git a/src/python/blazingmq/dev/configurator/__init__.py b/src/python/blazingmq/dev/configurator/__init__.py index a5508ccab..0f0b0c0fe 100644 --- a/src/python/blazingmq/dev/configurator/__init__.py +++ b/src/python/blazingmq/dev/configurator/__init__.py @@ -99,11 +99,12 @@ def instance(self) -> str: @property def host(self) -> str: - return self.config.app_config.network_interfaces.tcp_interface.name # type: ignore + return self.config.app_config.network_interfaces.tcp_interface.name @property def port(self) -> str: - return self.config.app_config.network_interfaces.tcp_interface.port # type: ignore + return self.config.app_config.network_interfaces.tcp_interface.port + @property def config_dir(self) -> Path: diff --git a/src/python/blazingmq/dev/it/fixtures.py b/src/python/blazingmq/dev/it/fixtures.py index 9d5d3fbd2..b1491b56f 100644 --- a/src/python/blazingmq/dev/it/fixtures.py +++ b/src/python/blazingmq/dev/it/fixtures.py @@ -516,11 +516,13 @@ def single_node_cluster_config( configurator: cfg.Configurator, port_allocator: Iterator[int], mode: Mode ): mode.tweak(configurator.proto.cluster) + tcp_host = "localhost" + tcp_port = next(port_allocator) broker = configurator.broker( name="single", - tcp_host="localhost", - tcp_port=next(port_allocator), + tcp_host=tcp_host, + tcp_port=tcp_port, data_center="single_node", ) @@ -560,14 +562,16 @@ def multi_node_cluster_config( reverse_proxy: bool = False, ) -> None: mode.tweak(configurator.proto.cluster) + tcp_host = "localhost" + tcp_port = next(port_allocator) cluster = configurator.cluster( name="itCluster", nodes=[ configurator.broker( name=f"{data_center}{broker}", - tcp_host="localhost", - tcp_port=next(port_allocator), + tcp_host=tcp_host, + tcp_port=tcp_port, data_center=data_center, ) for data_center in ("east", "west") @@ -578,10 +582,11 @@ def multi_node_cluster_config( add_test_domains(cluster) for data_center in ("east", "west"): + tcp_port = next(port_allocator) configurator.broker( name=f"{data_center}p", - tcp_host="localhost", - tcp_port=next(port_allocator), + tcp_host=tcp_host, + tcp_port=tcp_port, data_center=data_center, ).proxy(cluster, reverse=reverse_proxy) @@ -712,14 +717,16 @@ def virtual_cluster_config( reverse_proxy: bool = False, ) -> None: mode.tweak(configurator.proto.cluster) + tcp_host = "localhost" + tcp_port = next(port_allocator) final_cluster = configurator.cluster( name="itCluster", nodes=[ configurator.broker( name=f"{data_center}{broker}", - tcp_host="localhost", - tcp_port=next(port_allocator), + tcp_host=tcp_host, + tcp_port=tcp_port, data_center=data_center, ) for data_center in ("east", "west") @@ -728,13 +735,14 @@ def virtual_cluster_config( ) add_test_domains(final_cluster) + tcp_port = next(port_allocator) cluster = configurator.virtual_cluster( name="itVirtualCluster", nodes=[ configurator.broker( name=f"{data_center}v", - tcp_host="localhost", - tcp_port=next(port_allocator), + tcp_host=tcp_host, + tcp_port=tcp_port, data_center=data_center, ) for data_center in ("east", "west") @@ -743,10 +751,11 @@ def virtual_cluster_config( cluster.proxy(final_cluster) for data_center in ("east", "west"): + tcp_port = next(port_allocator) configurator.broker( name=f"{data_center}p", - tcp_host="localhost", - tcp_port=next(port_allocator), + tcp_host=tcp_host, + tcp_port=tcp_port, data_center=data_center, ).proxy(cluster, reverse=reverse_proxy) diff --git a/src/python/blazingmq/dev/it/tweaks/generated.py b/src/python/blazingmq/dev/it/tweaks/generated.py index 26a0ff548..720765fde 100644 --- a/src/python/blazingmq/dev/it/tweaks/generated.py +++ b/src/python/blazingmq/dev/it/tweaks/generated.py @@ -40,1505 +40,1304 @@ def __call__(self, tweak: Callable) -> Callable: class Broker: class TaskConfig(metaclass=TweakMetaclass): class AllocatorType(metaclass=TweakMetaclass): - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.AllocatorType, NoneType - ], - ) -> Callable: ... - + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.AllocatorType,NoneType]) -> Callable: + ... allocator_type = AllocatorType() - + class AllocationLimit(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... allocation_limit = AllocationLimit() - + class LogController(metaclass=TweakMetaclass): class FileName(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... file_name = FileName() - + class FileMaxAgeDays(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... file_max_age_days = FileMaxAgeDays() - + class RotationBytes(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... rotation_bytes = RotationBytes() - + class LogfileFormat(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... logfile_format = LogfileFormat() - + class ConsoleFormat(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... console_format = ConsoleFormat() - + class LoggingVerbosity(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... logging_verbosity = LoggingVerbosity() - + class BslsLogSeverityThreshold(metaclass=TweakMetaclass): - - def __call__(self, value: str) -> Callable: ... - + + def __call__(self, value: str) -> Callable: + ... bsls_log_severity_threshold = BslsLogSeverityThreshold() - + class ConsoleSeverityThreshold(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... console_severity_threshold = ConsoleSeverityThreshold() - + class Categories(metaclass=TweakMetaclass): - - def __call__(self, value: None) -> Callable: ... - + + def __call__(self, value: None) -> Callable: + ... categories = Categories() - + class Syslog(metaclass=TweakMetaclass): class Enabled(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... enabled = Enabled() - + class AppName(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... app_name = AppName() - + class LogFormat(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... log_format = LogFormat() - + class Verbosity(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... verbosity = Verbosity() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.SyslogConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.SyslogConfig,NoneType]) -> Callable: + ... syslog = Syslog() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.LogController, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.LogController,NoneType]) -> Callable: + ... log_controller = LogController() - - def __call__( - self, value: typing.Union[blazingmq.schemas.mqbcfg.TaskConfig, NoneType] - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.TaskConfig,NoneType]) -> Callable: + ... task_config = TaskConfig() - + class AppConfig(metaclass=TweakMetaclass): class BrokerInstanceName(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... broker_instance_name = BrokerInstanceName() - + class BrokerVersion(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... broker_version = BrokerVersion() - + class ConfigVersion(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... config_version = ConfigVersion() - + class EtcDir(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... etc_dir = EtcDir() - + class HostName(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... host_name = HostName() - + class HostTags(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... host_tags = HostTags() - + class HostDataCenter(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... host_data_center = HostDataCenter() - + class IsRunningOnDev(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[bool, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[bool,NoneType]) -> Callable: + ... is_running_on_dev = IsRunningOnDev() - + class LogsObserverMaxSize(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... logs_observer_max_size = LogsObserverMaxSize() - + class LatencyMonitorDomain(metaclass=TweakMetaclass): - - def __call__(self, value: str) -> Callable: ... - + + def __call__(self, value: str) -> Callable: + ... latency_monitor_domain = LatencyMonitorDomain() - + class DispatcherConfig(metaclass=TweakMetaclass): class Sessions(metaclass=TweakMetaclass): class NumProcessors(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... num_processors = NumProcessors() - + class ProcessorConfig(metaclass=TweakMetaclass): class QueueSize(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size = QueueSize() - + class QueueSizeLowWatermark(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size_low_watermark = QueueSizeLowWatermark() - + class QueueSizeHighWatermark(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size_high_watermark = QueueSizeHighWatermark() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.DispatcherProcessorParameters, - NoneType, - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.DispatcherProcessorParameters,NoneType]) -> Callable: + ... processor_config = ProcessorConfig() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.DispatcherProcessorConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.DispatcherProcessorConfig,NoneType]) -> Callable: + ... sessions = Sessions() - + class Queues(metaclass=TweakMetaclass): class NumProcessors(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... num_processors = NumProcessors() - + class ProcessorConfig(metaclass=TweakMetaclass): class QueueSize(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size = QueueSize() - + class QueueSizeLowWatermark(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size_low_watermark = QueueSizeLowWatermark() - + class QueueSizeHighWatermark(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size_high_watermark = QueueSizeHighWatermark() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.DispatcherProcessorParameters, - NoneType, - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.DispatcherProcessorParameters,NoneType]) -> Callable: + ... processor_config = ProcessorConfig() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.DispatcherProcessorConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.DispatcherProcessorConfig,NoneType]) -> Callable: + ... queues = Queues() - + class Clusters(metaclass=TweakMetaclass): class NumProcessors(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... num_processors = NumProcessors() - + class ProcessorConfig(metaclass=TweakMetaclass): class QueueSize(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size = QueueSize() - + class QueueSizeLowWatermark(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size_low_watermark = QueueSizeLowWatermark() - + class QueueSizeHighWatermark(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... queue_size_high_watermark = QueueSizeHighWatermark() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.DispatcherProcessorParameters, - NoneType, - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.DispatcherProcessorParameters,NoneType]) -> Callable: + ... processor_config = ProcessorConfig() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.DispatcherProcessorConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.DispatcherProcessorConfig,NoneType]) -> Callable: + ... clusters = Clusters() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.DispatcherConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.DispatcherConfig,NoneType]) -> Callable: + ... dispatcher_config = DispatcherConfig() - + class Stats(metaclass=TweakMetaclass): class SnapshotInterval(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... snapshot_interval = SnapshotInterval() - + class Plugins(metaclass=TweakMetaclass): class Name(metaclass=TweakMetaclass): - - def __call__(self, value: str) -> Callable: ... - + + def __call__(self, value: str) -> Callable: + ... name = Name() - + class QueueSize(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... queue_size = QueueSize() - + class QueueHighWatermark(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... queue_high_watermark = QueueHighWatermark() - + class QueueLowWatermark(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... queue_low_watermark = QueueLowWatermark() - + class PublishInterval(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... publish_interval = PublishInterval() - + class NamespacePrefix(metaclass=TweakMetaclass): - - def __call__(self, value: str) -> Callable: ... - + + def __call__(self, value: str) -> Callable: + ... namespace_prefix = NamespacePrefix() - + class Hosts(metaclass=TweakMetaclass): - - def __call__(self, value: None) -> Callable: ... - + + def __call__(self, value: None) -> Callable: + ... hosts = Hosts() - + class InstanceId(metaclass=TweakMetaclass): - - def __call__(self, value: str) -> Callable: ... - + + def __call__(self, value: str) -> Callable: + ... instance_id = InstanceId() - + class PrometheusSpecific(metaclass=TweakMetaclass): class Mode(metaclass=TweakMetaclass): - - def __call__( - self, value: blazingmq.schemas.mqbcfg.ExportMode - ) -> Callable: ... - + + def __call__(self, value: blazingmq.schemas.mqbcfg.ExportMode) -> Callable: + ... mode = Mode() - + class Host(metaclass=TweakMetaclass): - - def __call__(self, value: str) -> Callable: ... - + + def __call__(self, value: str) -> Callable: + ... host = Host() - + class Port(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... port = Port() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.StatPluginConfigPrometheus, - NoneType, - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.StatPluginConfigPrometheus,NoneType]) -> Callable: + ... prometheus_specific = PrometheusSpecific() - - def __call__(self, value: None) -> Callable: ... - + + + def __call__(self, value: None) -> Callable: + ... plugins = Plugins() - + class Printer(metaclass=TweakMetaclass): class PrintInterval(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... print_interval = PrintInterval() - + class File(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... file = File() - + class MaxAgeDays(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... max_age_days = MaxAgeDays() - + class RotateBytes(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... rotate_bytes = RotateBytes() - + class RotateDays(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... rotate_days = RotateDays() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.StatsPrinterConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.StatsPrinterConfig,NoneType]) -> Callable: + ... printer = Printer() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbcfg.StatsConfig, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.StatsConfig,NoneType]) -> Callable: + ... stats = Stats() - + class NetworkInterfaces(metaclass=TweakMetaclass): class Heartbeats(metaclass=TweakMetaclass): class Client(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... client = Client() - + class DownstreamBroker(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... downstream_broker = DownstreamBroker() - + class UpstreamBroker(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... upstream_broker = UpstreamBroker() - + class ClusterPeer(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... cluster_peer = ClusterPeer() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.Heartbeat, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.Heartbeat,NoneType]) -> Callable: + ... heartbeats = Heartbeats() - + class TcpInterface(metaclass=TweakMetaclass): class Name(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... name = Name() - + class Port(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... port = Port() - + class IoThreads(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... io_threads = IoThreads() - + class MaxConnections(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_connections = MaxConnections() - + class LowWatermark(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... low_watermark = LowWatermark() - + class HighWatermark(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... high_watermark = HighWatermark() - + class NodeLowWatermark(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... node_low_watermark = NodeLowWatermark() - + class NodeHighWatermark(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... node_high_watermark = NodeHighWatermark() - + class HeartbeatIntervalMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... heartbeat_interval_ms = HeartbeatIntervalMs() - + class Listeners(metaclass=TweakMetaclass): class Name(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... name = Name() - + class Port(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... port = Port() - - def __call__(self, value: None) -> Callable: ... - + + class Tls(metaclass=TweakMetaclass): + + def __call__(self, value: bool) -> Callable: + ... + tls = Tls() + + + def __call__(self, value: None) -> Callable: + ... listeners = Listeners() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.TcpInterfaceConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.TcpInterfaceConfig,NoneType]) -> Callable: + ... tcp_interface = TcpInterface() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.NetworkInterfaces, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.NetworkInterfaces,NoneType]) -> Callable: + ... network_interfaces = NetworkInterfaces() - + class BmqconfConfig(metaclass=TweakMetaclass): class CacheTtlseconds(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... cache_ttlseconds = CacheTtlseconds() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.BmqconfConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.BmqconfConfig,NoneType]) -> Callable: + ... bmqconf_config = BmqconfConfig() - + class Plugins(metaclass=TweakMetaclass): class Libraries(metaclass=TweakMetaclass): - - def __call__(self, value: None) -> Callable: ... - + + def __call__(self, value: None) -> Callable: + ... libraries = Libraries() - + class Enabled(metaclass=TweakMetaclass): - - def __call__(self, value: None) -> Callable: ... - + + def __call__(self, value: None) -> Callable: + ... enabled = Enabled() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbcfg.Plugins, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.Plugins,NoneType]) -> Callable: + ... plugins = Plugins() - + class MessagePropertiesV2(metaclass=TweakMetaclass): class AdvertiseV2Support(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... advertise_v2_support = AdvertiseV2Support() - + class MinCppSdkVersion(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... min_cpp_sdk_version = MinCppSdkVersion() - + class MinJavaSdkVersion(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... min_java_sdk_version = MinJavaSdkVersion() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.MessagePropertiesV2, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.MessagePropertiesV2,NoneType]) -> Callable: + ... message_properties_v2 = MessagePropertiesV2() - + class ConfigureStream(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... configure_stream = ConfigureStream() - + class AdvertiseSubscriptions(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... advertise_subscriptions = AdvertiseSubscriptions() - - def __call__( - self, value: typing.Union[blazingmq.schemas.mqbcfg.AppConfig, NoneType] - ) -> Callable: ... - + + class RouteCommandTimeoutMs(metaclass=TweakMetaclass): + + def __call__(self, value: int) -> Callable: + ... + route_command_timeout_ms = RouteCommandTimeoutMs() + + class TlsConfig(metaclass=TweakMetaclass): + class CertificateAuthority(metaclass=TweakMetaclass): + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... + certificate_authority = CertificateAuthority() + + class Certificate(metaclass=TweakMetaclass): + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... + certificate = Certificate() + + class Key(metaclass=TweakMetaclass): + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... + key = Key() + + class Versions(metaclass=TweakMetaclass): + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... + versions = Versions() + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.TlsConfig,NoneType]) -> Callable: + ... + tls_config = TlsConfig() + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.AppConfig,NoneType]) -> Callable: + ... app_config = AppConfig() - + class Domain: class Name(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... name = Name() - + class Mode(metaclass=TweakMetaclass): class Fanout(metaclass=TweakMetaclass): class AppIds(metaclass=TweakMetaclass): - - def __call__(self, value: None) -> Callable: ... - + + def __call__(self, value: None) -> Callable: + ... app_ids = AppIds() - + class PublishAppIdMetrics(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... publish_app_id_metrics = PublishAppIdMetrics() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.QueueModeFanout, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.QueueModeFanout,NoneType]) -> Callable: + ... fanout = Fanout() - + class Priority(metaclass=TweakMetaclass): - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.QueueModePriority, NoneType - ], - ) -> Callable: ... - + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.QueueModePriority,NoneType]) -> Callable: + ... priority = Priority() - + class Broadcast(metaclass=TweakMetaclass): - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.QueueModeBroadcast, NoneType - ], - ) -> Callable: ... - + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.QueueModeBroadcast,NoneType]) -> Callable: + ... broadcast = Broadcast() - - def __call__( - self, value: typing.Union[blazingmq.schemas.mqbconf.QueueMode, NoneType] - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.QueueMode,NoneType]) -> Callable: + ... mode = Mode() - + class Storage(metaclass=TweakMetaclass): class DomainLimits(metaclass=TweakMetaclass): class Messages(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... messages = Messages() - + class MessagesWatermarkRatio(metaclass=TweakMetaclass): - - def __call__(self, value: decimal.Decimal) -> Callable: ... - + + def __call__(self, value: decimal.Decimal) -> Callable: + ... messages_watermark_ratio = MessagesWatermarkRatio() - + class Bytes(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... bytes = Bytes() - + class BytesWatermarkRatio(metaclass=TweakMetaclass): - - def __call__(self, value: decimal.Decimal) -> Callable: ... - + + def __call__(self, value: decimal.Decimal) -> Callable: + ... bytes_watermark_ratio = BytesWatermarkRatio() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbconf.Limits, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.Limits,NoneType]) -> Callable: + ... domain_limits = DomainLimits() - + class QueueLimits(metaclass=TweakMetaclass): class Messages(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... messages = Messages() - + class MessagesWatermarkRatio(metaclass=TweakMetaclass): - - def __call__(self, value: decimal.Decimal) -> Callable: ... - + + def __call__(self, value: decimal.Decimal) -> Callable: + ... messages_watermark_ratio = MessagesWatermarkRatio() - + class Bytes(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[int, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... bytes = Bytes() - + class BytesWatermarkRatio(metaclass=TweakMetaclass): - - def __call__(self, value: decimal.Decimal) -> Callable: ... - + + def __call__(self, value: decimal.Decimal) -> Callable: + ... bytes_watermark_ratio = BytesWatermarkRatio() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbconf.Limits, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.Limits,NoneType]) -> Callable: + ... queue_limits = QueueLimits() - + class Config(metaclass=TweakMetaclass): class InMemory(metaclass=TweakMetaclass): - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.InMemoryStorage, NoneType - ], - ) -> Callable: ... - + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.InMemoryStorage,NoneType]) -> Callable: + ... in_memory = InMemory() - + class FileBacked(metaclass=TweakMetaclass): - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.FileBackedStorage, NoneType - ], - ) -> Callable: ... - + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.FileBackedStorage,NoneType]) -> Callable: + ... file_backed = FileBacked() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbconf.Storage, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.Storage,NoneType]) -> Callable: + ... config = Config() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.StorageDefinition, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.StorageDefinition,NoneType]) -> Callable: + ... storage = Storage() - + class MaxConsumers(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_consumers = MaxConsumers() - + class MaxProducers(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_producers = MaxProducers() - + class MaxQueues(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_queues = MaxQueues() - + class MsgGroupIdConfig(metaclass=TweakMetaclass): class Rebalance(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... rebalance = Rebalance() - + class MaxGroups(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_groups = MaxGroups() - + class TtlSeconds(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... ttl_seconds = TtlSeconds() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.MsgGroupIdConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.MsgGroupIdConfig,NoneType]) -> Callable: + ... msg_group_id_config = MsgGroupIdConfig() - + class MaxIdleTime(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_idle_time = MaxIdleTime() - + class MessageTtl(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... message_ttl = MessageTtl() - + class MaxDeliveryAttempts(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_delivery_attempts = MaxDeliveryAttempts() - + class DeduplicationTimeMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... deduplication_time_ms = DeduplicationTimeMs() - + class Consistency(metaclass=TweakMetaclass): class Eventual(metaclass=TweakMetaclass): - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.QueueConsistencyEventual, NoneType - ], - ) -> Callable: ... - + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.QueueConsistencyEventual,NoneType]) -> Callable: + ... eventual = Eventual() - + class Strong(metaclass=TweakMetaclass): - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbconf.QueueConsistencyStrong, NoneType - ], - ) -> Callable: ... - + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.QueueConsistencyStrong,NoneType]) -> Callable: + ... strong = Strong() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbconf.Consistency, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.Consistency,NoneType]) -> Callable: + ... consistency = Consistency() - + class Subscriptions(metaclass=TweakMetaclass): class AppId(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... app_id = AppId() - + class Expression(metaclass=TweakMetaclass): class Version(metaclass=TweakMetaclass): - - def __call__( - self, value: blazingmq.schemas.mqbconf.ExpressionVersion - ) -> Callable: ... - + + def __call__(self, value: blazingmq.schemas.mqbconf.ExpressionVersion) -> Callable: + ... version = Version() - + class Text(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... text = Text() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbconf.Expression, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbconf.Expression,NoneType]) -> Callable: + ... expression = Expression() - - def __call__(self, value: None) -> Callable: ... - + + + def __call__(self, value: None) -> Callable: + ... subscriptions = Subscriptions() - + class Cluster: class Name(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... name = Name() - + class Nodes(metaclass=TweakMetaclass): class Id(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... id = Id() - + class Name(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... name = Name() - + class DataCenter(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... data_center = DataCenter() - + class Transport(metaclass=TweakMetaclass): class Tcp(metaclass=TweakMetaclass): class Endpoint(metaclass=TweakMetaclass): - - def __call__( - self, value: typing.Union[str, NoneType] - ) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... endpoint = Endpoint() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.TcpClusterNodeConnection, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.TcpClusterNodeConnection,NoneType]) -> Callable: + ... tcp = Tcp() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.ClusterNodeConnection, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.ClusterNodeConnection,NoneType]) -> Callable: + ... transport = Transport() - - def __call__(self, value: None) -> Callable: ... - + + + def __call__(self, value: None) -> Callable: + ... nodes = Nodes() - + class PartitionConfig(metaclass=TweakMetaclass): class NumPartitions(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... num_partitions = NumPartitions() - + class Location(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... location = Location() - + class ArchiveLocation(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[str, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[str,NoneType]) -> Callable: + ... archive_location = ArchiveLocation() - + class MaxDataFileSize(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... max_data_file_size = MaxDataFileSize() - + class MaxJournalFileSize(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... max_journal_file_size = MaxJournalFileSize() - + class MaxQlistFileSize(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... max_qlist_file_size = MaxQlistFileSize() - + class Preallocate(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... preallocate = Preallocate() - + class MaxArchivedFileSets(metaclass=TweakMetaclass): - - def __call__(self, value: typing.Union[int, NoneType]) -> Callable: ... - + + def __call__(self, value: typing.Union[int,NoneType]) -> Callable: + ... max_archived_file_sets = MaxArchivedFileSets() - + class PrefaultPages(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... prefault_pages = PrefaultPages() - + class FlushAtShutdown(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... flush_at_shutdown = FlushAtShutdown() - + class SyncConfig(metaclass=TweakMetaclass): class StartupRecoveryMaxDurationMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... startup_recovery_max_duration_ms = StartupRecoveryMaxDurationMs() - + class MaxAttemptsStorageSync(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_attempts_storage_sync = MaxAttemptsStorageSync() - + class StorageSyncReqTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... storage_sync_req_timeout_ms = StorageSyncReqTimeoutMs() - + class MasterSyncMaxDurationMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... master_sync_max_duration_ms = MasterSyncMaxDurationMs() - + class PartitionSyncStateReqTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... partition_sync_state_req_timeout_ms = PartitionSyncStateReqTimeoutMs() - + class PartitionSyncDataReqTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... partition_sync_data_req_timeout_ms = PartitionSyncDataReqTimeoutMs() - + class StartupWaitDurationMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... startup_wait_duration_ms = StartupWaitDurationMs() - + class FileChunkSize(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... file_chunk_size = FileChunkSize() - + class PartitionSyncEventSize(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... partition_sync_event_size = PartitionSyncEventSize() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.StorageSyncConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.StorageSyncConfig,NoneType]) -> Callable: + ... sync_config = SyncConfig() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbcfg.PartitionConfig, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.PartitionConfig,NoneType]) -> Callable: + ... partition_config = PartitionConfig() - + class MasterAssignment(metaclass=TweakMetaclass): - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.MasterAssignmentAlgorithm, NoneType - ], - ) -> Callable: ... - + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.MasterAssignmentAlgorithm,NoneType]) -> Callable: + ... master_assignment = MasterAssignment() - + class Elector(metaclass=TweakMetaclass): class InitialWaitTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... initial_wait_timeout_ms = InitialWaitTimeoutMs() - + class MaxRandomWaitTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_random_wait_timeout_ms = MaxRandomWaitTimeoutMs() - + class ScoutingResultTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... scouting_result_timeout_ms = ScoutingResultTimeoutMs() - + class ElectionResultTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... election_result_timeout_ms = ElectionResultTimeoutMs() - + class HeartbeatBroadcastPeriodMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... heartbeat_broadcast_period_ms = HeartbeatBroadcastPeriodMs() - + class HeartbeatCheckPeriodMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... heartbeat_check_period_ms = HeartbeatCheckPeriodMs() - + class HeartbeatMissCount(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... heartbeat_miss_count = HeartbeatMissCount() - + class Quorum(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... quorum = Quorum() - + class LeaderSyncDelayMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... leader_sync_delay_ms = LeaderSyncDelayMs() - - def __call__( - self, - value: typing.Union[blazingmq.schemas.mqbcfg.ElectorConfig, NoneType], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.ElectorConfig,NoneType]) -> Callable: + ... elector = Elector() - + class QueueOperations(metaclass=TweakMetaclass): class OpenTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... open_timeout_ms = OpenTimeoutMs() - + class ConfigureTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... configure_timeout_ms = ConfigureTimeoutMs() - + class CloseTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... close_timeout_ms = CloseTimeoutMs() - + class ReopenTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... reopen_timeout_ms = ReopenTimeoutMs() - + class ReopenRetryIntervalMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... reopen_retry_interval_ms = ReopenRetryIntervalMs() - + class ReopenMaxAttempts(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... reopen_max_attempts = ReopenMaxAttempts() - + class AssignmentTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... assignment_timeout_ms = AssignmentTimeoutMs() - + class KeepaliveDurationMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... keepalive_duration_ms = KeepaliveDurationMs() - + class ConsumptionMonitorPeriodMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... consumption_monitor_period_ms = ConsumptionMonitorPeriodMs() - + class StopTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... stop_timeout_ms = StopTimeoutMs() - + class ShutdownTimeoutMs(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... shutdown_timeout_ms = ShutdownTimeoutMs() - + class AckWindowSize(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... ack_window_size = AckWindowSize() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.QueueOperationsConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.QueueOperationsConfig,NoneType]) -> Callable: + ... queue_operations = QueueOperations() - + class ClusterAttributes(metaclass=TweakMetaclass): class IsCslmodeEnabled(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... is_cslmode_enabled = IsCslmodeEnabled() - + class IsFsmworkflow(metaclass=TweakMetaclass): - - def __call__(self, value: bool) -> Callable: ... - + + def __call__(self, value: bool) -> Callable: + ... is_fsmworkflow = IsFsmworkflow() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.ClusterAttributes, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.ClusterAttributes,NoneType]) -> Callable: + ... cluster_attributes = ClusterAttributes() - + class ClusterMonitorConfig(metaclass=TweakMetaclass): class MaxTimeLeader(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_time_leader = MaxTimeLeader() - + class MaxTimeMaster(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_time_master = MaxTimeMaster() - + class MaxTimeNode(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_time_node = MaxTimeNode() - + class MaxTimeFailover(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... max_time_failover = MaxTimeFailover() - + class ThresholdLeader(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... threshold_leader = ThresholdLeader() - + class ThresholdMaster(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... threshold_master = ThresholdMaster() - + class ThresholdNode(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... threshold_node = ThresholdNode() - + class ThresholdFailover(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... threshold_failover = ThresholdFailover() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.ClusterMonitorConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.ClusterMonitorConfig,NoneType]) -> Callable: + ... cluster_monitor_config = ClusterMonitorConfig() - + class MessageThrottleConfig(metaclass=TweakMetaclass): class LowThreshold(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... low_threshold = LowThreshold() - + class HighThreshold(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... high_threshold = HighThreshold() - + class LowInterval(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... low_interval = LowInterval() - + class HighInterval(metaclass=TweakMetaclass): - - def __call__(self, value: int) -> Callable: ... - + + def __call__(self, value: int) -> Callable: + ... high_interval = HighInterval() - - def __call__( - self, - value: typing.Union[ - blazingmq.schemas.mqbcfg.MessageThrottleConfig, NoneType - ], - ) -> Callable: ... - + + + def __call__(self, value: typing.Union[blazingmq.schemas.mqbcfg.MessageThrottleConfig,NoneType]) -> Callable: + ... message_throttle_config = MessageThrottleConfig() + broker = Broker() domain = Domain() cluster = Cluster() + diff --git a/src/python/blazingmq/schemas/mqbcfg.py b/src/python/blazingmq/schemas/mqbcfg.py index 547c6cca0..9dd5e3592 100644 --- a/src/python/blazingmq/schemas/mqbcfg.py +++ b/src/python/blazingmq/schemas/mqbcfg.py @@ -979,6 +979,8 @@ class TcpInterfaceListener: A name to associate this listener to. port.................: The port this listener will accept connections on. + tls..................: + Use TLS on this interface. """ name: Optional[str] = field( @@ -997,6 +999,65 @@ class TcpInterfaceListener: "required": True, }, ) + tls: bool = field( + default=False, + metadata={ + "type": "Element", + "namespace": "http://bloomberg.com/schemas/mqbcfg", + "required": True, + }, + ) + + +@dataclass +class TlsConfig: + """certificateAuthority.: + + A path to the FILE, containing concatenation of known certificates + the server can use to reference as its certificate store. + certificate..........: + A path to the FILE, containing the certificate the broker will use + to identify itself to other clients. + key..................: + A path to the FILE, containing the private key that the broker uses + to read the certificate. + versions.............: + A string with a comma-separated list of supported protocol versions. + """ + + certificate_authority: Optional[str] = field( + default=None, + metadata={ + "name": "certificateAuthority", + "type": "Element", + "namespace": "http://bloomberg.com/schemas/mqbcfg", + "required": True, + }, + ) + certificate: Optional[str] = field( + default=None, + metadata={ + "type": "Element", + "namespace": "http://bloomberg.com/schemas/mqbcfg", + "required": True, + }, + ) + key: Optional[str] = field( + default=None, + metadata={ + "type": "Element", + "namespace": "http://bloomberg.com/schemas/mqbcfg", + "required": True, + }, + ) + versions: Optional[str] = field( + default=None, + metadata={ + "type": "Element", + "namespace": "http://bloomberg.com/schemas/mqbcfg", + "required": True, + }, + ) @dataclass @@ -1855,7 +1916,9 @@ class AppConfig: plugins..............: configuration for the plugins msgPropertiesSupport.: information about if/how to advertise support for v2 message properties configureStream......: send new ConfigureStream instead of old ConfigureQueue - advertiseSubscriptions.: temporarily control use of ConfigureStream in SDK/> + advertiseSubscriptions.: temporarily control use of ConfigureStream in SDK + routeCommandTimeoutMs: maximum amount of time to wait for a routed command's response + tlsConfig............: optional configuation for TLS """ broker_instance_name: Optional[str] = field( @@ -2018,6 +2081,23 @@ class AppConfig: "required": True, }, ) + route_command_timeout_ms: int = field( + default=3000, + metadata={ + "name": "routeCommandTimeoutMs", + "type": "Element", + "namespace": "http://bloomberg.com/schemas/mqbcfg", + "required": True, + }, + ) + tls_config: Optional[TlsConfig] = field( + default=None, + metadata={ + "name": "tlsConfig", + "type": "Element", + "namespace": "http://bloomberg.com/schemas/mqbcfg", + }, + ) @dataclass