Skip to content

Commit 2f7802f

Browse files
committed
Eliminate heap allocations
1 parent c8d37e0 commit 2f7802f

9 files changed

Lines changed: 276 additions & 233 deletions

File tree

doc/developer-guide/api/functions/TSVConnClientHelloGet.en.rst

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,21 @@ Synopsis
2929
#include <ts/ts.h>
3030
3131
.. function:: TSClientHello TSVConnClientHelloGet(TSVConn sslp)
32-
.. function:: void TSClientHelloDestroy(TSClientHello ch)
3332
.. function:: TSReturnCode TSClientHelloExtensionGet(TSClientHello ch, unsigned int type, const unsigned char **out, size_t *outlen)
3433

3534
Description
3635
===========
3736

3837
:func:`TSVConnClientHelloGet` retrieves ClientHello message data from the TLS
39-
virtual connection :arg:`sslp`. Returns ``nullptr`` if :arg:`sslp` is invalid
40-
or not a TLS connection.
38+
virtual connection :arg:`sslp`. Returns a :type:`TSClientHello` always. The availability
39+
of the returned object must be checked before use.
4140

4241
.. important::
4342

4443
This function should only be called from the ``TS_EVENT_SSL_CLIENT_HELLO`` hook.
4544
The returned :type:`TSClientHello` is only valid during the SSL ClientHello event processing.
4645
Using this function from other hooks may result in accessing invalid or stale data.
4746

48-
The caller must call :func:`TSClientHelloDestroy` to free the returned object.
49-
50-
:func:`TSClientHelloDestroy` frees the :type:`TSClientHello` object :arg:`ch`.
51-
5247
:func:`TSClientHelloExtensionGet` retrieves extension data for the specified
5348
:arg:`type` (e.g., ``0x10`` for ALPN). Returns :enumerator:`TS_SUCCESS` if
5449
found, :enumerator:`TS_ERROR` otherwise. The returned pointer in :arg:`out` is

doc/developer-guide/api/types/TSClientHello.en.rst

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,20 @@ Description
4242
a client during the TLS handshake. It provides access to the client's TLS
4343
version, cipher suites, and extensions.
4444

45-
Objects of this type are obtained via :func:`TSVConnClientHelloGet` and must
46-
be freed using :func:`TSClientHelloDestroy`. The implementation abstracts
47-
differences between OpenSSL and BoringSSL to provide a consistent interface.
45+
The implementation abstracts differences between OpenSSL and BoringSSL to
46+
provide a consistent interface.
4847

4948
Accessor Methods
5049
================
5150

5251
The following methods are available to access ClientHello data:
5352

53+
.. function:: bool is_available() const
54+
55+
Returns whether the object contains valid values. As long as
56+
:func:`TSVConnClientHelloGet` is called for a TLS connection, the return
57+
value should be `true`.
58+
5459
.. function:: uint16_t get_version() const
5560

5661
Returns the TLS version from the ClientHello message.

include/iocore/net/TLSSNISupport.h

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
*/
2424
#pragma once
2525

26+
#include "tscore/ink_config.h"
2627
#include "tscore/ink_memory.h"
2728
#include "SSLTypes.h"
2829

