SSL_CTX_SET_CLIENT_HELLO_CB(3SSL)
π SSL_CTX_set_client_hello_cb β the OpenSSL 3SSL manual, but make it chat π
okay bestie, sit down, hydrate, and let's talk about SSL_CTX_set_client_hello_cb, plus its whole little friend group: SSL_client_hello_cb_fn, SSL_client_hello_isv2, SSL_client_hello_get0_legacy_version, SSL_client_hello_get0_random, SSL_client_hello_get0_session_id, SSL_client_hello_get0_ciphers, SSL_client_hello_get0_compression_methods, SSL_client_hello_get1_extensions_present, SSL_client_hello_get_extension_order, and SSL_client_hello_get0_ext. They're all callback-ish, early-stage, server-side ClientHello processing girlies. πΈ
β¨ NAME β¨
So basically these are callback functions for early server-side ClientHello processing. That's the whole vibe. π
π SYNOPSIS π
Here's the cast, exactly as they're declared, no edits, we don't do that here:
typedef int (*SSL_client_hello_cb_fn)(SSL *s, int *al, void *arg);
void SSL_CTX_set_client_hello_cb(SSL_CTX *c, SSL_client_hello_cb_fn *f,
void *arg);
unsigned int SSL_client_hello_get0_legacy_version(SSL *s);
size_t SSL_client_hello_get0_random(SSL *s, const unsigned char **out);
size_t SSL_client_hello_get0_session_id(SSL *s, const unsigned char **out);
size_t SSL_client_hello_get0_ciphers(SSL *s, const unsigned char **out);
size_t SSL_client_hello_get0_compression_methods(SSL *s,
const unsigned char **out);
int SSL_client_hello_get1_extensions_present(SSL *s, int **out,
size_t *outlen);
int SSL_client_hello_get_extension_order(SSL *s, uint16_t *exts,
size_t *num_exts);
int SSL_client_hello_get0_ext(SSL *s, unsigned int type, const unsigned char **out,
size_t *outlen);
π¨ Deprecation alert, bestie π¨ The following function has been deprecated since OpenSSL 4.0, and you can hide it entirely by definingOPENSSL_API_COMPATwith a suitable version value β peepopenssl_user_macros(7)if you wanna know more:
int SSL_client_hello_isv2(SSL *s);
π DESCRIPTION π
the setter, the callback, the whole situation
SSL_CTX_set_client_hello_cb() sets the callback function, and that callback gets called automatically during the early stages of ClientHello processing on the server. The arg you hand over when you set the callback gets passed right back to the callback at runtime, like a little gift you leave for future you. π
Now listen, the return value of your callback literally decides fates:
- Return failure (
0) β the connection terminates π and you, yes you, should say what alert value goes out in thealparameter. Do not leavealhanging, that's rude. - Return a negative value β you suspend the handshake, and the handshake function returns immediately.
SSL_get_error(3)will hand youSSL_ERROR_WANT_CLIENT_HELLO_CBto confirm the handshake got suspended. π
And here's the important part, no cap: it's the ClientHello callback's job to store info about the state of the last call, if it needs that to continue. Next time you call into the handshake function, the ClientHello callback gets called again, and if it returns success, normal handshake processing picks up right from that point. Resume, slay, continue. β¨
the SSLv2 one (rip)
SSL_client_hello_isv2() tells you whether the ClientHello was carried in an SSLv2 record and is in the SSLv2 format. Support for the SSLv2 format was removed in 4.0, so this function will always return 0. She's giving nostalgia, not function. πͺ¦
the raw-field getters
SSL_client_hello_get0_random(), SSL_client_hello_get0_session_id(), SSL_client_hello_get0_ciphers(), and SSL_client_hello_get0_compression_methods() give you access to the corresponding ClientHello fields. They return the field length, and optionally set an out pointer to the octets of that field. Obsessed with a clean pointer handoff. π«Ά
the single-extension one
Similarly, SSL_client_hello_get0_ext() gives you access to individual extensions from the ClientHello, one extension at a time. For the wire protocol extension type value you provide, the extension value and length come back in the output parameters (if present).
the "which extensions tho" one
SSL_client_hello_get1_extensions_present() is your lil pre-game: use it prior to SSL_client_hello_get0_ext() to figure out which extensions are actually present in the ClientHello before you go querying for them.
- Both the
outandoutlenparameters are required. - On success, the caller must release the storage allocated for
*outusingOPENSSL_free(). Yes, you. Free it. π₯ - The contents of
*outis an array of integers holding the numerical value of the TLS extension types, in the order they appear in the ClientHello. *outlenholds the number of elements in the array.- If the ClientHello has no extensions at all, the function still returns success, with
*outset toNULLand*outlenset to0. Iconic minimalism. - β οΈ Note that
SSL_client_hello_get1_extensions_present()returns only recognised extensions. So unrecognised extensions (including GREASE) will not appear in the output. Sorry GREASE girlies. π
the no-malloc sibling
SSL_client_hello_get_extension_order() is basically SSL_client_hello_get1_extensions_present() but without internal memory allocation. Very "I brought my own storage" energy. π
- Called with
extsset toNULLβ it returns the number of extensions (perfect for allocating storage for a subsequent call, iykyk). - Otherwise,
*extsgets populated with the ExtensionType values, in the order the corresponding extensions appeared in the ClientHello. *num_extsis an input/output parameter: input = the size of the storage you allocated; output = how many ExtensionType values were written.- If the input
*num_extsis smaller than the number of extensions in question β that is treated as an error. π¨ - A subsequent call with
extsset toNULLcan retrieve the size of storage needed. - A ClientHello that contained no extensions is treated as success, with
*num_extsset to0.
π NOTES π
The ClientHello callback hands application code a vast window of possibilities to affect the TLS handshake. Main character energy, honestly. π
A primary use is letting the server examine the server name indication extension from the client, so it can pick an appropriate certificate to present and make other configuration adjustments relevant to that server name and its config. Those config changes can include:
- swapping out the associated
SSL_CTXpointer, - modifying the server's list of permitted TLS versions,
- changing the server's cipher list in response to the client's cipher list,
- etc etc, you get it. π¦
Also, recommended: applications should utilize a ClientHello callback and NOT use a servername callback, to avoid unexpected behavior that occurs due to the relative order of processing between things like session resumption and the historical servername callback. Learn from the elders' mistakes, bestie. π
π The SSL_client_hello_* family of functions may only be called from code executing within a ClientHello callback. That's the VIP section only.
π The SSL_client_hello_get0_*() functions return raw ClientHello data, whereas SSL_client_hello_get1_extensions_present() returns only recognized extensions β so unknown/GREASE extensions are not included.
π When Encrypted Client Hello (ECH) is used and ECH decryption has succeeded, the inner ClientHello message is what's visible to the ClientHello callback functions. If ECH decryption failed, the outer ClientHello message content is provided instead. π΅οΈββοΈ
β RETURN VALUES β
Your supplied ClientHello callback returns:
SSL_CLIENT_HELLO_SUCCESSon success βSSL_CLIENT_HELLO_ERRORon failure βSSL_CLIENT_HELLO_RETRYto suspend processing π
And the crew:
SSL_client_hello_isv2()returns0.SSL_client_hello_get0_random(),SSL_client_hello_get0_session_id(),SSL_client_hello_get0_ciphers(), andSSL_client_hello_get0_compression_methods()return the length of the corresponding ClientHello fields. If zero is returned, do not assume the output pointer is valid. Just don't. πSSL_client_hello_get0_ext()returns1if the extension of typetypeis present, and0otherwise.SSL_client_hello_get1_extensions_present()returns1on success and0on failure.SSL_client_hello_get_extension_order()returns1on success and0on failure.
π¦ SEE ALSO π¦
π HISTORY π
The SSL ClientHello callback, SSL_client_hello_isv2(), SSL_client_hello_get0_random(), SSL_client_hello_get0_session_id(), SSL_client_hello_get0_ciphers(), SSL_client_hello_get0_compression_methods(), SSL_client_hello_get0_ext(), and SSL_client_hello_get1_extensions_present() were added in OpenSSL 1.1.1. Then SSL_client_hello_get_extension_order() joined the group chat in OpenSSL 3.2.0. β¨
π COPYRIGHT π
Copyright 2017-2026 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.
and that's the whole page bestie, mwah π this was 4.0.3, dated 2026-09-29, and it's the SSL_CTX_SET_CLIENT_HELLO_CB(3SSL) manual β go forth and callback responsibly π§π
π¦ where this page comes from
- ubuntu stonking: libssl-doc 4.0.3-1ubuntu1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms