Add PROXYv2 header utilities

This commit adds a set of utilities for dealing with PROXYv2 headers,
both parsing and generating them. The code has no dependencies from
the networking code and is (for the most part) a "separate library".

The part responsible for handling incoming PROXYv2 headers is
structured as a state machine which accepts data as input and calls a
callback to notify the upper-level code about the data processing
status.

Such a design, among other things, makes it easy to write a thorough
unit test suite for that, as there are fewer dependencies as well as
will not stand in the way of any changes in the networking code.
This commit is contained in:
Artem Boldariev
2023-03-03 16:42:47 +02:00
parent 2653e668e3
commit 2c76717881
3 changed files with 2297 additions and 0 deletions

View File

@@ -64,6 +64,7 @@ libisc_la_HEADERS = \
include/isc/parseint.h \
include/isc/pause.h \
include/isc/portset.h \
include/isc/proxy2.h \
include/isc/quota.h \
include/isc/radix.h \
include/isc/random.h \
@@ -170,6 +171,7 @@ libisc_la_SOURCES = \
picohttpparser.h \
portset.c \
probes.d \
proxy2.c \
quota.c \
radix.c \
random.c \

View File

@@ -0,0 +1,855 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#pragma once
#include <isc/buffer.h>
#include <isc/mem.h>
#include <isc/sockaddr.h>
/* Definitions taken or derived from the specification */
#define ISC_PROXY2_HEADER_SIGNATURE \
("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A")
#define ISC_PROXY2_HEADER_SIGNATURE_SIZE (12)
#define ISC_PROXY2_HEADER_SIZE \
(ISC_PROXY2_HEADER_SIGNATURE_SIZE + 1 /* version and command */ + \
1 /* protocol and family */ + 2 /* data size */)
#define ISC_PROXY2_MAX_SIZE (ISC_PROXY2_HEADER_SIZE + UINT16_MAX)
#define ISC_PROXY2_MIN_AF_INET_SIZE \
(ISC_PROXY2_HEADER_SIZE + 4 /* src_addr */ + 4 /* dst_addr */ + \
2 /* src port */ + 2 /* dst_port */)
#define ISC_PROXY2_MIN_AF_INET6_SIZE \
(ISC_PROXY2_HEADER_SIZE + 16 /* src_addr */ + 16 /* dst_addr */ + \
2 /* src port */ + 2 /* dst_port */)
#define ISC_PROXY2_AF_UNIX_MAX_PATH_LEN (108)
#define ISC_PROXY2_MIN_AF_UNIX_SIZE \
(ISC_PROXY2_HEADER_SIZE + \
ISC_PROXY2_AF_UNIX_MAX_PATH_LEN /* src_addr */ + \
ISC_PROXY2_AF_UNIX_MAX_PATH_LEN /* dst_addr */)
#define ISC_PROXY2_TLV_HEADER_SIZE \
(1 /* type */ + 1 /* length_hi */ + 1 /* length_lo */)
#define ISC_PROXY2_TLS_SUBHEADER_MIN_SIZE \
(1 /* client_flags */ + 4 /* verify */)
ISC_LANG_BEGINDECLS
typedef enum isc_proxy2_command {
ISC_PROXY2_CMD_ILLEGAL = -1,
/*
* PROXYv2 header does not contain any addresses and is supposedly
* created on behalf of locally running software.
*/
ISC_PROXY2_CMD_LOCAL = 0,
/*
* PROXYv2 header contains address-related information and is
* created on a behalf of the client.
*/
ISC_PROXY2_CMD_PROXY = 1
} isc_proxy2_command_t;
typedef enum isc_proxy2_addrfamily {
ISC_PROXY2_AF_UNSPEC = 0,
ISC_PROXY2_AF_INET = 1,
ISC_PROXY2_AF_INET6 = 2,
ISC_PROXY2_AF_UNIX = 3
} isc_proxy2_addrfamily_t;
typedef enum isc_proxy2_socktype {
ISC_PROXY2_SOCK_ILLEGAL = -1,
ISC_PROXY2_SOCK_UNSPEC = 0,
ISC_PROXY2_SOCK_STREAM = 1,
ISC_PROXY2_SOCK_DGRAM = 2
} isc_proxy2_socktype_t;
typedef enum isc_proxy2_tlv_type {
/*
* Application-Layer Protocol Negotiation (ALPN). It is a byte
* sequence defining the upper layer protocol in use over the
* connection.
*/
ISC_PROXY2_TLV_TYPE_ALPN = 0x01,
/*
* Contains the host name value passed by the client, as an
* UTF8-encoded string.
*/
ISC_PROXY2_TLV_TYPE_AUTHORITY = 0x02,
/*
* The value is a 32-bit number storing the CRC32c checksum of the
* PROXY protocol header.
*/
ISC_PROXY2_TLV_TYPE_CRC32C = 0x03,
/*
* The TLV of this type should be ignored when parsed. The value
* is zero or more bytes. Can be used for data padding or
* alignment.
*/
ISC_PROXY2_TLV_TYPE_NOOP = 0x04,
/*
* The value is an opaque byte sequence of up to 128 bytes
* generated by the upstream proxy that uniquely identifies the
* connection.
*/
ISC_PROXY2_TLV_TYPE_UNIQUE_ID = 0x05,
/*
* SSL type contains subfields of the given subtypes (see
* isc_proxy2_tlv_subtype_tls_t). The header contains:
*
* - uint8_t client is a bit-field made of
* isc_proxy2_tls_client_flags_t;
* - uint32_t verify (0 for a successfully verified certificate);
*/
ISC_PROXY2_TLV_TYPE_TLS = 0x20,
/*
* The type PP2_TYPE_NETNS defines the value as the US-ASCII
* string representation of the namespace's name.
*/
ISC_PROXY2_TLV_TYPE_NETNS = 0x30,
/*
* The following range of 16 type values is reserved for
* application-specific data and will be never used by the PROXY
* Protocol.
*/
ISC_PROXY2_TLV_TYPE_MIN_CUSTOM = 0xE0,
ISC_PROXY2_TLV_TYPE_MAX_CUSTOM = 0xEF,
/*
* This range of 8 values is reserved for temporary experimental
* use by application developers and protocol designers.
*/
ISC_PROXY2_TLV_TYPE_MIN_EXPERIMENT = 0xF0,
ISC_PROXY2_TLV_TYPE_MAX_EXPERIMENT = 0xF7,
/*
* The following range of 8 values is reserved for future use,
* potentially to extend the protocol with multibyte type values.
*/
ISC_PROXY2_TLV_TYPE_MIN_FUTURE = 0xF8,
ISC_PROXY2_TLV_TYPE_MAX_FUTURE = 0xFF
} isc_proxy2_tlv_type_t;
typedef enum isc_proxy2_tls_client_flags {
/* The flag indicates that the client connected over SSL/TLS. */
ISC_PROXY2_CLIENT_TLS = 0x01,
/* The client provided a certificate over the current connection. */
ISC_PROXY2_CLIENT_CERT_CONN = 0x02,
/*
* The client provided a certificate at least once over the TLS
* session this connection belongs to.
*/
ISC_PROXY2_CLIENT_CERT_SESS = 0x04
} isc_proxy2_tls_client_flags_t;
typedef enum isc_proxy2_tlv_subtype_tls {
/*
* The US-ASCII string representation of the TLS version the TLV
* format.
*/
ISC_PROXY2_TLV_SUBTYPE_TLS_VERSION = 0x21,
/*
* The Common Name field of the client certificate's Distinguished
* Name in the TLV format.
*/
ISC_PROXY2_TLV_SUBTYPE_TLS_CN = 0x22,
/*
* The US-ASCII string name of the used cipher, for
* example "ECDHE-RSA-AES128-GCM-SHA256".
*/
ISC_PROXY2_TLV_SUBTYPE_TLS_CIPHER = 0x23,
/*
* The US-ASCII string name of the algorithm used to sign the
* certificate presented by the frontend when the incoming
* connection was made over an SSL/TLS transport layer, for
* example "SHA256".
*/
ISC_PROXY2_TLV_SUBTYPE_TLS_SIG_ALG = 0x24,
/*
* The US-ASCII string name of the algorithm used to generate the
* key of the certificate presented by the frontend when the
* incoming connection was made over an SSL/TLS transport layer,
* for example "RSA2048".
*/
ISC_PROXY2_TLV_SUBTYPE_TLS_KEY_ALG = 0x25
} isc_proxy2_tlv_subtype_tls_t;
/*
* Definitions related to processing and verification of existing PROXYv2
* headers
*/
typedef struct isc_proxy2_handler isc_proxy2_handler_t;
/*!<
* 'isc_proxy2_handler_t' is an entity designed for processing of the
* PROXYv2 data received from a network. Despite its purpose, it is
* designed as a state machine which, in fact, has no direct connection
* to the networking code. Interaction with the networking code is done
* via the provided API only.
*
* The entity is designed as a state machine which accepts data on
* input and calls a user-provided data processing callback to notify
* about data processing status and, in the case of successful
* processing, provide the upper level code with the data obtained
* from the PROXYv2 header and associated payload.
*
* The reason for the state machine-based approach is
* many-fold. Firstly, the protocol itself is well suited for
* processing by a state machine with well-defined steps. Secondly,
* such a design allows iterative data processing combined with
* verification, which is more secure than trying to read seemingly
* enough data, process it, and then retrospectively verify
* it. Thirdly, such an approach aligns well with how stream-based
* transports work - PROXYv2 headers might arrive torn into multiple
* parts which need to be assembled (theoretically, it is fine to send
* data over TCP bite-by-bite), and we should handle such
* cases. Additionally to that, we should stop reading data as soon as
* we detect that it is ill-formed. This design allows that and also
* can be used easily with datagram-based networking code.
*
* Another important characteristic of the state machine-based code is
* that it can be unit-tested separately from the rest of the code
* base. Of course, we use that to our advantage.
*
* The implementations closely follows the PROXYv2 protocol
* specification from 2020/03/05
* (https://www.haproxy.org/download/2.9/doc/proxy-protocol.txt),
* however, enough functionality is provided to handle future
* extensions, too. To be fair, our needs are quite modest and we are
* not interested in all the information PROXYv2 protocol could carry
* - we are mostly interested in the basics. However, our protocol
* handling code is fairly complete at the time of writing leaving a
* good foundation for further extensions, as the PROXYv2 protocol is
* itself extensible. The only missing thing is header checksum
* verification - but that functionality is optional. That being said, it is
* easy to add that, should we ever need to.
*/
typedef void (*isc_proxy2_handler_cb_t)(const isc_result_t result,
const isc_proxy2_command_t cmd,
const int socktype,
const isc_sockaddr_t *restrict src_addr,
const isc_sockaddr_t *restrict dst_addr,
const isc_region_t *restrict tlv_data,
const isc_region_t *restrict extra,
void *cbarg);
/*!<
* PROXYv2 data processing callback.
*
* Arguments:
*\li 'result' - error status code;
*\li 'cmd' - PROXYv2 command;
*\li 'socktype' - PROXYv2 addresses socket type
*(SOCK_STREAM, SOCK_DGRAM, or '0' for "unspecified" (SOCK_UNSPEC)).
*\li 'src_addr' - original source address extracted from the PROXYv2 header;
*\li 'dst_addr' - original destination address extracted from the PROXYv2
*header;
*\li 'tlv_data' - TLV-data extracted from the header;
*\li 'extra_data' - extra unprocessed data past the PROXYv2 header. It is
*not a part of the header, but it is fine to receive this when reading data
*over TCP-based transports. In general, needs to be passed to the upper
*level as is;
*\li 'cbarg' - opaque pointer to user supplied data.
*
* The user-provided data processing callback function can get the
* following error status codes:
* \li 'ISC_R_SUCCESS' - the header has been processed, and data has been
* extracted from the received header and its payload;
* \li 'ISC_R_NOMORE' - the data passed was processed, and we need more to
* continue processing (=resume reading from the network as we have no more
* data to process);
* \li 'ISC_R_UNEXPECTED' - an unexpected value has been detected;
* \li 'ISC_R_RANGE' - an expected value is not within an expected range.
*
* When processing error status within the callback, in general, we
* are interested in dispatching on the first two values, as anything
* else can be treated as hard-stop errors: their purpose is to give a
* little insight into what has happened without going into gory
* details (as we are not interested in them most of the time anyway).
*
* Any of the argument pointers can be 'NULL', identifying that the
* corresponding data is not present in the PROXYv2 header. Also,
* 'socktype' can be '-1' in a case of processing error.
*
*/
struct isc_proxy2_handler {
isc_buffer_t hdrbuf; /*!< Internal buffer for assembling PROXYv2 header
*/
uint8_t buf[256]; /*!< Internal buffer static storage */
int state; /*!< Current state machine state */
uint16_t expect_data; /*!< How much data do we need to switch to the
next state */
uint16_t max_size; /*!< Max PROXYv2 header size including its payload */
isc_proxy2_handler_cb_t cb; /*!< Data processing callback. */
void *cbarg; /*!< Callback argument. */
bool calling_cb; /*<! Callback calling marker. Used to detect recursive
object uses (changing the data state from within
the callback). */
isc_result_t result; /*<! The last passed to the callback processing
status value. */
isc_mem_t *mctx;
uint16_t header_size; /*!< Total PROXYv2 header size (including the
payload. */
uint16_t tlv_data_size; /*!< The size of TLVs payload size */
isc_proxy2_command_t cmd; /*!< The decoded PROXYv2 command */
isc_proxy2_addrfamily_t proxy_addr_family; /*!< The decoded PROXYv2
address family */
isc_proxy2_socktype_t proxy_socktype; /*!< The decoded PROXYv2 socket
type */
isc_region_t tlv_data; /*!< TLV data region within the handled PROXYv2
header */
isc_region_t extra_data; /*!< Data past the PROXYv2 header (not
belonging to it) */
};
void
isc_proxy2_handler_init(isc_proxy2_handler_t *restrict handler, isc_mem_t *mctx,
const uint16_t max_size, isc_proxy2_handler_cb_t cb,
void *cbarg);
/*!<
* \brief Initialise the given 'isc_proxy2_handler_t' object, attach
* to the memory context.
*
* Arguments:
*\li 'mctx' - memory context;
*\li 'max_size' - the upper limit for the PROXYv2 header and its payload (0 -
*unlimited);
*\li 'cb' - data processing callback;
*\li 'cbarg' - data processing callback argument.
*
* Requires:
*\li 'handler' is not NULL;
*\li 'mctx' is not NULL;
*\li 'max_size' is >= `ISC_PROXY2_HEADER_SIZE` or is 0;
*\li 'cb' is not NULL.
*/
void
isc_proxy2_handler_uninit(isc_proxy2_handler_t *restrict handler);
/*!<
* \brief Un-initialise the given 'isc_proxy2_handler_t' object, detach
* from the attached memory context. Invalidate any internal unprocessed data.
*
* Requires:
*\li 'handler' is not NULL.
*/
void
isc_proxy2_handler_clear(isc_proxy2_handler_t *restrict handler);
/*!<
* \brief Clear the given 'isc_proxy2_handler_t' object from
* any unprocessed data, clear the last data processing status (set it to
* 'ISC_R_UNSET'). Effectively, the function returns the object to its initial
* state.
*
* Requires:
*\li 'handler' is not NULL.
*/
isc_proxy2_handler_t *
isc_proxy2_handler_new(isc_mem_t *mctx, const uint16_t max_size,
isc_proxy2_handler_cb_t cb, void *cbarg);
/*!<
* \brief Allocate and initialise a new 'isc_proxy2_handler_t'
* object, attach to the memory context.
*
* Arguments:
*\li 'mctx' - memory context;
*\li 'max_size' - the upper limit for the PROXYv2 header and its payload (0 -
*unlimited);
*\li 'cb' - data processing callback;
*\li 'cbarg' - data
*processing callback argument.
*
* Requires:
*\li 'mctx' is not NULL;
*\li 'max_size' is >= `ISC_PROXY2_HEADER_SIZE` or is 0;
*\li 'cb' is not NULL.
*/
void
isc_proxy2_handler_free(isc_proxy2_handler_t **restrict handler);
/*!<
* \brief Un-initialise the given 'isc_proxy2_handler_t' object, detach
* from the attached memory context, free the memory consumed by the object.
*
* Requires:
*\li 'handler' is not NULL;
*\li 'handler' is not pointing to NULL.
*/
void
isc_proxy2_handler_setcb(isc_proxy2_handler_t *restrict handler,
isc_proxy2_handler_cb_t cb, void *cbarg);
/*!<
* \brief Change the data processing callback and its argument within the
* given 'isc_proxy2_handler_t' object.
*
* Arguments:
*\li 'handler' - PROXYv2 handler object;
*\li 'cb' - new data processing callback;
*\li 'cbarg' - new data processing callback argument.
*
* Requires:
*\li 'handler' is not NULL;
*\li 'cb' is not NULL.
*/
isc_result_t
isc_proxy2_handler_push_data(isc_proxy2_handler_t *restrict handler,
const void *restrict buf,
const unsigned int buf_size);
/*!<
* \brief Push new data to the given 'isc_proxy2_handler_t'
* object. Call the callback passing a status and a result of data
* processing to it.
*
* To avoid erroneously recursive usage of the object, it is forbidden to call
* this function from within the callback. Doing so will abort the program.
*
* Requires:
*\li 'handler' is not NULL;
*\li 'buf' is not NULL;
*\li 'buf_size' is not 0.
*/
isc_result_t
isc_proxy2_handler_push(isc_proxy2_handler_t *restrict handler,
const isc_region_t *restrict region);
/*!<
* \brief The same as 'isc_proxy2_handler_push_data()' but pushes that
* data for processing via an 'isc_region_t' object.
*
* Requires:
*\li 'handler' is not NULL;
*\li 'region' is not NULL.
*/
isc_result_t
isc_proxy2_handler_result(const isc_proxy2_handler_t *restrict handler);
/*!<
* \brief Return the last data processing status passed to the
* callback.
*
* Requires:
*\li 'handler' is not NULL.
*
* Return values:
* \li 'ISC_R_SUCCESS' - the header has been processed, and data has been
* extracted from the received header and its payload;
* \li 'ISC_R_NOMORE' - the data passed was processed, and we need more to
* continue processing (=resume reading from the network as we have no more
* data to process);
* \li 'ISC_R_UNEXPECTED' - an unexpected value has been detected;
* \li 'ISC_R_RANGE' - an expected value is not within an expected range.
*/
size_t
isc_proxy2_handler_header(const isc_proxy2_handler_t *restrict handler,
isc_region_t *restrict region);
/*!<
* \brief Get the complete processed PROXYv2 header as is
* (e.g. for forwarding).
*
* Requires:
*\li 'handler' is not NULL;
*\li 'region' is NULL or points to a zeroed 'isc_region_t' object.
*
* Return the size of the header or 0 on error (if it has not been
* processed yet).
*/
size_t
isc_proxy2_handler_tlvs(const isc_proxy2_handler_t *restrict handler,
isc_region_t *restrict region);
/*!<
* \brief Get the TLV-data within the processed PROXYv2 header.
*
* Requires:
*\li 'handler' is not NULL;
*\li 'region' is NULL or points to a zeroed 'isc_region_t' object.
*
* Return the size of the header or 0 on error (if it has not been
* processed yet).
*/
size_t
isc_proxy2_handler_extra(const isc_proxy2_handler_t *restrict handler,
isc_region_t *restrict region);
/*!<
* \brief Get the data past the processed PROXYv2 header. The data
* is not the part of the PROXYv2 header itself. That can happen (and
* does happen) when data is being sent over TCP.
*
* Requires:
*\li 'handler' is not NULL;
*\li 'region' is NULL or points to a zeroed 'isc_region_t' object.
*
* Return the size of the header or 0 on error (if it has not been
* processed yet).
*/
isc_result_t
isc_proxy2_handler_addresses(const isc_proxy2_handler_t *restrict handler,
int *restrict psocktype,
isc_sockaddr_t *restrict psrc_addr,
isc_sockaddr_t *restrict pdst_addr);
/*!<
* \brief Get the addresses directly from the processed PROXYv2
* header. If you are not interested in particular data, you can pass
* NULL as the argument to ignore it.
*
* Requires:
*\li 'handler' is not NULL.
*/
isc_result_t
isc_proxy2_header_handle_directly(const isc_region_t *restrict header_data,
const isc_proxy2_handler_cb_t cb,
void *cbarg);
/*!<
* \brief Process PROXYv2 header in one go directly without memory
* allocation and copying. Specifically designed to work when a
* complete header and associated follow-up data is expected (for
* example, when datagram transports are used, like UDP).
*
* Requires:
*\li 'header_data' is not NULL;
*\li 'cb' is not NULL.
*
* Return values are the same that get passed to the processing
* callback. Given that processing should complete in one go, getting
* anything except `ISC_R_SUCCESS` indicates failure.
*/
typedef bool (*isc_proxy2_tlv_cb_t)(const isc_proxy2_tlv_type_t tlv_type,
const isc_region_t *restrict data,
void *cbarg);
/*!<
* \brief Callback used for iterating over TLV data extracted from
* PROXYv2 headers.
*
* Arguments:
*\li 'tlv_type' - type value (see the 'isc_proxy2_tlv_type_t' enumeration);
*\li 'data' - pointer to 'isc_region_t' object referring to the data;
*\li 'cbarg' - opaque pointer to user supplied data.
*
* Return values:
*\li 'true' - continue processing the next TLV entry (if any);
*\li 'false' - stop processing TLV-entries.
*/
isc_result_t
isc_proxy2_tlv_iterate(const isc_region_t *restrict tlv_data,
const isc_proxy2_tlv_cb_t cb, void *cbarg);
/*!<
* \brief Iterate over the TLV data extracted from PROXYv2 headers.
*
* Arguments:
*\li 'tlv_data' - TLV data extracted from a PROXYv2 header;
*\li 'cb' - user provided iteration callback;
*\li 'cbarg' - user provided iteration callback argument.
*
* Requires:
*\li 'tlv_data' is not NULL;
*\li 'cb' is not NULL.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_RANGE' - malformed TLV data was detected.
*/
typedef bool (*isc_proxy2_tls_subtlv_cb_t)(
const uint8_t client_flags, const bool client_cert_verified,
const isc_proxy2_tlv_subtype_tls_t tls_subtlv_type,
const isc_region_t *restrict data, void *cbarg);
/*!<
* \brief Callback used for iterating over TLS sub-TLV data extracted from
* PROXYv2 headers.
*
* Arguments:
*\li 'client_flags' - TLS client flags extracted from TLS TLV data
* (see 'isc_proxy2_tls_client_flags_t' enumeration);
*\li 'client_cert_verified' - flag which indicates if the supplied
*TLS client certificate was verified- (if provided by the client);
*\li 'tls_subtlv_type' - TLS sub-TLV type (see the
*'isc_proxy2_tlv_subtype_tls_t' enumeration);
*\li 'cbarg' - opaque pointer to user supplied data.
*
* Return values:
*\li 'true' - continue processing the next TLV entry (if any);
*\li 'false' - stop processing TLV-entries.
*/
isc_result_t
isc_proxy2_subtlv_tls_header_data(const isc_region_t *restrict tls_tlv_data,
uint8_t *restrict pclient_flags,
bool *restrict pclient_cert_verified);
/*!<
* \brief Get data from a TLS ('ISC_PROXY2_TLV_TYPE_TLS') TLV value.
*
* Arguments:
*\li 'pclient_flags' - a pointer to the variable to receive the TLS client
*flags (see 'isc_proxy2_tls_client_flags_t' enumeration for more details);
*\li 'pclient_cert_verified' - a pointer the value to receive TLS client
*certificate verification status ('true' - verified).
*
* Requires:
*\li 'tls_tlv_data' is not NULL;
*\li 'pclient_flags' is either NULL or a pointer pointing to a
*zeroed variable;
*\li 'pclient_cert_verified' is either NULL or a pointer pointing to a
*zeroed variable.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_RANGE' - malformed TLV data was detected.
*/
isc_result_t
isc_proxy2_subtlv_tls_iterate(const isc_region_t *restrict tls_tlv_data,
const isc_proxy2_tls_subtlv_cb_t cb, void *cbarg);
/*!<
* \brief Iterate over the sub-TLV data extracted from TLS
* ('ISC_PROXY2_TLV_TYPE_TLS') TLV value of a PROXYv2 header.
*
* Arguments:
*\li 'tls_tlv_data' - TLS-realted sub-TLV data extracted from
*a PROXYv2 header;
*\li 'cb' - user provided iteration callback;
*\li 'cbarg' - user provided iteration callback argument.
*
* Requires:
*\li 'tls_tlv_data' is not NULL;
*\li 'cb' is not NULL.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_RANGE' - malformed TLV data was detected.
*/
isc_result_t
isc_proxy2_tlv_data_verify(const isc_region_t *restrict tlv_data);
/*!<
* \brief Verify TLV-data structure extracted from a PROXYv2 header.
* The function loops over the data verifying that TLVs are structured
* in a correct way.
*
* NOTE: If you are using an 'isc_proxy2_handler_t' object then there
* is no need for you to call this function as it is called during
* the normal operation. It is exposed mostly for unit testing
* purposes or for verifying outgoing data, should it be required.
*
* Arguments:
*\li 'tlv_data' - TLV data extracted from a PROXYv2 header.
*
* Requires:
*\li 'tlv_data' is not NULL.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_RANGE' - malformed TLV data was detected.
*/
/*
* Definitions related to generation of PROXYv2 headers
*/
isc_result_t
isc_proxy2_make_header(isc_buffer_t *restrict outbuf,
const isc_proxy2_command_t cmd, const int socktype,
const isc_sockaddr_t *restrict src_addr,
const isc_sockaddr_t *restrict dst_addr,
const isc_region_t *restrict tlv_data);
/*!<
* \brief Create a PROXYv2 header.
*
* Arguments:
*\li 'outbuf' - the output buffer;
*\li 'cmd' - PROXYv2 command;
*\li 'socktype' - PROXYv2 socket type (possible values are 'SOCK_STREAM',
*'SOCK_DGRAM', or '0' for "unspecified");
*\li 'src_addr' - source address, if any;
*\li 'dst_addr' - destination address, if any;
*\li 'tlv_data' - TLV data, if any.
*
* Requires:
*\li 'outbuf' is not NULL;
*\li 'cmd' is 'ISC_PROXY2_CMD_PROXY' or 'socktype' is equal to '0';
*\li either both of 'src_addr' and 'dst_addr' are NULL or both are not;
*\li both of 'src_addr' and 'dst_addr' are of the same type when specified.
*
* Notes:
*
* When 'cmd' equals 'ISC_PROXY2_CMD_LOCAL', then 'socktype' must equal '0'
* (unspecified) and both 'src_addr' and 'dst_addr' must be 'NULL'.
* When 'cmd' equals 'ISC_PROXY2_CMD_PROXY', then having 'socktype' being equal
* to '0' will instruct the function to create PROXYv2 header marked as bearing
* address of "unspecified" ('0') opaque type. In this case both 'src_addr' and
* 'dst_addr' must be 'NULL'. In other cases the address type is determined
* from the 'src_addr' and 'dst_addr' arguments (and might 'AF_INET',
* 'AF_INET6', and 'AF_UNIX' per the protocol spec).
* The socket type, when applicable, is determined from the 'socktype' argument
* and must be any of 'SOCK_STREAM', 'SOCK_DGRAM', when applicable, or '0'
* (unspecified).
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_UNEXPECTED' - an unexpected value has been detected in the input
*data (function arguments);
*\li 'ISC_R_NOSPACE' - not enough data in the output buffer;
*\li 'ISC_R_RANGE' - too much data to fit PROXYv2 header.
*/
isc_result_t
isc_proxy2_header_append(isc_buffer_t *restrict outbuf,
const isc_region_t *restrict data);
/*!<
* \brief Append arbitrary data to PROXYv2 header and update the
* length field within the header accordingly. It is used as foundation
* for TLV appending functionality. Also, it can be used to add address
* information in the case when "unspecified" opaque format is used.
*
* Arguments:
*\li 'outbuf' - the output buffer containing a valid PROXYv2 header;
*\li 'data' - use provided arbitrary data.
*
* Requires:
*\li 'outbuf' is not NULL;
*\li used region within 'outbuf' is more or equal
*to 'ISC_PROXY2_HEADER_SIZE';
*\li 'data' is not NULL.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_NOSPACE' - not enough data in the output buffer;
*\li 'ISC_R_RANGE' - too much data for PROXYv2 header.
*/
isc_result_t
isc_proxy2_header_append_tlv(isc_buffer_t *restrict outbuf,
const isc_proxy2_tlv_type_t tlv_type,
const isc_region_t *restrict data);
/*!<
* \brief Append TLV data to PROXYv2 header and update the
* length field within the header accordingly.
*
* Requires:
*\li 'outbuf' is not NULL;
*\li used region within 'outbuf' is more or equal
*to 'ISC_PROXY2_HEADER_SIZE';
*\li 'data' is not NULL.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_NOSPACE' - not enough data in the output buffer
*\li 'ISC_R_RANGE' - too much data for PROXYv2 header.
*/
isc_result_t
isc_proxy2_header_append_tlv_string(isc_buffer_t *restrict outbuf,
const isc_proxy2_tlv_type_t tlv_type,
const char *restrict str);
/*!<
* \brief Append the string as TLV data to PROXYv2 header and update the
* length field within the header accordingly.
*
* Requires:
*\li 'outbuf' is not NULL;
*\li used region within 'outbuf' is more or equal
*to 'ISC_PROXY2_HEADER_SIZE';
*\li 'data' is not NULL.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_NOSPACE' - not enough data in the output buffer.
*\li 'ISC_R_RANGE' - too much data for PROXYv2 header.
*/
isc_result_t
isc_proxy2_make_tls_subheader(isc_buffer_t *restrict outbuf,
const uint8_t client_flags,
const bool client_cert_verified,
const isc_region_t *restrict tls_subtlvs_data);
/*!<
* \brief Create TLS (ISC_PROXY2_TLV_TYPE_TLS) TLV subheader which
* can later be added to the PROXYv2 header TLV data.
*
* Arguments:
*\li 'client_flags' - TLS client flags (see
'isc_proxy2_tls_client_flags_t' enumeration for more details);
*\li 'client_cert_verified' - TLS client certificate verification
*status ('true' - verified).
*\li 'tls_subtlvs_data' - TLS subtlvs data, if any (see
'isc_proxy2_tlv_subtype_tls_t' for more details).
*
* Requires:
*\li 'outbuf' is not NULL;
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_NOSPACE' - not enough data in the output buffer;
*\li 'ISC_R_RANGE' - too much data for a TLV value.
*/
isc_result_t
isc_proxy2_append_tlv(isc_buffer_t *restrict outbuf, const uint8_t type,
const isc_region_t *restrict data);
/*!<
* \brief Append TLV data to the end of the buffer. Compared to
* 'isc_proxy2_header_append_tlv()' it does not try to look for a
* correct PROXYv2 header at the beginning of the buffer and update
* its length field. The main purpose of this function is to work with
* sub-TLVs.
*
* Requires:
*\li 'outbuf' is not NULL;
*\li 'data' is not NULL.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_NOSPACE' - not enough data in the output buffer
*\li 'ISC_R_RANGE' - too much data for PROXYv2 header.
*/
isc_result_t
isc_proxy2_append_tlv_string(isc_buffer_t *restrict outbuf, const uint8_t type,
const char *restrict str);
/*!<
* \brief Append the string as TLV data to the end of the
* buffer. Compared to 'isc_proxy2_header_append_tlv_string()' it does not
* try to look for a correct PROXYv2 header at the beginning of the
* buffer and update its length field. The main purpose of this
* function is to work with sub-TLVs.
*
* Requires:
*\li 'outbuf' is not NULL;
*\li 'data' is not NULL.
*
* Return values:
*\li 'ISC_R_SUCCESS' - iteration over the data was successful;
*\li 'ISC_R_NOSPACE' - not enough data in the output buffer
*\li 'ISC_R_RANGE' - too much data for PROXYv2 header.
*/
ISC_LANG_ENDDECLS

1440
lib/isc/proxy2.c Normal file

File diff suppressed because it is too large Load Diff