summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTimotej S <6674623+underhood@users.noreply.github.com>2021-03-19 11:33:23 +0100
committerGitHub <noreply@github.com>2021-03-19 11:33:23 +0100
commitf36d14dbb8633d3228bd116b524ff5d13b3c87d3 (patch)
tree155a8be006407b1ce249978cf4d0fbd1b20746af
parent59638f03a9a4c0a48591d31f3c0e8985c47a484c (diff)
move https_client into separate file (#10784)
-rw-r--r--Makefile.am2
-rw-r--r--aclk/aclk_otp.c248
-rw-r--r--aclk/https_client.c246
-rw-r--r--aclk/https_client.h11
4 files changed, 260 insertions, 247 deletions
diff --git a/Makefile.am b/Makefile.am
index 9ea6790979..180d337ce9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -555,6 +555,8 @@ ACLK_FILES = \
aclk/aclk_tx_msgs.h \
aclk/aclk_rx_msgs.c \
aclk/aclk_rx_msgs.h \
+ aclk/https_client.c \
+ aclk/https_client.h \
$(NULL)
else #ACLK_NG
ACLK_FILES = \
diff --git a/aclk/aclk_otp.c b/aclk/aclk_otp.c
index 22ce746f61..fcb9d600cd 100644
--- a/aclk/aclk_otp.c
+++ b/aclk/aclk_otp.c
@@ -3,7 +3,7 @@
#include "aclk_otp.h"
-#include "libnetdata/libnetdata.h"
+#include "https_client.h"
#include "../daemon/common.h"
@@ -167,252 +167,6 @@ static int private_decrypt(RSA *p_key, unsigned char * enc_data, int data_len, u
return result;
}
-typedef enum http_req_type {
- HTTP_REQ_GET,
- HTTP_REQ_POST
-} http_req_type;
-
-enum http_parse_state {
- HTTP_PARSE_INITIAL = 0,
- HTTP_PARSE_HEADERS,
- HTTP_PARSE_CONTENT
-};
-
-typedef struct {
- enum http_parse_state state;
- int content_length;
- int http_code;
-} http_parse_ctx;
-
-#define HTTP_PARSE_CTX_INITIALIZER { .state = HTTP_PARSE_INITIAL, .content_length = -1, .http_code = 0 }
-
-#define NEED_MORE_DATA 0
-#define PARSE_SUCCESS 1
-#define PARSE_ERROR -1
-#define HTTP_LINE_TERM "\x0D\x0A"
-#define RESP_PROTO "HTTP/1.1 "
-#define HTTP_KEYVAL_SEPARATOR ": "
-#define HTTP_HDR_BUFFER_SIZE 256
-#define PORT_STR_MAX_BYTES 7
-
-static void process_http_hdr(http_parse_ctx *parse_ctx, const char *key, const char *val)
-{
- // currently we care only about content-length
- // but in future the way this is written
- // it can be extended
- if (!strcmp("content-length", key)) {
- parse_ctx->content_length = atoi(val);
- }
-}
-
-static int parse_http_hdr(rbuf_t buf, http_parse_ctx *parse_ctx)
-{
- int idx, idx_end;
- char buf_key[HTTP_HDR_BUFFER_SIZE];
- char buf_val[HTTP_HDR_BUFFER_SIZE];
- char *ptr = buf_key;
- if (!rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx_end)) {
- error("CRLF expected");
- return 1;
- }
-
- char *separator = rbuf_find_bytes(buf, HTTP_KEYVAL_SEPARATOR, strlen(HTTP_KEYVAL_SEPARATOR), &idx);
- if (!separator) {
- error("Missing Key/Value separator");
- return 1;
- }
- if (idx >= HTTP_HDR_BUFFER_SIZE) {
- error("Key name is too long");
- return 1;
- }
-
- rbuf_pop(buf, buf_key, idx);
- buf_key[idx] = 0;
-
- rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
- idx_end -= strlen(HTTP_KEYVAL_SEPARATOR) + idx;
- if (idx_end >= HTTP_HDR_BUFFER_SIZE) {
- error("Value of key \"%s\" too long", buf_key);
- return 1;
- }
-
- rbuf_pop(buf, buf_val, idx_end);
- buf_val[idx_end] = 0;
-
- rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
-
- for (ptr = buf_key; *ptr; ptr++)
- *ptr = tolower(*ptr);
-
- process_http_hdr(parse_ctx, buf_key, buf_val);
-
- return 0;
-}
-
-static int parse_http_response(rbuf_t buf, http_parse_ctx *parse_ctx)
-{
- int idx;
- char rc[4];
-
- do {
- if (parse_ctx->state != HTTP_PARSE_CONTENT && !rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx))
- return NEED_MORE_DATA;
- switch (parse_ctx->state) {
- case HTTP_PARSE_INITIAL:
- if (rbuf_memcmp_n(buf, RESP_PROTO, strlen(RESP_PROTO))) {
- error("Expected response to start with \"%s\"", RESP_PROTO);
- return PARSE_ERROR;
- }
- rbuf_bump_tail(buf, strlen(RESP_PROTO));
- if (rbuf_pop(buf, rc, 4) != 4) {
- error("Expected HTTP status code");
- return PARSE_ERROR;
- }
- if (rc[3] != ' ') {
- error("Expected space after HTTP return code");
- return PARSE_ERROR;
- }
- rc[3] = 0;
- parse_ctx->http_code = atoi(rc);
- if (parse_ctx->http_code < 100 || parse_ctx->http_code >= 600) {
- error("HTTP code not in range 100 to 599");
- return PARSE_ERROR;
- }
-
- rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
-
- rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
-
- parse_ctx->state = HTTP_PARSE_HEADERS;
- break;
- case HTTP_PARSE_HEADERS:
- if (!idx) {
- parse_ctx->state = HTTP_PARSE_CONTENT;
- rbuf_bump_tail(buf, strlen(HTTP_LINE_TERM));
- break;
- }
- if (parse_http_hdr(buf, parse_ctx))
- return PARSE_ERROR;
- rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
- rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
- break;
- case HTTP_PARSE_CONTENT:
- if (parse_ctx->content_length < 0) {
- error("content-length missing and http headers ended");
- return PARSE_ERROR;
- }
- if (rbuf_bytes_available(buf) >= (size_t)parse_ctx->content_length)
- return PARSE_SUCCESS;
- return NEED_MORE_DATA;
- }
- } while(1);
-}
-
-static int https_request(http_req_type method, char *host, int port, char *url, char *b, size_t b_size, char *payload)
-{
- struct timeval timeout = { .tv_sec = 30, .tv_usec = 0 };
- char sport[PORT_STR_MAX_BYTES];
- size_t len = 0;
- int rc = 1;
- int ret;
- char *ptr;
- http_parse_ctx parse_ctx = HTTP_PARSE_CTX_INITIALIZER;
-
- rbuf_t buffer = rbuf_create(b_size);
- if (!buffer)
- return 1;
-
- snprintf(sport, PORT_STR_MAX_BYTES, "%d", port);
-
- if (payload != NULL)
- len = strlen(payload);
-
- snprintf(
- b,
- b_size,
- "%s %s HTTP/1.1\r\nHost: %s\r\nAccept: application/json\r\nContent-length: %zu\r\nAccept-Language: en-us\r\n"
- "User-Agent: Netdata/rocks\r\n\r\n",
- (method == HTTP_REQ_GET ? "GET" : "POST"), url, host, len);
-
- if (payload != NULL)
- strncat(b, payload, b_size - len);
-
- len = strlen(b);
-
- debug(D_ACLK, "Sending HTTPS req (%zu bytes): '%s'", len, b);
- int sock = connect_to_this_ip46(IPPROTO_TCP, SOCK_STREAM, host, 0, sport, &timeout);
-
- if (unlikely(sock == -1)) {
- error("Handshake failed");
- goto exit_buf;
- }
-
- SSL_CTX *ctx = security_initialize_openssl_client();
- if (ctx==NULL) {
- error("Cannot allocate SSL context");
- goto exit_sock;
- }
- // Certificate chain: not updating the stores - do we need private CA roots?
- // Calls to SSL_CTX_load_verify_locations would go here.
- SSL *ssl = SSL_new(ctx);
- if (ssl==NULL) {
- error("Cannot allocate SSL");
- goto exit_CTX;
- }
- SSL_set_fd(ssl, sock);
- ret = SSL_connect(ssl);
- if (ret != 1) {
- error("SSL_connect() failed with err=%d", ret);
- goto exit_SSL;
- }
-
- ret = SSL_write(ssl, b, len);
- if (ret <= 0)
- {
- error("SSL_write() failed with err=%d", ret);
- goto exit_SSL;
- }
-
- b[0] = 0;
-
- do {
- ptr = rbuf_get_linear_insert_range(buffer, &len);
- ret = SSL_read(ssl, ptr, len - 1);
- if (ret)
- rbuf_bump_head(buffer, ret);
- if (ret <= 0)
- {
- error("No response available - SSL_read()=%d", ret);
- goto exit_FULL;
- }
- } while (!(ret = parse_http_response(buffer, &parse_ctx)));
-
- if (ret != PARSE_SUCCESS) {
- error("Error parsing HTTP response");
- goto exit_FULL;
- }
-
- if (parse_ctx.http_code < 200 || parse_ctx.http_code >= 300) {
- error("HTTP Response not Success (got %d)", parse_ctx.http_code);
- goto exit_FULL;
- }
-
- len = rbuf_pop(buffer, b, b_size);
- b[MIN(len, b_size-1)] = 0;
-
- rc = 0;
-exit_FULL:
-exit_SSL:
- SSL_free(ssl);
-exit_CTX:
- SSL_CTX_free(ctx);
-exit_sock:
- close(sock);
-exit_buf:
- rbuf_free(buffer);
- return rc;
-}
-
// aclk_get_mqtt_otp is slightly modified original code from @amoss
void aclk_get_mqtt_otp(RSA *p_key, char *aclk_hostname, int port, char **mqtt_usr, char **mqtt_pass)
{
diff --git a/aclk/https_client.c b/aclk/https_client.c
new file mode 100644
index 0000000000..1b9546d779
--- /dev/null
+++ b/aclk/https_client.c
@@ -0,0 +1,246 @@
+#include "libnetdata/libnetdata.h"
+
+#include "https_client.h"
+
+#include "../mqtt_websockets/c-rbuf/include/ringbuffer.h"
+
+enum http_parse_state {
+ HTTP_PARSE_INITIAL = 0,
+ HTTP_PARSE_HEADERS,
+ HTTP_PARSE_CONTENT
+};
+
+typedef struct {
+ enum http_parse_state state;
+ int content_length;
+ int http_code;
+} http_parse_ctx;
+
+#define HTTP_PARSE_CTX_INITIALIZER { .state = HTTP_PARSE_INITIAL, .content_length = -1, .http_code = 0 }
+
+#define NEED_MORE_DATA 0
+#define PARSE_SUCCESS 1
+#define PARSE_ERROR -1
+#define HTTP_LINE_TERM "\x0D\x0A"
+#define RESP_PROTO "HTTP/1.1 "
+#define HTTP_KEYVAL_SEPARATOR ": "
+#define HTTP_HDR_BUFFER_SIZE 256
+#define PORT_STR_MAX_BYTES 7
+
+static void process_http_hdr(http_parse_ctx *parse_ctx, const char *key, const char *val)
+{
+ // currently we care only about content-length
+ // but in future the way this is written
+ // it can be extended
+ if (!strcmp("content-length", key)) {
+ parse_ctx->content_length = atoi(val);
+ }
+}
+
+static int parse_http_hdr(rbuf_t buf, http_parse_ctx *parse_ctx)
+{
+ int idx, idx_end;
+ char buf_key[HTTP_HDR_BUFFER_SIZE];
+ char buf_val[HTTP_HDR_BUFFER_SIZE];
+ char *ptr = buf_key;
+ if (!rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx_end)) {
+ error("CRLF expected");
+ return 1;
+ }
+
+ char *separator = rbuf_find_bytes(buf, HTTP_KEYVAL_SEPARATOR, strlen(HTTP_KEYVAL_SEPARATOR), &idx);
+ if (!separator) {
+ error("Missing Key/Value separator");
+ return 1;
+ }
+ if (idx >= HTTP_HDR_BUFFER_SIZE) {
+ error("Key name is too long");
+ return 1;
+ }
+
+ rbuf_pop(buf, buf_key, idx);
+ buf_key[idx] = 0;
+
+ rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
+ idx_end -= strlen(HTTP_KEYVAL_SEPARATOR) + idx;
+ if (idx_end >= HTTP_HDR_BUFFER_SIZE) {
+ error("Value of key \"%s\" too long", buf_key);
+ return 1;
+ }
+
+ rbuf_pop(buf, buf_val, idx_end);
+ buf_val[idx_end] = 0;
+
+ rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
+
+ for (ptr = buf_key; *ptr; ptr++)
+ *ptr = tolower(*ptr);
+
+ process_http_hdr(parse_ctx, buf_key, buf_val);
+
+ return 0;
+}
+
+static int parse_http_response(rbuf_t buf, http_parse_ctx *parse_ctx)
+{
+ int idx;
+ char rc[4];
+
+ do {
+ if (parse_ctx->state != HTTP_PARSE_CONTENT && !rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx))
+ return NEED_MORE_DATA;
+ switch (parse_ctx->state) {
+ case HTTP_PARSE_INITIAL:
+ if (rbuf_memcmp_n(buf, RESP_PROTO, strlen(RESP_PROTO))) {
+ error("Expected response to start with \"%s\"", RESP_PROTO);
+ return PARSE_ERROR;
+ }
+ rbuf_bump_tail(buf, strlen(RESP_PROTO));
+ if (rbuf_pop(buf, rc, 4) != 4) {
+ error("Expected HTTP status code");
+ return PARSE_ERROR;
+ }
+ if (rc[3] != ' ') {
+ error("Expected space after HTTP return code");
+ return PARSE_ERROR;
+ }
+ rc[3] = 0;
+ parse_ctx->http_code = atoi(rc);
+ if (parse_ctx->http_code < 100 || parse_ctx->http_code >= 600) {
+ error("HTTP code not in range 100 to 599");
+ return PARSE_ERROR;
+ }
+
+ rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
+
+ rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
+
+ parse_ctx->state = HTTP_PARSE_HEADERS;
+ break;
+ case HTTP_PARSE_HEADERS:
+ if (!idx) {
+ parse_ctx->state = HTTP_PARSE_CONTENT;
+ rbuf_bump_tail(buf, strlen(HTTP_LINE_TERM));
+ break;
+ }
+ if (parse_http_hdr(buf, parse_ctx))
+ return PARSE_ERROR;
+ rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
+ rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
+ break;
+ case HTTP_PARSE_CONTENT:
+ if (parse_ctx->content_length < 0) {
+ error("content-length missing and http headers ended");
+ return PARSE_ERROR;
+ }
+ if (rbuf_bytes_available(buf) >= (size_t)parse_ctx->content_length)
+ return PARSE_SUCCESS;
+ return NEED_MORE_DATA;
+ }
+ } while(1);
+}
+
+int https_request(http_req_type_t method, char *host, int port, char *url, char *b, size_t b_size, char *payload)
+{
+ struct timeval timeout = { .tv_sec = 30, .tv_usec = 0 };
+ char sport[PORT_STR_MAX_BYTES];
+ size_t len = 0;
+ int rc = 1;
+ int ret;
+ char *ptr;
+ http_parse_ctx parse_ctx = HTTP_PARSE_CTX_INITIALIZER;
+
+ rbuf_t buffer = rbuf_create(b_size);
+ if (!buffer)
+ return 1;
+
+ snprintf(sport, PORT_STR_MAX_BYTES, "%d", port);
+
+ if (payload != NULL)
+ len = strlen(payload);
+
+ snprintf(
+ b,
+ b_size,
+ "%s %s HTTP/1.1\r\nHost: %s\r\nAccept: application/json\r\nContent-length: %zu\r\nAccept-Language: en-us\r\n"
+ "User-Agent: Netdata/rocks\r\n\r\n",
+ (method == HTTP_REQ_GET ? "GET" : "POST"), url, host, len);
+
+ if (payload != NULL)
+ strncat(b, payload, b_size - len);
+
+ len = strlen(b);
+
+ debug(D_ACLK, "Sending HTTPS req (%zu bytes): '%s'", len, b);
+ int sock = connect_to_this_ip46(IPPROTO_TCP, SOCK_STREAM, host, 0, sport, &timeout);
+
+ if (unlikely(sock == -1)) {
+ error("Handshake failed");
+ goto exit_buf;
+ }
+
+ SSL_CTX *ctx = security_initialize_openssl_client();
+ if (ctx==NULL) {
+ error("Cannot allocate SSL context");
+ goto exit_sock;
+ }
+ // Certificate chain: not updating the stores - do we need private CA roots?
+ // Calls to SSL_CTX_load_verify_locations would go here.
+ SSL *ssl = SSL_new(ctx);
+ if (ssl==NULL) {
+ error("Cannot allocate SSL");
+ goto exit_CTX;
+ }
+ SSL_set_fd(ssl, sock);
+ ret = SSL_connect(ssl);
+ if (ret != 1) {
+ error("SSL_connect() failed with err=%d", ret);
+ goto exit_SSL;
+ }
+
+ ret = SSL_write(ssl, b, len);
+ if (ret <= 0)
+ {
+ error("SSL_write() failed with err=%d", ret);
+ goto exit_SSL;
+ }
+
+ b[0] = 0;
+
+ do {
+ ptr = rbuf_get_linear_insert_range(buffer, &len);
+ ret = SSL_read(ssl, ptr, len - 1);
+ if (ret)
+ rbuf_bump_head(buffer, ret);
+ if (ret <= 0)
+ {
+ error("No response available - SSL_read()=%d", ret);
+ goto exit_FULL;
+ }
+ } while (!(ret = parse_http_response(buffer, &parse_ctx)));
+
+ if (ret != PARSE_SUCCESS) {
+ error("Error parsing HTTP response");
+ goto exit_FULL;
+ }
+
+ if (parse_ctx.http_code < 200 || parse_ctx.http_code >= 300) {
+ error("HTTP Response not Success (got %d)", parse_ctx.http_code);
+ goto exit_FULL;
+ }
+
+ len = rbuf_pop(buffer, b, b_size);
+ b[MIN(len, b_size-1)] = 0;
+
+ rc = 0;
+exit_FULL:
+exit_SSL:
+ SSL_free(ssl);
+exit_CTX:
+ SSL_CTX_free(ctx);
+exit_sock:
+ close(sock);
+exit_buf:
+ rbuf_free(buffer);
+ return rc;
+}
diff --git a/aclk/https_client.h b/aclk/https_client.h
new file mode 100644
index 0000000000..0d2e0dba7e
--- /dev/null
+++ b/aclk/https_client.h
@@ -0,0 +1,11 @@
+#ifndef NETDATA_HTTPS_CLIENT_H
+#define NETDATA_HTTPS_CLIENT_H
+
+typedef enum http_req_type {
+ HTTP_REQ_GET,
+ HTTP_REQ_POST
+} http_req_type_t;
+
+int https_request(http_req_type_t method, char *host, int port, char *url, char *b, size_t b_size, char *payload);
+
+#endif /* NETDATA_HTTPS_CLIENT_H */