master
c 194 lines 6.22 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4
5 ENUM_STR_MAP_DEFINE(ND_SOCK_ERROR) = {
6 { .id = ND_SOCK_ERR_NONE, .name = "no socket error", },
7 { .id = ND_SOCK_ERR_CONNECTION_REFUSED, .name = "connection refused", },
8 { .id = ND_SOCK_ERR_CANNOT_RESOLVE_HOSTNAME, .name = "cannot resolve hostname", },
9 { .id = ND_SOCK_ERR_FAILED_TO_CREATE_SOCKET, .name = "cannot create socket", },
10 { .id = ND_SOCK_ERR_NO_HOST_IN_DEFINITION, .name = "no host in definition", },
11 { .id = ND_SOCK_ERR_POLL_ERROR, .name = "socket poll() error", },
12 { .id = ND_SOCK_ERR_TIMEOUT, .name = "timeout", },
13 { .id = ND_SOCK_ERR_SSL_CANT_ESTABLISH_SSL_CONNECTION, .name = "cannot establish SSL connection", },
14 { .id = ND_SOCK_ERR_SSL_INVALID_CERTIFICATE, .name = "invalid SSL certification", },
15 { .id = ND_SOCK_ERR_SSL_FAILED_TO_OPEN, .name = "failed to open SSL", },
16 { .id = ND_SOCK_ERR_THREAD_CANCELLED, .name = "thread cancelled", },
17 { .id = ND_SOCK_ERR_NO_DESTINATION_AVAILABLE, .name = "no destination available", },
18 { .id = ND_SOCK_ERR_UNKNOWN_ERROR, .name = "unknown error", },
19
20 // terminator
21 { .name = NULL, .id = 0 }
22 };
23
24 ENUM_STR_DEFINE_FUNCTIONS(ND_SOCK_ERROR, ND_SOCK_ERR_NONE, "");
25
26 // --------------------------------------------------------------------------------------------------------------------
27
28 static const unsigned char alpn_proto_list[] = {
29 18, 'n', 'e', 't', 'd', 'a', 't', 'a', '_', 's', 't', 'r', 'e', 'a', 'm', '/', '2', '.', '0',
30 8, 'h', 't', 't', 'p', '/', '1', '.', '1'
31 };
32
33 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;
50 return false;
51 }
52
53 if (s->verify_certificate && security_test_certificate(s->ssl.conn)) {
54 // certificate is not valid
55 s->error = ND_SOCK_ERR_SSL_INVALID_CERTIFICATE;
56 return false;
57 }
58
59 return true;
60 }
61
62 s->error = ND_SOCK_ERR_SSL_FAILED_TO_OPEN;
63 return false;
64 }
65
66 bool nd_sock_connect_to_this(ND_SOCK *s, const char *definition, int default_port, time_t timeout, bool ssl) {
67 if(!s)
68 return false;
69
70 nd_sock_close(s);
71
72 if(!definition || !*definition) {
73 s->error = ND_SOCK_ERR_NO_HOST_IN_DEFINITION;
74 return false;
75 }
76
77 // Extract hostname for SNI before establishing connection
78 if(ssl) {
79 CLEAN_CHAR_P *buffer = strdupz(definition);
80
81 char *host = buffer;
82
83 // Skip protocol prefix if present
84 if(strncmp(host, "tcp:", 4) == 0)
85 host += 4;
86 else if(strncmp(host, "udp:", 4) == 0)
87 host += 4;
88
89 // Handle IPv6 addresses in brackets
90 char *hostname_end = host;
91 if(*host == '[') {
92 host++; // Skip opening bracket
93 while(*hostname_end && *hostname_end != ']') hostname_end++;
94 if(*hostname_end == ']')
95 *hostname_end = '\0';
96 }
97 else {
98 // For IPv4 or hostnames, find the end (before port or interface)
99 while(*hostname_end && *hostname_end != ':' && *hostname_end != '%') hostname_end++;
100 if(*hostname_end)
101 *hostname_end = '\0';
102 }
103
104 // Store the hostname for SNI if it's not empty
105 if(host && *host) {
106 s->sni_hostname = strdupz(host);
107 }
108 }
109
110 struct timeval tv = {
111 .tv_sec = timeout,
112 .tv_usec = 0
113 };
114
115 s->fd = connect_to_this(definition, default_port, &tv);
116 if(s->fd < 0) {
117 s->error = -s->fd;
118 return false;
119 }
120
121 if(ssl && s->ctx) {
122 if (!nd_sock_open_ssl(s)) {
123 close(s->fd);
124 s->fd = -1;
125 return false;
126 }
127 }
128 else
129 s->ssl = NETDATA_SSL_UNSET_CONNECTION;
130
131 return true;
132 }
133
134 ALWAYS_INLINE
135 ssize_t nd_sock_send_timeout(ND_SOCK *s, void *buf, size_t len, int flags, time_t timeout) {
136 switch(wait_on_socket_or_cancel_with_timeout(&s->ssl, s->fd, (int)(timeout * 1000), POLLOUT, NULL)) {
137 case 0: // data are waiting
138 break;
139
140 case 1: // timeout
141 s->error = ND_SOCK_ERR_TIMEOUT;
142 return 0;
143
144 case -1: // thread cancelled
145 s->error = ND_SOCK_ERR_THREAD_CANCELLED;
146 return -1;
147
148 case 2: // poll() error
149 s->error = ND_SOCK_ERR_POLL_ERROR;
150 return -1;
151
152 default:
153 s->error = ND_SOCK_ERR_UNKNOWN_ERROR;
154 return -1;
155 }
156
157 if(s->ssl.conn) {
158 if (nd_sock_is_ssl(s))
159 return netdata_ssl_write(&s->ssl, buf, len);
160 else
161 return -1;
162 }
163
164 return send(s->fd, buf, len, flags);
165 }
166
167 ALWAYS_INLINE
168 ssize_t nd_sock_recv_timeout(ND_SOCK *s, void *buf, size_t len, int flags, time_t timeout) {
169 switch(wait_on_socket_or_cancel_with_timeout(&s->ssl, s->fd, (int)(timeout * 1000), POLLIN, NULL)) {
170 case 0: // data are waiting
171 break;
172
173 case 1: // timeout
174 s->error = ND_SOCK_ERR_TIMEOUT;
175 return 0;
176
177 case -1: // thread cancelled
178 s->error = ND_SOCK_ERR_THREAD_CANCELLED;
179 return -1;
180
181 case 2: // poll() error
182 s->error = ND_SOCK_ERR_POLL_ERROR;
183 return -1;
184
185 default:
186 s->error = ND_SOCK_ERR_UNKNOWN_ERROR;
187 return -1;
188 }
189
190 if (nd_sock_is_ssl(s))
191 return netdata_ssl_read(&s->ssl, buf, len);
192
193 return recv(s->fd, buf, len, flags);
194 }