Add compatibility for TCP(SSL) multiplexing (#21715)
* Initial plan * Add SNI support for SSL streaming connections - Add sni_hostname field to ND_SOCK structure - Update nd_sock_close() to free sni_hostname memory - Modify nd_sock_open_ssl() to set SNI using SSL_set_tlsext_host_name() - Update nd_sock_connect_to_this() to extract hostname from destination Co-authored-by: ilyam8 <22274335+ilyam8@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: ilyam8 <22274335+ilyam8@users.noreply.github.com> Co-authored-by: Ilya Mashchenko <ilya@netdata.cloud>
Copilot committed
Mar 3, 2026 at 21:22 UTC
d05b41b19766a4184d0e7ec530f5416237cfa34a
2 files changed
+49
src/libnetdata/socket/nd-sock.c
+44
@@ -34,6 +34,16 @@ static bool nd_sock_open_ssl(ND_SOCK *s) {
34
if(!s) return false;
35
36
if (netdata_ssl_open_ext(&s->ssl, s->ctx, s->fd, alpn_proto_list, sizeof(alpn_proto_list))) {
37
+ // Set SNI hostname if available
38
+ if(s->sni_hostname && *s->sni_hostname) {
39
+ if(!SSL_set_tlsext_host_name(s->ssl.conn, s->sni_hostname)) {
40
+ nd_log(NDLS_DAEMON, NDLP_WARNING,
41
+ "Failed to set SNI hostname '%s' for SSL connection",
42
+ s->sni_hostname);
43
+ // Continue anyway - SNI failure is not fatal
44
+ }
45
+ }
46
+
47
if(!netdata_ssl_connect(&s->ssl)) {
48
// couldn't connect
49
s->error = ND_SOCK_ERR_SSL_CANT_ESTABLISH_SSL_CONNECTION;
@@ -56,6 +66,40 @@ static bool nd_sock_open_ssl(ND_SOCK *s) {
66
bool nd_sock_connect_to_this(ND_SOCK *s, const char *definition, int default_port, time_t timeout, bool ssl) {
67
nd_sock_close(s);
68
69
+ // Extract hostname for SNI before establishing connection
70
+ if(ssl && definition) {
71
+ char buffer[strlen(definition) + 1];
72
+ strcpy(buffer, definition);
73
+
74
+ char *host = buffer;
75
+
76
+ // Skip protocol prefix if present
77
+ if(strncmp(host, "tcp:", 4) == 0)
78
+ host += 4;
79
+ else if(strncmp(host, "udp:", 4) == 0)
80
+ host += 4;
81
+
82
+ // Handle IPv6 addresses in brackets
83
+ char *hostname_end = host;
84
+ if(*host == '[') {
85
+ host++; // Skip opening bracket
86
+ while(*hostname_end && *hostname_end != ']') hostname_end++;
87
+ if(*hostname_end == ']')
88
+ *hostname_end = '\0';
89
+ }
90
+ else {
91
+ // For IPv4 or hostnames, find the end (before port or interface)
92
+ while(*hostname_end && *hostname_end != ':' && *hostname_end != '%') hostname_end++;
93
+ if(*hostname_end)
94
+ *hostname_end = '\0';
95
+ }
96
+
97
+ // Store the hostname for SNI if it's not empty
98
+ if(host && *host) {
99
+ s->sni_hostname = strdupz(host);
100
+ }
101
+ }
102
+
103
struct timeval tv = {
104
.tv_sec = timeout,
105
.tv_usec = 0
src/libnetdata/socket/nd-sock.h
+5
@@ -32,6 +32,7 @@ typedef struct nd_sock {
32
int fd;
33
NETDATA_SSL ssl;
34
SSL_CTX *ctx;
35
+ char *sni_hostname; // hostname for SNI in SSL/TLS connections
36
} ND_SOCK;
37
38
#define ND_SOCK_INIT(ssl_ctx, ssl_verify) (ND_SOCK){ \
@@ -40,6 +41,7 @@ typedef struct nd_sock {
41
.fd = -1, \
42
.ssl = NETDATA_SSL_UNSET_CONNECTION, \
43
.ctx = ssl_ctx, \
44
+ .sni_hostname = NULL, \
45
}
46
47
static inline void nd_sock_init(ND_SOCK *s, SSL_CTX *ctx, bool verify_certificate) {
@@ -48,6 +50,7 @@ static inline void nd_sock_init(ND_SOCK *s, SSL_CTX *ctx, bool verify_certificat
50
s->fd = -1;
51
s->ssl = NETDATA_SSL_UNSET_CONNECTION;
52
s->ctx = ctx;
53
+ s->sni_hostname = NULL;
54
}
55
56
ALWAYS_INLINE
@@ -69,6 +72,8 @@ static void nd_sock_close(ND_SOCK *s) {
72
s->fd = -1;
73
}
74
75
+ freez(s->sni_hostname);
76
+ s->sni_hostname = NULL;
77
s->error = ND_SOCK_ERR_NONE;
78
}
79