@@ -40,11 +41,44 @@ class TLSSNISupport
4041
{
4142
public:
4243
ClientHello(ClientHelloContainer chc) : _chc(chc) {}
44+
~ClientHello();
45+
46+
class ExtensionIdIterator
47+
{
48+
public:
49+
#if HAVE_SSL_CTX_SET_CLIENT_HELLO_CB
50+
ExtensionIdIterator(int *ids, size_t len, size_t offset) : _ids(ids), _ext_len(len), _offset(offset) {}
51+
#elif HAVE_SSL_CTX_SET_SELECT_CERTIFICATE_CB
52+
ExtensionIdIterator(const uint8_t *extensions, size_t len, size_t offset)
53+
: _extensions(extensions), _ext_len(len), _offset(offset)
54+
{
55+
}
56+
#endif
57+
~ExtensionIdIterator();
58+
59+
ExtensionIdIterator &operator++();
60+
bool operator==(const ExtensionIdIterator &b) const;
61+
int operator*() const;
62+
63+
private:
64+
#if HAVE_SSL_CTX_SET_CLIENT_HELLO_CB
65+
int *_extensions;
66+
#elif HAVE_SSL_CTX_SET_SELECT_CERTIFICATE_CB
67+
const uint8_t *_extensions;
68+
#endif
69+
size_t _ext_len;
70+
size_t _offset;
71+
};
72+
73+
uint16_t getVersion();
74+
std::string_view getCipherSuites();
75+
ExtensionIdIterator begin();
76+
ExtensionIdIterator end();
77+
4378
/**
4479
* @return 1 if successful
4580
*/
46-
int getExtension(int type, const uint8_t **out, size_t *outlen);
47-
ClientHelloContainer get_client_hello_container();
81+
int getExtension(int type, const uint8_t **out, size_t *outlen);
4882

4983
private:
5084
ClientHelloContainer _chc;
@@ -56,9 +90,9 @@ class TLSSNISupport
5690
static TLSSNISupport *getInstance(SSL *ssl);
5791
static void bind(SSL *ssl, TLSSNISupport *snis);
5892
static void unbind(SSL *ssl);
59-
int perform_sni_action(SSL &ssl);
60-
ClientHelloContainer get_client_hello_container() const;
61-
void set_client_hello_container(ClientHelloContainer container);
93+
94+
int perform_sni_action(SSL &ssl);
95+
ClientHello *get_client_hello() const;
6296
// Callback functions for OpenSSL libraries
6397

6498
/** Process a CLIENT_HELLO from a client.
@@ -116,6 +150,6 @@ class TLSSNISupport
116150
// Null-terminated string, or nullptr if there is no SNI server name.
117151
std::unique_ptr<char[]> _sni_server_name;
118152

119-
void _set_sni_server_name_buffer(std::string_view name);
120-
ClientHelloContainer _chc = nullptr;
153+
void _set_sni_server_name_buffer(std::string_view name);
154+
ClientHello *_ch;
121155
};

include/ts/apidefs.h.in

Lines changed: 34 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,124 +1050,70 @@ struct TSHttp2Priority {
10501050
* or -1 if the stream has no dependency. */
10511051
int32_t stream_dependency;
10521052
};
1053-
/**
1054-
* A structure for SSL Client Hello data
1055-
*/
1056-
struct tsapi_ssl_client_hello {
1057-
uint16_t version{0};
1058-
const uint8_t *cipher_suites{nullptr};
1059-
size_t cipher_suites_len{0};
1060-
const uint8_t *extensions{nullptr};
1061-
size_t extensions_len{0};
1062-
int *extension_ids{nullptr};
1063-
size_t extension_ids_len{0};
1064-
void *ssl_ptr{nullptr};
1065-
};
10661053

10671054
// Wrapper class that provides controlled access to client hello data
1068-
class TSClientHelloImpl
1055+
class TSClientHello
10691056
{
10701057
public:
1071-
// Type alias for extension type list
1072-
using TSExtensionTypeList = std::vector<uint16_t>;
1058+
class TSExtensionTypeList
1059+
{
1060+
public:
1061+
TSExtensionTypeList(void *ch) : _ch(ch) {}
10731062

1074-
TSClientHelloImpl(std::unique_ptr<tsapi_ssl_client_hello> ch) : _ssl_client_hello(std::move(ch)) {}
1063+
class Iterator
1064+
{
1065+
public:
1066+
Iterator(const void *ite);
1067+
Iterator &operator++();
1068+
bool operator==(const Iterator &b) const;
1069+
int operator*() const;
10751070

1076-
~TSClientHelloImpl() = default;
1071+
private:
1072+
char _real_iterator[24];
1073+
};
10771074

1078-
uint16_t
1079-
get_version() const
1080-
{
1081-
return _ssl_client_hello->version;
1082-
}
1075+
Iterator begin();
1076+
Iterator end();
10831077

1084-
const uint8_t *
1085-
get_cipher_suites() const
1086-
{
1087-
return _ssl_client_hello->cipher_suites;
1088-
}
1078+
private:
1079+
void *_ch;
1080+
};
10891081

1090-
size_t
1091-
get_cipher_suites_len() const
1092-
{
1093-
return _ssl_client_hello->cipher_suites_len;
1094-
}
1082+
TSClientHello(void *ch) : _client_hello(ch) {}
10951083

1096-
const uint8_t *
1097-
get_extensions() const
1098-
{
1099-
return _ssl_client_hello->extensions;
1100-
}
1084+
~TSClientHello() = default;
11011085

1102-
size_t
1103-
get_extensions_len() const
1086+
explicit
1087+
operator bool() const
11041088
{
1105-
return _ssl_client_hello->extensions_len;
1089+
return _client_hello != nullptr;
11061090
}
11071091

1108-
const int *
1109-
get_extension_ids() const
1110-
{
1111-
return _ssl_client_hello->extension_ids;
1112-
}
1092+
bool is_available() const;
11131093

1114-
size_t
1115-
get_extension_ids_len() const
1116-
{
1117-
return _ssl_client_hello->extension_ids_len;
1118-
}
1094+
uint16_t get_version() const;
11191095

1120-
void *
1121-
get_ssl_ptr() const
1122-
{
1123-
return _ssl_client_hello->ssl_ptr;
1124-
}
1096+
const uint8_t *get_cipher_suites() const;
1097+
1098+
size_t get_cipher_suites_len() const;
11251099

11261100
// Returns an iterable container of extension type IDs
11271101
// This abstracts the difference between BoringSSL (extensions buffer) and OpenSSL (extension_ids array)
11281102
TSExtensionTypeList
11291103
get_extension_types() const
11301104
{
1131-
TSExtensionTypeList result;
1132-
1133-
// For BoringSSL, parse the extensions buffer
1134-
if (_ssl_client_hello->extensions != nullptr) {
1135-
const uint8_t *ext = _ssl_client_hello->extensions;
1136-
size_t remaining = _ssl_client_hello->extensions_len;
1137-
1138-
while (remaining >= 4) {
1139-
uint16_t ext_type = (ext[0] << 8) | ext[1];
1140-
uint16_t ext_len = (ext[2] << 8) | ext[3];
1141-
size_t total_ext_size = 4 + ext_len;
1142-
1143-
result.push_back(ext_type);
1144-
1145-
if (total_ext_size > remaining) {
1146-
break;
1147-
}
1148-
ext += total_ext_size;
1149-
remaining -= total_ext_size;
1150-
}
1151-
}
1152-
// For OpenSSL, use the extension IDs array
1153-
else if (_ssl_client_hello->extension_ids != nullptr) {
1154-
for (size_t i = 0; i < _ssl_client_hello->extension_ids_len; i++) {
1155-
result.push_back(static_cast<uint16_t>(_ssl_client_hello->extension_ids[i]));
1156-
}
1157-
}
1158-
1159-
return result;
1105+
return TSExtensionTypeList(_client_hello);
11601106
}
11611107

11621108
// Internal accessor for API implementation
1163-
tsapi_ssl_client_hello *
1109+
void *
11641110
_get_internal() const
11651111
{
1166-
return _ssl_client_hello.get();
1112+
return _client_hello;
11671113
}
11681114

11691115
private:
1170-
std::unique_ptr<tsapi_ssl_client_hello> _ssl_client_hello;
1116+
void *_client_hello;
11711117
};
11721118

11731119
using TSFile = struct tsapi_file *;
@@ -1205,7 +1151,6 @@ using TSHostLookupResult = struct tsapi_hostlookupresult *;
12051151
using TSAIOCallback = struct tsapi_aiocallback *;
12061152
using TSAcceptor = struct tsapi_net_accept *;
12071153
using TSRemapPluginInfo = struct tsapi_remap_plugin_info *;
1208-
using TSClientHello = TSClientHelloImpl *;
12091154

12101155
using TSFetchSM = struct tsapi_fetchsm *;
12111156

include/ts/ts.h

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,32 +1351,12 @@ const char *TSVConnSslSniGet(TSVConn sslp, int *length);
13511351
structure. For OpenSSL, cipher suites and extension IDs are extracted using
13521352
SSL_client_hello_get0_* functions.
13531353
1354-
Memory Management: The caller must call TSClientHelloDestroy() to free the
1355-
returned object when it is no longer needed. Failure to do so will result
1356-
in memory leaks, especially for OpenSSL which allocates memory for the
1357-
extension IDs array.
1358-
13591354
@param sslp The SSL virtual connection handle. Must not be nullptr.
1360-
@return Pointer to TSClientHello object containing Client Hello data, or
1361-
nullptr if the client hello is not available or if an error occurs.
1355+
@return A TSClientHello object containing Client Hello data.
13621356
1363-
@see TSClientHelloDestroy
13641357
@see TSClientHelloExtensionGet
13651358
*/
13661359
TSClientHello TSVConnClientHelloGet(TSVConn sslp);
1367-
/**
1368-
Destroys a Client Hello object and frees associated memory.
1369-
1370-
This function must be called to properly free a TSClientHello object
1371-
obtained from TSVConnClientHelloGet(). It handles SSL library-specific
1372-
cleanup, including freeing the extension IDs array allocated by OpenSSL's
1373-
SSL_client_hello_get1_extensions_present() function.
1374-
1375-
@param ch The Client Hello object to destroy.
1376-
1377-
@see TSVConnClientHelloGet
1378-
*/
1379-
void TSClientHelloDestroy(TSClientHello ch);
13801360

