master
c 1,023 lines 33.8 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4
5 #include "https_client.h"
6
7 #include "aclk_util.h"
8
9 #include "daemon/pulse/pulse.h"
10
11 ENUM_STR_MAP_DEFINE(https_client_resp_t) = {
12 {
13 .id = HTTPS_CLIENT_RESP_OK,
14 .name = "ok",
15 },
16 {
17 .id = HTTPS_CLIENT_RESP_UNKNOWN_ERROR,
18 .name = "unknown error",
19 },
20 {
21 .id = HTTPS_CLIENT_RESP_NO_MEM,
22 .name = "not enough memory",
23 },
24 {
25 .id = HTTPS_CLIENT_RESP_NONBLOCK_FAILED,
26 .name = "cannot set socket to non-blocking mode",
27 },
28 {
29 .id = HTTPS_CLIENT_RESP_PROXY_NEGOTIATION_FAILED,
30 .name = "proxy negotiation failed",
31 },
32 {
33 .id = HTTPS_CLIENT_RESP_NO_SSL_CTX,
34 .name = "cannot create SSL ctx",
35 },
36 {
37 .id = HTTPS_CLIENT_RESP_NO_SSL_VERIFY_PATHS,
38 .name = "cannot set SSL verify paths",
39 },
40 {
41 .id = HTTPS_CLIENT_RESP_NO_SSL_NEW,
42 .name = "cannot create SSL",
43 },
44 {
45 .id = HTTPS_CLIENT_RESP_NO_TLS_SNI,
46 .name = "cannot set TLS SNI",
47 },
48 {
49 .id = HTTPS_CLIENT_RESP_SSL_CONNECT_FAILED,
50 .name = "SSL_connect() failed",
51 },
52 {
53 .id = HTTPS_CLIENT_RESP_SSL_START_FAILED,
54 .name = "cannot start SSL connection",
55 },
56 {
57 .id = HTTPS_CLIENT_RESP_UNKNOWN_REQUEST_TYPE,
58 .name = "unknown https client request type",
59 },
60 {
61 .id = HTTPS_CLIENT_RESP_HEADER_WRITE_FAILED,
62 .name = "https client failed to write http header",
63 },
64 {
65 .id = HTTPS_CLIENT_RESP_PAYLOAD_WRITE_FAILED,
66 .name = "https client failed to write http payload",
67 },
68 {
69 .id = HTTPS_CLIENT_RESP_POLL_ERROR,
70 .name = "https client poll() error",
71 },
72 {
73 .id = HTTPS_CLIENT_RESP_TIMEOUT,
74 .name = "https client timeout",
75 },
76 {
77 .id = HTTPS_CLIENT_RESP_READ_ERROR,
78 .name = "https client read error",
79 },
80 {
81 .id = HTTPS_CLIENT_RESP_PARSE_ERROR,
82 .name = "https client parsing of response failed",
83 },
84 {
85 .id = HTTPS_CLIENT_RESP_ENV_AGENT_NOT_CLAIMED,
86 .name = "agent is not claimed (during /env)",
87 },
88 {
89 .id = HTTPS_CLIENT_RESP_ENV_NOT_200,
90 .name = "/env response code is not 200",
91 },
92 {
93 .id = HTTPS_CLIENT_RESP_ENV_EMPTY,
94 .name = "/env response is empty",
95 },
96 {
97 .id = HTTPS_CLIENT_RESP_ENV_NOT_JSON,
98 .name = "/env response is not JSON",
99 },
100 {
101 .id = HTTPS_CLIENT_RESP_OTP_CHALLENGE_NOT_200,
102 .name = "otp challenge response is not http/200",
103 },
104 {
105 .id = HTTPS_CLIENT_RESP_OTP_CHALLENGE_INVALID,
106 .name = "otp challenge response is invalid",
107 },
108 {
109 .id = HTTPS_CLIENT_RESP_OTP_PASSWORD_NOT_201,
110 .name = "otp password response is not http/201",
111 },
112 {
113 .id = HTTPS_CLIENT_RESP_OTP_PASSWORD_EMPTY,
114 .name = "otp password response is empty",
115 },
116 {
117 .id = HTTPS_CLIENT_RESP_OTP_PASSWORD_NOT_JSON,
118 .name = "otp password response is not JSON",
119 },
120 {
121 .id = HTTPS_CLIENT_RESP_OTP_AGENT_NOT_CLAIMED,
122 .name = "agent is not claimed (during otp)",
123 },
124 {
125 .id = HTTPS_CLIENT_RESP_OTP_CHALLENGE_DECRYPTION_FAILED,
126 .name = "otp challenge decryption failed",
127 },
128
129 // terminator
130 {.name = NULL, .id = 0}
131 };
132
133 ENUM_STR_DEFINE_FUNCTIONS(https_client_resp_t, HTTPS_CLIENT_RESP_UNKNOWN_ERROR, "unknown error");
134
135 static const char *http_req_type_to_str(http_req_type_t req) {
136 switch (req) {
137 case HTTP_REQ_GET:
138 return "GET";
139 case HTTP_REQ_POST:
140 return "POST";
141 default:
142 return "unknown";
143 }
144 }
145
146 #define TRANSFER_ENCODING_CHUNKED (-2)
147
148 void http_parse_ctx_destroy(http_parse_ctx *ctx)
149 {
150 if(!ctx->headers)
151 return;
152
153 c_rhash_iter_t iter;
154 const char *key;
155
156 c_rhash_iter_t_initialize(&iter);
157 while ( !c_rhash_iter_str_keys(ctx->headers, &iter, &key) ) {
158 void *val;
159 c_rhash_get_ptr_by_str(ctx->headers, key, &val);
160 freez(val);
161 }
162
163 c_rhash_destroy(ctx->headers);
164 ctx->headers = NULL;
165 }
166
167 void http_parse_ctx_create(http_parse_ctx *ctx, enum http_parse_state parse_state)
168 {
169 http_parse_ctx_destroy(ctx);
170
171 ctx->state = parse_state;
172 ctx->content_length = -1;
173 ctx->http_code = 0;
174 ctx->headers = c_rhash_new(0);
175 ctx->flags = HTTP_PARSE_FLAGS_DEFAULT;
176 ctx->chunked_content_state = CHUNKED_CONTENT_CHUNK_SIZE;
177 ctx->chunk_size = 0;
178 ctx->chunk_got = 0;
179 ctx->chunked_response_written = 0;
180 ctx->chunked_response_size = 0;
181 ctx->chunked_response = NULL;
182 }
183
184 #define POLL_TO_MS 100
185
186 #define HTTP_LINE_TERM "\x0D\x0A"
187 #define HTTP_LINE_TERM_LEN (sizeof(HTTP_LINE_TERM) - 1)
188 #define RESP_PROTO "HTTP/1.1 "
189 #define RESP_PROTO10 "HTTP/1.0 "
190 #define HTTP_KEYVAL_SEPARATOR ": "
191 #define HTTP_HDR_BUFFER_SIZE 1024
192 #define PORT_STR_MAX_BYTES 12
193
194 static int process_http_hdr(http_parse_ctx *parse_ctx, const char *key, const char *val)
195 {
196 // currently we care only about specific headers
197 // we can skip the rest
198 if (parse_ctx->content_length < 0 && !strcmp("content-length", key)) {
199 if (parse_ctx->content_length == TRANSFER_ENCODING_CHUNKED) {
200 netdata_log_error("ACLK: Content-length and transfer-encoding: chunked headers are mutually exclusive");
201 return 1;
202 }
203 if (parse_ctx->content_length != -1) {
204 netdata_log_error("ACLK: Duplicate content-length header");
205 return 1;
206 }
207 parse_ctx->content_length = str2u(val);
208 if (parse_ctx->content_length < 0) {
209 netdata_log_error("ACLK: Invalid content-length %d", parse_ctx->content_length);
210 return 1;
211 }
212 return 0;
213 }
214 if (!strcmp("transfer-encoding", key)) {
215 if (!strcmp("chunked", val)) {
216 if (parse_ctx->content_length != -1) {
217 netdata_log_error("ACLK: Content-length and transfer-encoding: chunked headers are mutually exclusive");
218 return 1;
219 }
220 parse_ctx->content_length = TRANSFER_ENCODING_CHUNKED;
221 }
222 return 0;
223 }
224 void *prev_val = NULL;
225 if (!c_rhash_get_ptr_by_str(parse_ctx->headers, key, &prev_val))
226 freez(prev_val); // drop previous allocation before overwriting
227
228 char *val_cpy = strdupz(val);
229 c_rhash_insert_str_ptr(parse_ctx->headers, key, val_cpy);
230 return 0;
231 }
232
233 const char *get_http_header_by_name(http_parse_ctx *ctx, const char *name)
234 {
235 const char *ret;
236 if (c_rhash_get_ptr_by_str(ctx->headers, name, (void**)&ret))
237 return NULL;
238
239 return ret;
240 }
241
242 static int parse_http_hdr(rbuf_t buf, http_parse_ctx *parse_ctx)
243 {
244 int idx, idx_end;
245 char buf_key[HTTP_HDR_BUFFER_SIZE];
246 char buf_val[HTTP_HDR_BUFFER_SIZE];
247 char *ptr;
248
249 if (!rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx_end)) {
250 netdata_log_error("ACLK: CRLF expected");
251 return 1;
252 }
253
254 char *separator = rbuf_find_bytes(buf, HTTP_KEYVAL_SEPARATOR, strlen(HTTP_KEYVAL_SEPARATOR), &idx);
255 if (!separator) {
256 netdata_log_error("ACLK: Missing Key/Value separator");
257 return 1;
258 }
259 if (idx >= HTTP_HDR_BUFFER_SIZE) {
260 netdata_log_error("ACLK: Key name is too long");
261 return 1;
262 }
263
264 rbuf_pop(buf, buf_key, idx);
265 buf_key[idx] = 0;
266
267 rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
268 idx_end -= strlen(HTTP_KEYVAL_SEPARATOR) + idx;
269 if (idx_end >= HTTP_HDR_BUFFER_SIZE) {
270 netdata_log_error("ACLK: Value of key \"%s\" too long", buf_key);
271 return 1;
272 }
273
274 rbuf_pop(buf, buf_val, idx_end);
275 buf_val[idx_end] = 0;
276
277 for (ptr = buf_key; *ptr; ptr++)
278 *ptr = tolower(*ptr);
279
280 if (process_http_hdr(parse_ctx, buf_key, buf_val))
281 return 1;
282
283 return 0;
284 }
285
286 static inline void chunked_response_buffer_grow_by(http_parse_ctx *parse_ctx, size_t size)
287 {
288 if (unlikely(parse_ctx->chunked_response_size == 0)) {
289 parse_ctx->chunked_response = mallocz(size);
290 parse_ctx->chunked_response_size = size;
291 return;
292 }
293 parse_ctx->chunked_response = reallocz((void *)parse_ctx->chunked_response, parse_ctx->chunked_response_size + size);
294 parse_ctx->chunked_response_size += size;
295 }
296
297 static int process_chunked_content(rbuf_t buf, http_parse_ctx *parse_ctx)
298 {
299 int idx;
300 size_t bytes_to_copy;
301
302 do {
303 switch (parse_ctx->chunked_content_state) {
304 case CHUNKED_CONTENT_CHUNK_SIZE:
305 if (!rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx)) {
306 if (rbuf_bytes_available(buf) >= rbuf_get_capacity(buf))
307 return HTTP_PARSE_ERROR;
308 return HTTP_PARSE_NEED_MORE_DATA;
309 }
310 if (idx == 0) {
311 parse_ctx->chunked_content_state = CHUNKED_CONTENT_FINAL_CRLF;
312 continue;
313 }
314 if (idx >= HTTP_HDR_BUFFER_SIZE) {
315 netdata_log_error("ACLK: Chunk size is too long");
316 return HTTP_PARSE_ERROR;
317 }
318 char buf_size[HTTP_HDR_BUFFER_SIZE];
319 rbuf_pop(buf, buf_size, idx);
320 buf_size[idx] = 0;
321 long chunk_size = strtol(buf_size, NULL, 16);
322 if (chunk_size < 0 || chunk_size == LONG_MAX) {
323 netdata_log_error("ACLK: Chunk size out of range");
324 return HTTP_PARSE_ERROR;
325 }
326 parse_ctx->chunk_size = chunk_size;
327 if (parse_ctx->chunk_size == 0) {
328 if (errno == EINVAL) {
329 netdata_log_error("ACLK: Invalid chunk size");
330 return HTTP_PARSE_ERROR;
331 }
332 parse_ctx->chunked_content_state = CHUNKED_CONTENT_CHUNK_END_CRLF;
333 continue;
334 }
335 parse_ctx->chunk_got = 0;
336 chunked_response_buffer_grow_by(parse_ctx, parse_ctx->chunk_size);
337 rbuf_bump_tail(buf, HTTP_LINE_TERM_LEN);
338 parse_ctx->chunked_content_state = CHUNKED_CONTENT_CHUNK_DATA;
339 // fallthrough
340 case CHUNKED_CONTENT_CHUNK_DATA:
341 if (!(bytes_to_copy = rbuf_bytes_available(buf)))
342 return HTTP_PARSE_NEED_MORE_DATA;
343 if (bytes_to_copy > parse_ctx->chunk_size - parse_ctx->chunk_got)
344 bytes_to_copy = parse_ctx->chunk_size - parse_ctx->chunk_got;
345 rbuf_pop(buf, parse_ctx->chunked_response + parse_ctx->chunked_response_written, bytes_to_copy);
346 parse_ctx->chunk_got += bytes_to_copy;
347 parse_ctx->chunked_response_written += bytes_to_copy;
348 if (parse_ctx->chunk_got != parse_ctx->chunk_size)
349 continue;
350 parse_ctx->chunked_content_state = CHUNKED_CONTENT_CHUNK_END_CRLF;
351 // fallthrough
352 case CHUNKED_CONTENT_FINAL_CRLF:
353 case CHUNKED_CONTENT_CHUNK_END_CRLF:
354 if (rbuf_bytes_available(buf) < HTTP_LINE_TERM_LEN)
355 return HTTP_PARSE_NEED_MORE_DATA;
356 char buf_crlf[HTTP_LINE_TERM_LEN];
357 rbuf_pop(buf, buf_crlf, HTTP_LINE_TERM_LEN);
358 if (memcmp(buf_crlf, HTTP_LINE_TERM, HTTP_LINE_TERM_LEN)) {
359 netdata_log_error("ACLK: CRLF expected");
360 return HTTP_PARSE_ERROR;
361 }
362 if (parse_ctx->chunked_content_state == CHUNKED_CONTENT_FINAL_CRLF) {
363 if (parse_ctx->chunked_response_size != parse_ctx->chunked_response_written)
364 netdata_log_error("ACLK: Chunked response size mismatch");
365 chunked_response_buffer_grow_by(parse_ctx, 1);
366 parse_ctx->chunked_response[parse_ctx->chunked_response_written] = 0;
367 return HTTP_PARSE_SUCCESS;
368 }
369 if (parse_ctx->chunk_size == 0) {
370 parse_ctx->chunked_content_state = CHUNKED_CONTENT_FINAL_CRLF;
371 continue;
372 }
373 parse_ctx->chunked_content_state = CHUNKED_CONTENT_CHUNK_SIZE;
374 continue;
375 }
376 } while(1);
377 }
378
379 http_parse_rc parse_http_response(rbuf_t buf, http_parse_ctx *parse_ctx)
380 {
381 int idx;
382 char rc[4];
383
384 do {
385 if (parse_ctx->state != HTTP_PARSE_CONTENT && !rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx))
386 return HTTP_PARSE_NEED_MORE_DATA;
387 switch (parse_ctx->state) {
388 case HTTP_PARSE_PROXY_CONNECT:
389 case HTTP_PARSE_INITIAL:
390 if (rbuf_memcmp_n(buf, RESP_PROTO, strlen(RESP_PROTO))) {
391 if (parse_ctx->state == HTTP_PARSE_PROXY_CONNECT) {
392 if (rbuf_memcmp_n(buf, RESP_PROTO10, strlen(RESP_PROTO10))) {
393 netdata_log_error(
394 "ACLK: Expected response to start with \"%s\" or \"%s\"", RESP_PROTO, RESP_PROTO10);
395 return HTTP_PARSE_ERROR;
396 }
397 }
398 else {
399 netdata_log_error("ACLK: Expected response to start with \"%s\"", RESP_PROTO);
400 return HTTP_PARSE_ERROR;
401 }
402 }
403 rbuf_bump_tail(buf, strlen(RESP_PROTO));
404 if (rbuf_pop(buf, rc, 4) != 4) {
405 netdata_log_error("ACLK: Expected HTTP status code");
406 return HTTP_PARSE_ERROR;
407 }
408 if (rc[3] != ' ') {
409 netdata_log_error("ACLK: Expected space after HTTP return code");
410 return HTTP_PARSE_ERROR;
411 }
412 rc[3] = 0;
413 parse_ctx->http_code = atoi(rc);
414 if (parse_ctx->http_code < 100 || parse_ctx->http_code >= 600) {
415 netdata_log_error("ACLK: HTTP code not in range 100 to 599");
416 return HTTP_PARSE_ERROR;
417 }
418
419 rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
420
421 rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
422
423 parse_ctx->state = HTTP_PARSE_HEADERS;
424 break;
425 case HTTP_PARSE_HEADERS:
426 if (!idx) {
427 parse_ctx->state = HTTP_PARSE_CONTENT;
428 rbuf_bump_tail(buf, strlen(HTTP_LINE_TERM));
429 break;
430 }
431 if (parse_http_hdr(buf, parse_ctx))
432 return HTTP_PARSE_ERROR;
433 rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
434 rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
435 break;
436 case HTTP_PARSE_CONTENT:
437 // replies like CONNECT etc. do not have content
438 if (parse_ctx->content_length == TRANSFER_ENCODING_CHUNKED)
439 return process_chunked_content(buf, parse_ctx);
440
441 if (parse_ctx->content_length < 0)
442 return HTTP_PARSE_SUCCESS;
443
444 if (parse_ctx->flags & HTTP_PARSE_FLAG_DONT_WAIT_FOR_CONTENT)
445 return HTTP_PARSE_SUCCESS;
446
447 if (rbuf_bytes_available(buf) >= (size_t)parse_ctx->content_length)
448 return HTTP_PARSE_SUCCESS;
449 return HTTP_PARSE_NEED_MORE_DATA;
450 }
451 } while(1);
452 }
453
454 typedef struct https_req_ctx {
455 https_req_t *request;
456
457 int sock;
458 rbuf_t buf_rx;
459
460 struct pollfd poll_fd;
461
462 SSL_CTX *ssl_ctx;
463 SSL *ssl;
464
465 size_t written;
466
467 http_parse_ctx parse_ctx;
468
469 time_t req_start_time;
470 } https_req_ctx_t;
471
472 static int https_req_check_timedout(https_req_ctx_t *ctx) {
473 if (now_realtime_sec() > ctx->req_start_time + ctx->request->timeout_s) {
474 netdata_log_error("ACLK: request timed out");
475 return 1;
476 }
477 return 0;
478 }
479
480 static char *_ssl_err_tos(int err)
481 {
482 switch(err){
483 case SSL_ERROR_SSL:
484 return "SSL_ERROR_SSL";
485 case SSL_ERROR_WANT_READ:
486 return "SSL_ERROR_WANT_READ";
487 case SSL_ERROR_WANT_WRITE:
488 return "SSL_ERROR_WANT_WRITE";
489 case SSL_ERROR_NONE:
490 return "SSL_ERROR_NONE";
491 case SSL_ERROR_ZERO_RETURN:
492 return "SSL_ERROR_ZERO_RETURN";
493 case SSL_ERROR_WANT_CONNECT:
494 return "SSL_ERROR_WANT_CONNECT";
495 case SSL_ERROR_WANT_ACCEPT:
496 return "SSL_ERROR_WANT_ACCEPT";
497 }
498 return "Unknown!!!";
499 }
500
501 static int socket_write_all(https_req_ctx_t *ctx, char *data, size_t data_len) {
502 ctx->written = 0;
503 ctx->poll_fd.events = POLLOUT;
504
505 do {
506 int ret = poll(&ctx->poll_fd, 1, POLL_TO_MS);
507 if (ret < 0) {
508 netdata_log_error("ACLK: poll error");
509 return 1;
510 }
511 if (ret == 0) {
512 if (https_req_check_timedout(ctx)) {
513 netdata_log_error("ACLK: Poll timed out");
514 return 2;
515 }
516 continue;
517 }
518
519 ret = write(ctx->sock, &data[ctx->written], data_len - ctx->written);
520 if (ret > 0) {
521 ctx->written += ret;
522 } else if (errno != EAGAIN && errno != EWOULDBLOCK) {
523 netdata_log_error("ACLK: Error writing to socket");
524 return 3;
525 }
526 } while (ctx->written < data_len);
527
528 return 0;
529 }
530
531 static int ssl_write_all(https_req_ctx_t *ctx, char *data, size_t data_len) {
532 ctx->written = 0;
533 ctx->poll_fd.events |= POLLOUT;
534
535 do {
536 int ret = poll(&ctx->poll_fd, 1, POLL_TO_MS);
537 if (ret < 0) {
538 netdata_log_error("ACLK: poll error");
539 return 1;
540 }
541 if (ret == 0) {
542 if (https_req_check_timedout(ctx)) {
543 netdata_log_error("ACLK: Poll timed out");
544 return 2;
545 }
546 continue;
547 }
548 ctx->poll_fd.events = 0;
549
550 ret = SSL_write(ctx->ssl, &data[ctx->written], data_len - ctx->written);
551 if (ret > 0) {
552 ctx->written += ret;
553 } else {
554 ret = SSL_get_error(ctx->ssl, ret);
555 switch (ret) {
556 case SSL_ERROR_WANT_READ:
557 ctx->poll_fd.events |= POLLIN;
558 break;
559 case SSL_ERROR_WANT_WRITE:
560 ctx->poll_fd.events |= POLLOUT;
561 break;
562 default:
563 netdata_log_error("ACLK: SSL_write Err: %s", _ssl_err_tos(ret));
564 return 3;
565 }
566 }
567 } while (ctx->written < data_len);
568
569 return 0;
570 }
571
572 static inline int https_client_write_all(https_req_ctx_t *ctx, char *data, size_t data_len) {
573 if (ctx->ssl_ctx)
574 return ssl_write_all(ctx, data, data_len);
575 return socket_write_all(ctx, data, data_len);
576 }
577
578 static https_client_resp_t read_parse_response(https_req_ctx_t *ctx) {
579 int ret;
580 char *ptr;
581 size_t size;
582
583 ctx->poll_fd.events = POLLIN;
584 do {
585 ret = poll(&ctx->poll_fd, 1, POLL_TO_MS);
586 if (ret < 0) {
587 netdata_log_error("ACLK: poll error");
588 return HTTPS_CLIENT_RESP_POLL_ERROR;
589 }
590 if (ret == 0) {
591 if (https_req_check_timedout(ctx)) {
592 netdata_log_error("ACLK: poll() timed out");
593 return HTTPS_CLIENT_RESP_TIMEOUT;
594 }
595 if (!ctx->ssl_ctx)
596 continue;
597 }
598 ctx->poll_fd.events = 0;
599
600 do {
601 ptr = rbuf_get_linear_insert_range(ctx->buf_rx, &size);
602
603 if (ctx->ssl_ctx)
604 ret = SSL_read(ctx->ssl, ptr, size);
605 else
606 ret = read(ctx->sock, ptr, size);
607
608 if (ret > 0) {
609 rbuf_bump_head(ctx->buf_rx, ret);
610 } else {
611 if (ctx->ssl_ctx) {
612 ret = SSL_get_error(ctx->ssl, ret);
613 switch (ret) {
614 case SSL_ERROR_WANT_READ:
615 ctx->poll_fd.events |= POLLIN;
616 break;
617 case SSL_ERROR_WANT_WRITE:
618 ctx->poll_fd.events |= POLLOUT;
619 break;
620 default:
621 netdata_log_error("ACLK: SSL_read() Err: %s", _ssl_err_tos(ret));
622 return HTTPS_CLIENT_RESP_READ_ERROR;
623 }
624 }
625 else {
626 if (errno != EAGAIN && errno != EWOULDBLOCK) {
627 netdata_log_error("ACLK: read error");
628 return HTTPS_CLIENT_RESP_READ_ERROR;
629 }
630 ctx->poll_fd.events |= POLLIN;
631 }
632 }
633 } while (ctx->poll_fd.events == 0 && rbuf_bytes_free(ctx->buf_rx) > 0);
634 } while (!(ret = parse_http_response(ctx->buf_rx, &ctx->parse_ctx)));
635
636 if (ret != HTTP_PARSE_SUCCESS) {
637 netdata_log_error("ACLK: error parsing HTTP response");
638 return HTTPS_CLIENT_RESP_PARSE_ERROR;
639 }
640
641 return HTTPS_CLIENT_RESP_OK;
642 }
643
644 static const char *http_methods[] = {
645 [HTTP_REQ_GET] = "GET ",
646 [HTTP_REQ_POST] = "POST ",
647 };
648
649
650 #define TX_BUFFER_SIZE 8192
651 #define RX_BUFFER_SIZE (TX_BUFFER_SIZE*2)
652 static https_client_resp_t handle_http_request(https_req_ctx_t *ctx) {
653 BUFFER *hdr = buffer_create(TX_BUFFER_SIZE, &netdata_buffers_statistics.buffers_aclk);
654 https_client_resp_t rc = HTTPS_CLIENT_RESP_OK;
655
656 http_req_type_t req_type = ctx->request->request_type;
657
658 if (req_type >= HTTP_REQ_INVALID) {
659 netdata_log_error("ACLK: unknown HTTPS request type!");
660 rc = HTTPS_CLIENT_RESP_UNKNOWN_REQUEST_TYPE;
661 goto err_exit;
662 }
663 buffer_strcat(hdr, http_methods[req_type]);
664
665 buffer_strcat(hdr, ctx->request->url);
666 http_parse_ctx_create(&ctx->parse_ctx, HTTP_PARSE_INITIAL);
667
668 buffer_strcat(hdr, HTTP_1_1 HTTP_ENDL);
669
670 buffer_sprintf(hdr, "Host: %s\x0D\x0A", ctx->request->host);
671 buffer_strcat(hdr, "User-Agent: Netdata/rocks newhttpclient\x0D\x0A");
672
673 if (req_type == HTTP_REQ_POST && ctx->request->payload && ctx->request->payload_size) {
674 buffer_sprintf(hdr, "Content-Length: %zu\x0D\x0A", ctx->request->payload_size);
675 }
676 buffer_strcat(hdr, "\x0D\x0A");
677
678 // Send the request
679 if (https_client_write_all(ctx, hdr->buffer, hdr->len)) {
680 netdata_log_error("ACLK: couldn't write HTTP request header into SSL connection");
681 rc = HTTPS_CLIENT_RESP_HEADER_WRITE_FAILED;
682 goto err_exit;
683 }
684
685 if (req_type == HTTP_REQ_POST && ctx->request->payload && ctx->request->payload_size) {
686 if (https_client_write_all(ctx, ctx->request->payload, ctx->request->payload_size)) {
687 netdata_log_error("ACLK: couldn't write payload into SSL connection");
688 rc = HTTPS_CLIENT_RESP_PAYLOAD_WRITE_FAILED;
689 goto err_exit;
690 }
691 }
692
693 // Read The Response
694 rc = read_parse_response(ctx);
695 if (rc != HTTPS_CLIENT_RESP_OK) {
696 netdata_log_error("ACLK: error reading or parsing response from server");
697 if (ctx->parse_ctx.chunked_response) {
698 freez(ctx->parse_ctx.chunked_response);
699 ctx->parse_ctx.chunked_response = NULL;
700 ctx->parse_ctx.chunked_response_size = 0;
701 ctx->parse_ctx.chunked_response_written = 0;
702 }
703 }
704
705 err_exit:
706 buffer_free(hdr);
707 return rc;
708 }
709
710 static int cert_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
711 {
712 int err = 0;
713
714 if (!preverify_ok) {
715 err = X509_STORE_CTX_get_error(ctx);
716 netdata_ssl_log_verify_error(ctx);
717 }
718
719 if(cloud_config_insecure_get()) {
720 if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
721 preverify_ok = 1;
722 netdata_log_error(
723 "ACLK: Self Signed Certificate Accepted as the agent was configured with ACLK_SSL_ALLOW_SELF_SIGNED");
724 }
725 }
726
727 return preverify_ok;
728 }
729
730 https_client_resp_t https_request(https_req_t *request, https_req_response_t *response, bool *fallback_ipv4)
731 {
732 https_client_resp_t rc;
733 int ret;
734 char connect_port_str[PORT_STR_MAX_BYTES];
735
736 bool proxy_used = (request->proxy_host != NULL);
737
738 // extract protocol prefix from proxy URL for logging
739 const char *proxy_proto = "";
740 char proto_buf[16];
741 if (proxy_used && request->proxy) {
742 const char *sep = strstr(request->proxy, "://");
743 if (sep) {
744 size_t len = (size_t)(sep - request->proxy) + 3;
745 if (len < sizeof(proto_buf)) {
746 memcpy(proto_buf, request->proxy, len);
747 proto_buf[len] = '\0';
748 proxy_proto = proto_buf;
749 }
750 }
751 }
752
753 // assume no proxy
754 const char *connect_host;
755 int connect_port;
756
757 if (unlikely(proxy_used)) {
758 connect_host = request->proxy_host;
759 connect_port = request->proxy_port;
760 } else {
761 connect_host = request->host;
762 connect_port = request->port;
763 }
764
765 https_req_ctx_t *ctx = callocz(1, sizeof(https_req_ctx_t));
766 ctx->req_start_time = now_realtime_sec();
767
768 ctx->buf_rx = rbuf_create(RX_BUFFER_SIZE);
769 if (!ctx->buf_rx) {
770 rc = HTTPS_CLIENT_RESP_NO_MEM;
771 netdata_log_error("ACLK: couldn't allocate buffer for RX data");
772 goto exit_req_ctx;
773 }
774
775 snprintfz(connect_port_str, PORT_STR_MAX_BYTES, "%d", connect_port);
776
777 if (proxy_used)
778 nd_log_daemon(NDLP_INFO, "ACLK: connecting to %s:%d via proxy %s%s:%d%s",
779 request->host, request->port,
780 proxy_proto, request->proxy_host, request->proxy_port,
781 request->proxy_username ? " (with credentials)" : " (without credentials)");
782 else
783 nd_log_daemon(NDLP_INFO, "ACLK: connecting to %s:%d (no proxy)",
784 request->host, request->port);
785
786 struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
787 ctx->sock = connect_to_this_ip46(IPPROTO_TCP, SOCK_STREAM, connect_host, 0, connect_port_str, &timeout, fallback_ipv4);
788 if (ctx->sock < 0) {
789 rc = -ctx->sock;
790 netdata_log_error("ACLK: error connecting TCP socket to \"%s\"", connect_host);
791 goto exit_buf_rx;
792 }
793
794 if (fcntl(ctx->sock, F_SETFL, fcntl(ctx->sock, F_GETFL, 0) | O_NONBLOCK) == -1) {
795 rc = HTTPS_CLIENT_RESP_NONBLOCK_FAILED;
796 netdata_log_error("ACLK: error setting O_NONBLOCK to TCP socket.");
797 goto exit_sock;
798 }
799
800 ctx->poll_fd.fd = ctx->sock;
801
802 // Do proxy negotiation if proxy is used.
803 if (request->proxy_host) {
804 enum mqtt_wss_proxy_type proxy_type = (enum mqtt_wss_proxy_type)request->proxy_type;
805 if (proxy_type == MQTT_WSS_DIRECT)
806 proxy_type = aclk_proxy_type_from_scheme(request->proxy);
807 if (proxy_type == MQTT_WSS_DIRECT)
808 proxy_type = MQTT_WSS_PROXY_HTTP;
809
810 int proxy_timeout_ms = (request->timeout_s > 0 && request->timeout_s <= 2000000)
811 ? (int)request->timeout_s * 1000
812 : 30000;
813
814 if (aclk_proxy_negotiation_connect(ctx->sock, proxy_type, request->proxy_username, request->proxy_password,
815 request->host, request->port, proxy_timeout_ms)) {
816 rc = HTTPS_CLIENT_RESP_PROXY_NEGOTIATION_FAILED;
817 netdata_log_error("ACLK: %sproxy negotiation failed via %s:%d to %s:%d",
818 aclk_mqtt_proxy_type_to_scheme(proxy_type),
819 request->proxy_host, request->proxy_port,
820 request->host, request->port);
821 goto exit_sock;
822 }
823 }
824 ctx->request = request;
825
826 ctx->ssl_ctx = netdata_ssl_create_client_ctx(0);
827 if (ctx->ssl_ctx==NULL) {
828 rc = HTTPS_CLIENT_RESP_NO_SSL_CTX;
829 netdata_log_error("ACLK: cannot allocate SSL context");
830 goto exit_sock;
831 }
832
833 if (!SSL_CTX_set_default_verify_paths(ctx->ssl_ctx)) {
834 rc = HTTPS_CLIENT_RESP_NO_SSL_VERIFY_PATHS;
835 netdata_log_error("ACLK: error setting default verify paths");
836 goto exit_CTX;
837 }
838 SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, cert_verify_callback);
839
840 ctx->ssl = SSL_new(ctx->ssl_ctx);
841 if (ctx->ssl==NULL) {
842 rc = HTTPS_CLIENT_RESP_NO_SSL_NEW;
843 netdata_log_error("ACLK: cannot allocate SSL");
844 goto exit_CTX;
845 }
846
847 if (!SSL_set_tlsext_host_name(ctx->ssl, request->host)) {
848 rc = HTTPS_CLIENT_RESP_NO_TLS_SNI;
849 netdata_log_error("ACLK: error setting TLS SNI host");
850 goto exit_CTX;
851 }
852
853 SSL_set_fd(ctx->ssl, ctx->sock);
854 ret = SSL_connect(ctx->ssl);
855 if (ret != -1 && ret != 1) {
856 rc = HTTPS_CLIENT_RESP_SSL_CONNECT_FAILED;
857 netdata_log_error("ACLK: SSL failed to connect");
858 goto exit_SSL;
859 }
860 if (ret == -1) {
861 // expected as underlying socket is non blocking!
862 // consult SSL_connect documentation for details
863 int ec = SSL_get_error(ctx->ssl, ret);
864 if (ec != SSL_ERROR_WANT_READ && ec != SSL_ERROR_WANT_WRITE) {
865 rc = HTTPS_CLIENT_RESP_SSL_START_FAILED;
866 netdata_log_error("ACLK: failed to start SSL connection");
867 goto exit_SSL;
868 }
869 }
870
871 // The actual request here
872 rc = handle_http_request(ctx);
873 if (rc != HTTPS_CLIENT_RESP_OK) {
874 netdata_log_error("ACLK: couldn't process request");
875 http_parse_ctx_destroy(&ctx->parse_ctx);
876 goto exit_SSL;
877 }
878 http_parse_ctx_destroy(&ctx->parse_ctx);
879 response->http_code = ctx->parse_ctx.http_code;
880 if (ctx->parse_ctx.content_length == TRANSFER_ENCODING_CHUNKED) {
881 response->payload_size = ctx->parse_ctx.chunked_response_size;
882 response->payload = ctx->parse_ctx.chunked_response;
883 ctx->parse_ctx.chunked_response = NULL;
884 ctx->parse_ctx.chunked_response_size = 0;
885 ctx->parse_ctx.chunked_response_written = 0;
886 }
887 if (ctx->parse_ctx.content_length > 0) {
888 response->payload_size = ctx->parse_ctx.content_length;
889 response->payload = mallocz(response->payload_size + 1);
890 ret = rbuf_pop(ctx->buf_rx, response->payload, response->payload_size);
891 if (ret != (int)response->payload_size) {
892 netdata_log_error("ACLK: payload size doesn't match remaining data on the buffer!");
893 response->payload_size = ret;
894 }
895 // normally we take payload as it is and copy it
896 // but for convenience in cases where payload is sth. like
897 // json we add terminating zero so that user of the data
898 // doesn't have to convert to C string (0 terminated)
899 // other uses still have correct payload_size and can copy
900 // only exact data without affixed 0x00
901 ((char*)response->payload)[response->payload_size] = 0; // mallocz(response->payload_size + 1);
902 }
903 errno_clear();
904 netdata_log_info("ACLK: HTTPS \"%s\" request to \"%s\" finished with HTTP code: %d", http_req_type_to_str(ctx->request->request_type), ctx->request->host, response->http_code);
905
906 rc = HTTPS_CLIENT_RESP_OK;
907
908 exit_SSL:
909 SSL_free(ctx->ssl);
910 exit_CTX:
911 SSL_CTX_free(ctx->ssl_ctx);
912 exit_sock:
913 close(ctx->sock);
914 exit_buf_rx:
915 rbuf_free(ctx->buf_rx);
916 exit_req_ctx:
917 http_parse_ctx_destroy(&ctx->parse_ctx);
918 freez(ctx);
919 return rc;
920 }
921
922 void https_req_response_free(https_req_response_t *res) {
923 freez(res->payload);
924 }
925
926 static inline char *UNUSED_FUNCTION(min_non_null)(char *a, char *b) {
927 if (!a)
928 return b;
929 if (!b)
930 return a;
931 return (a < b ? a : b);
932 }
933
934 #define URI_PROTO_SEPARATOR "://"
935 #define URL_PARSER_LOG_PREFIX "ACLK: url_parser "
936
937 static int parse_host_port(url_t *url) {
938 char *ptr = strrchr(url->host, ':');
939 if (ptr) {
940 size_t port_len = strlen(ptr + 1);
941 if (!port_len) {
942 netdata_log_error(URL_PARSER_LOG_PREFIX ": specified but no port number");
943 return 1;
944 }
945 if (port_len > 5 /* MAX port length is 5digit long in decimal */) {
946 netdata_log_error(URL_PARSER_LOG_PREFIX "port # is too long");
947 return 1;
948 }
949 *ptr = 0;
950 if (!strlen(url->host)) {
951 netdata_log_error(URL_PARSER_LOG_PREFIX "host empty after removing port");
952 return 1;
953 }
954 url->port = atoi (ptr + 1);
955 }
956 return 0;
957 }
958
959 static inline void port_by_proto(url_t *url) {
960 if (url->port)
961 return;
962 if (!url->proto)
963 return;
964 if (!strcmp(url->proto, "http")) {
965 url->port = 80;
966 return;
967 }
968 if (!strcmp(url->proto, "https")) {
969 url->port = 443;
970 return;
971 }
972 }
973
974 #define STRDUPZ_2PTR(dest, start, end) do { \
975 dest = mallocz(1 + end - start); \
976 memcpy(dest, start, end - start); \
977 dest[end - start] = 0; \
978 } while(0)
979
980 int url_parse(const char *url, url_t *parsed) {
981 const char *start = url;
982 const char *end = strstr(url, URI_PROTO_SEPARATOR);
983
984 if (end) {
985 if (end == start) {
986 netdata_log_error(URL_PARSER_LOG_PREFIX "found " URI_PROTO_SEPARATOR " without protocol specified");
987 return 1;
988 }
989
990 STRDUPZ_2PTR(parsed->proto, start, end);
991 start = end + strlen(URI_PROTO_SEPARATOR);
992 }
993
994 end = strchr(start, '/');
995 if (!end)
996 end = start + strlen(start);
997
998 if (start == end) {
999 netdata_log_error(URL_PARSER_LOG_PREFIX "Host empty");
1000 return 1;
1001 }
1002
1003 STRDUPZ_2PTR(parsed->host, start, end);
1004
1005 if (parse_host_port(parsed))
1006 return 1;
1007
1008 if (!*end) {
1009 parsed->path = strdupz("/");
1010 port_by_proto(parsed);
1011 return 0;
1012 }
1013
1014 parsed->path = strdupz(end);
1015 port_by_proto(parsed);
1016 return 0;
1017 }
1018
1019 void url_t_destroy(url_t *url) {
1020 freez(url->host);
1021 freez(url->path);
1022 freez(url->proto);
1023 }