13811361
/**
13821362
Retrieve a specific TLS extension from the Client Hello.
@@ -1387,11 +1367,10 @@ void TSClientHelloDestroy(TSClientHello ch);
13871367
OpenSSL without requiring conditional compilation in the plugin.
13881368
13891369
The returned buffer is still owned by the underlying SSL context and must
1390-
not be freed by the caller. The buffer is valid only as long as the
1391-
TSClientHello object has not been destroyed.
1370+
not be freed by the caller. The buffer is valid only in the condition where
1371+
you can get a TSClientHello object from an SSL virtual connection.
13921372
13931373
@param ch The Client Hello object obtained from TSVConnClientHelloGet().
1394-
Must not be nullptr.
13951374
@param type The TLS extension type to retrieve.
13961375
@param out Pointer to receive the extension data buffer. Must not be nullptr.
13971376
@param outlen Pointer to receive the length of the extension data in bytes.
@@ -1402,7 +1381,6 @@ void TSClientHelloDestroy(TSClientHello ch);
14021381
or if an error occurred during lookup.
14031382
14041383
@see TSVConnClientHelloGet
1405-
@see TSClientHelloDestroy
14061384
*/
14071385
TSReturnCode TSClientHelloExtensionGet(TSClientHello ch, unsigned int type, const unsigned char **out, size_t *outlen);
14081386

plugins/experimental/ja4_fingerprint/plugin.cc

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -202,15 +202,13 @@ handle_client_hello(TSCont /* cont ATS_UNUSED */, TSEvent event, void *edata)
202202

203203
TSClientHello ch = TSVConnClientHelloGet(ssl_vc);
204204

205-
if (nullptr == ch) {
205+
if (!ch) {
206206
Dbg(dbg_ctl, "Could not get TSClientHello object.");
207207
} else {
208208
auto data{std::make_unique<JA4_data>()};
209209
data->fingerprint = get_fingerprint(ch);
210210
get_IP(TSNetVConnRemoteAddrGet(ssl_vc), data->IP_addr);
211211
log_fingerprint(data.get());
212-
// Clean up the TSClientHello structure
213-
TSClientHelloDestroy(ch);
214212
// The VCONN_CLOSE handler is now responsible for freeing the resource.
215213
TSUserArgSet(ssl_vc, *get_user_arg_index(), static_cast<void *>(data.release()));
216214
}
@@ -284,7 +282,7 @@ get_version(TSClientHello ch)
284282
return max_version;
285283
} else {
286284
Dbg(dbg_ctl, "No supported_versions extension... using legacy version.");
287-
return ch->get_version();
285+
return ch.get_version();
288286
}
289287
}
290288

@@ -308,8 +306,8 @@ get_first_ALPN(TSClientHello ch)
308306
void
309307
add_ciphers(JA4::TLSClientHelloSummary &summary, TSClientHello ch)
310308
{
311-
const uint8_t *buf = ch->get_cipher_suites();
312-
size_t buflen = ch->get_cipher_suites_len();
309+
const uint8_t *buf = ch.get_cipher_suites();
310+
size_t buflen = ch.get_cipher_suites_len();
313311

314312
if (buflen > 0) {
315313
for (std::size_t i = 0; i + 1 < buflen; i += 2) {
@@ -323,7 +321,7 @@ add_ciphers(JA4::TLSClientHelloSummary &summary, TSClientHello ch)
323321
void
324322
add_extensions(JA4::TLSClientHelloSummary &summary, TSClientHello ch)
325323
{
326-
for (auto ext_type : ch->get_extension_types()) {
324+
for (auto ext_type : ch.get_extension_types()) {
327325
summary.add_extension(ext_type);
328326
}
329327
}

0 commit comments

Comments
 (0)