@cryptotaxi247 / netdata-1 / commits / f36d14dbb

move https_client into separate file (#10784)

Timotej S committed Mar 19, 2021 at 11:33 UTC f36d14dbb8633d3228bd116b524ff5d13b3c87d3
4 files changed +260 -247
Makefile.am
+2
@@ -555,6 +555,8 @@ ACLK_FILES = \
555 aclk/aclk_tx_msgs.h \
556 aclk/aclk_rx_msgs.c \
557 aclk/aclk_rx_msgs.h \
558 + aclk/https_client.c \
559 + aclk/https_client.h \
560 $(NULL)
561 else #ACLK_NG
562 ACLK_FILES = \
aclk/aclk_otp.c
+1 -247
@@ -3,7 +3,7 @@
3
4 #include "aclk_otp.h"
5
6 -#include "libnetdata/libnetdata.h"
6 +#include "https_client.h"
7
8 #include "../daemon/common.h"
9
@@ -167,252 +167,6 @@ static int private_decrypt(RSA *p_key, unsigned char * enc_data, int data_len, u
167 return result;
168 }
169
170 -typedef enum http_req_type {
171 - HTTP_REQ_GET,
172 - HTTP_REQ_POST
173 -} http_req_type;
174 -
175 -enum http_parse_state {
176 - HTTP_PARSE_INITIAL = 0,
177 - HTTP_PARSE_HEADERS,
178 - HTTP_PARSE_CONTENT
179 -};
180 -
181 -typedef struct {
182 - enum http_parse_state state;
183 - int content_length;
184 - int http_code;
185 -} http_parse_ctx;
186 -
187 -#define HTTP_PARSE_CTX_INITIALIZER { .state = HTTP_PARSE_INITIAL, .content_length = -1, .http_code = 0 }
188 -
189 -#define NEED_MORE_DATA 0
190 -#define PARSE_SUCCESS 1
191 -#define PARSE_ERROR -1
192 -#define HTTP_LINE_TERM "\x0D\x0A"
193 -#define RESP_PROTO "HTTP/1.1 "
194 -#define HTTP_KEYVAL_SEPARATOR ": "
195 -#define HTTP_HDR_BUFFER_SIZE 256
196 -#define PORT_STR_MAX_BYTES 7
197 -
198 -static void process_http_hdr(http_parse_ctx *parse_ctx, const char *key, const char *val)
199 -{
200 - // currently we care only about content-length
201 - // but in future the way this is written
202 - // it can be extended
203 - if (!strcmp("content-length", key)) {
204 - parse_ctx->content_length = atoi(val);
205 - }
206 -}
207 -
208 -static int parse_http_hdr(rbuf_t buf, http_parse_ctx *parse_ctx)
209 -{
210 - int idx, idx_end;
211 - char buf_key[HTTP_HDR_BUFFER_SIZE];
212 - char buf_val[HTTP_HDR_BUFFER_SIZE];
213 - char *ptr = buf_key;
214 - if (!rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx_end)) {
215 - error("CRLF expected");
216 - return 1;
217 - }
218 -
219 - char *separator = rbuf_find_bytes(buf, HTTP_KEYVAL_SEPARATOR, strlen(HTTP_KEYVAL_SEPARATOR), &idx);
220 - if (!separator) {
221 - error("Missing Key/Value separator");
222 - return 1;
223 - }
224 - if (idx >= HTTP_HDR_BUFFER_SIZE) {
225 - error("Key name is too long");
226 - return 1;
227 - }
228 -
229 - rbuf_pop(buf, buf_key, idx);
230 - buf_key[idx] = 0;
231 -
232 - rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
233 - idx_end -= strlen(HTTP_KEYVAL_SEPARATOR) + idx;
234 - if (idx_end >= HTTP_HDR_BUFFER_SIZE) {
235 - error("Value of key \"%s\" too long", buf_key);
236 - return 1;
237 - }
238 -
239 - rbuf_pop(buf, buf_val, idx_end);
240 - buf_val[idx_end] = 0;
241 -
242 - rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
243 -
244 - for (ptr = buf_key; *ptr; ptr++)
245 - *ptr = tolower(*ptr);
246 -
247 - process_http_hdr(parse_ctx, buf_key, buf_val);
248 -
249 - return 0;
250 -}
251 -
252 -static int parse_http_response(rbuf_t buf, http_parse_ctx *parse_ctx)
253 -{
254 - int idx;
255 - char rc[4];
256 -
257 - do {
258 - if (parse_ctx->state != HTTP_PARSE_CONTENT && !rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx))
259 - return NEED_MORE_DATA;
260 - switch (parse_ctx->state) {
261 - case HTTP_PARSE_INITIAL:
262 - if (rbuf_memcmp_n(buf, RESP_PROTO, strlen(RESP_PROTO))) {
263 - error("Expected response to start with \"%s\"", RESP_PROTO);
264 - return PARSE_ERROR;
265 - }
266 - rbuf_bump_tail(buf, strlen(RESP_PROTO));
267 - if (rbuf_pop(buf, rc, 4) != 4) {
268 - error("Expected HTTP status code");
269 - return PARSE_ERROR;
270 - }
271 - if (rc[3] != ' ') {
272 - error("Expected space after HTTP return code");
273 - return PARSE_ERROR;
274 - }
275 - rc[3] = 0;
276 - parse_ctx->http_code = atoi(rc);
277 - if (parse_ctx->http_code < 100 || parse_ctx->http_code >= 600) {
278 - error("HTTP code not in range 100 to 599");
279 - return PARSE_ERROR;
280 - }
281 -
282 - rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
283 -
284 - rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
285 -
286 - parse_ctx->state = HTTP_PARSE_HEADERS;
287 - break;
288 - case HTTP_PARSE_HEADERS:
289 - if (!idx) {
290 - parse_ctx->state = HTTP_PARSE_CONTENT;
291 - rbuf_bump_tail(buf, strlen(HTTP_LINE_TERM));
292 - break;
293 - }
294 - if (parse_http_hdr(buf, parse_ctx))
295 - return PARSE_ERROR;
296 - rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
297 - rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
298 - break;
299 - case HTTP_PARSE_CONTENT:
300 - if (parse_ctx->content_length < 0) {
301 - error("content-length missing and http headers ended");
302 - return PARSE_ERROR;
303 - }
304 - if (rbuf_bytes_available(buf) >= (size_t)parse_ctx->content_length)
305 - return PARSE_SUCCESS;
306 - return NEED_MORE_DATA;
307 - }
308 - } while(1);
309 -}
310 -
311 -static int https_request(http_req_type method, char *host, int port, char *url, char *b, size_t b_size, char *payload)
312 -{
313 - struct timeval timeout = { .tv_sec = 30, .tv_usec = 0 };
314 - char sport[PORT_STR_MAX_BYTES];
315 - size_t len = 0;
316 - int rc = 1;
317 - int ret;
318 - char *ptr;
319 - http_parse_ctx parse_ctx = HTTP_PARSE_CTX_INITIALIZER;
320 -
321 - rbuf_t buffer = rbuf_create(b_size);
322 - if (!buffer)
323 - return 1;
324 -
325 - snprintf(sport, PORT_STR_MAX_BYTES, "%d", port);
326 -
327 - if (payload != NULL)
328 - len = strlen(payload);
329 -
330 - snprintf(
331 - b,
332 - b_size,
333 - "%s %s HTTP/1.1\r\nHost: %s\r\nAccept: application/json\r\nContent-length: %zu\r\nAccept-Language: en-us\r\n"
334 - "User-Agent: Netdata/rocks\r\n\r\n",
335 - (method == HTTP_REQ_GET ? "GET" : "POST"), url, host, len);
336 -
337 - if (payload != NULL)
338 - strncat(b, payload, b_size - len);
339 -
340 - len = strlen(b);
341 -
342 - debug(D_ACLK, "Sending HTTPS req (%zu bytes): '%s'", len, b);
343 - int sock = connect_to_this_ip46(IPPROTO_TCP, SOCK_STREAM, host, 0, sport, &timeout);
344 -
345 - if (unlikely(sock == -1)) {
346 - error("Handshake failed");
347 - goto exit_buf;
348 - }
349 -
350 - SSL_CTX *ctx = security_initialize_openssl_client();
351 - if (ctx==NULL) {
352 - error("Cannot allocate SSL context");
353 - goto exit_sock;
354 - }
355 - // Certificate chain: not updating the stores - do we need private CA roots?
356 - // Calls to SSL_CTX_load_verify_locations would go here.
357 - SSL *ssl = SSL_new(ctx);
358 - if (ssl==NULL) {
359 - error("Cannot allocate SSL");
360 - goto exit_CTX;
361 - }
362 - SSL_set_fd(ssl, sock);
363 - ret = SSL_connect(ssl);
364 - if (ret != 1) {
365 - error("SSL_connect() failed with err=%d", ret);
366 - goto exit_SSL;
367 - }
368 -
369 - ret = SSL_write(ssl, b, len);
370 - if (ret <= 0)
371 - {
372 - error("SSL_write() failed with err=%d", ret);
373 - goto exit_SSL;
374 - }
375 -
376 - b[0] = 0;
377 -
378 - do {
379 - ptr = rbuf_get_linear_insert_range(buffer, &len);
380 - ret = SSL_read(ssl, ptr, len - 1);
381 - if (ret)
382 - rbuf_bump_head(buffer, ret);
383 - if (ret <= 0)
384 - {
385 - error("No response available - SSL_read()=%d", ret);
386 - goto exit_FULL;
387 - }
388 - } while (!(ret = parse_http_response(buffer, &parse_ctx)));
389 -
390 - if (ret != PARSE_SUCCESS) {
391 - error("Error parsing HTTP response");
392 - goto exit_FULL;
393 - }
394 -
395 - if (parse_ctx.http_code < 200 || parse_ctx.http_code >= 300) {
396 - error("HTTP Response not Success (got %d)", parse_ctx.http_code);
397 - goto exit_FULL;
398 - }
399 -
400 - len = rbuf_pop(buffer, b, b_size);
401 - b[MIN(len, b_size-1)] = 0;
402 -
403 - rc = 0;
404 -exit_FULL:
405 -exit_SSL:
406 - SSL_free(ssl);
407 -exit_CTX:
408 - SSL_CTX_free(ctx);
409 -exit_sock:
410 - close(sock);
411 -exit_buf:
412 - rbuf_free(buffer);
413 - return rc;
414 -}
415 -
170 // aclk_get_mqtt_otp is slightly modified original code from @amoss
171 void aclk_get_mqtt_otp(RSA *p_key, char *aclk_hostname, int port, char **mqtt_usr, char **mqtt_pass)
172 {
aclk/https_client.c new
+246
@@ -0,0 +1,246 @@
1 +#include "libnetdata/libnetdata.h"
2 +
3 +#include "https_client.h"
4 +
5 +#include "../mqtt_websockets/c-rbuf/include/ringbuffer.h"
6 +
7 +enum http_parse_state {
8 + HTTP_PARSE_INITIAL = 0,
9 + HTTP_PARSE_HEADERS,
10 + HTTP_PARSE_CONTENT
11 +};
12 +
13 +typedef struct {
14 + enum http_parse_state state;
15 + int content_length;
16 + int http_code;
17 +} http_parse_ctx;
18 +
19 +#define HTTP_PARSE_CTX_INITIALIZER { .state = HTTP_PARSE_INITIAL, .content_length = -1, .http_code = 0 }
20 +
21 +#define NEED_MORE_DATA 0
22 +#define PARSE_SUCCESS 1
23 +#define PARSE_ERROR -1
24 +#define HTTP_LINE_TERM "\x0D\x0A"
25 +#define RESP_PROTO "HTTP/1.1 "
26 +#define HTTP_KEYVAL_SEPARATOR ": "
27 +#define HTTP_HDR_BUFFER_SIZE 256
28 +#define PORT_STR_MAX_BYTES 7
29 +
30 +static void process_http_hdr(http_parse_ctx *parse_ctx, const char *key, const char *val)
31 +{
32 + // currently we care only about content-length
33 + // but in future the way this is written
34 + // it can be extended
35 + if (!strcmp("content-length", key)) {
36 + parse_ctx->content_length = atoi(val);
37 + }
38 +}
39 +
40 +static int parse_http_hdr(rbuf_t buf, http_parse_ctx *parse_ctx)
41 +{
42 + int idx, idx_end;
43 + char buf_key[HTTP_HDR_BUFFER_SIZE];
44 + char buf_val[HTTP_HDR_BUFFER_SIZE];
45 + char *ptr = buf_key;
46 + if (!rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx_end)) {
47 + error("CRLF expected");
48 + return 1;
49 + }
50 +
51 + char *separator = rbuf_find_bytes(buf, HTTP_KEYVAL_SEPARATOR, strlen(HTTP_KEYVAL_SEPARATOR), &idx);
52 + if (!separator) {
53 + error("Missing Key/Value separator");
54 + return 1;
55 + }
56 + if (idx >= HTTP_HDR_BUFFER_SIZE) {
57 + error("Key name is too long");
58 + return 1;
59 + }
60 +
61 + rbuf_pop(buf, buf_key, idx);
62 + buf_key[idx] = 0;
63 +
64 + rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
65 + idx_end -= strlen(HTTP_KEYVAL_SEPARATOR) + idx;
66 + if (idx_end >= HTTP_HDR_BUFFER_SIZE) {
67 + error("Value of key \"%s\" too long", buf_key);
68 + return 1;
69 + }
70 +
71 + rbuf_pop(buf, buf_val, idx_end);
72 + buf_val[idx_end] = 0;
73 +
74 + rbuf_bump_tail(buf, strlen(HTTP_KEYVAL_SEPARATOR));
75 +
76 + for (ptr = buf_key; *ptr; ptr++)
77 + *ptr = tolower(*ptr);
78 +
79 + process_http_hdr(parse_ctx, buf_key, buf_val);
80 +
81 + return 0;
82 +}
83 +
84 +static int parse_http_response(rbuf_t buf, http_parse_ctx *parse_ctx)
85 +{
86 + int idx;
87 + char rc[4];
88 +
89 + do {
90 + if (parse_ctx->state != HTTP_PARSE_CONTENT && !rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx))
91 + return NEED_MORE_DATA;
92 + switch (parse_ctx->state) {
93 + case HTTP_PARSE_INITIAL:
94 + if (rbuf_memcmp_n(buf, RESP_PROTO, strlen(RESP_PROTO))) {
95 + error("Expected response to start with \"%s\"", RESP_PROTO);
96 + return PARSE_ERROR;
97 + }
98 + rbuf_bump_tail(buf, strlen(RESP_PROTO));
99 + if (rbuf_pop(buf, rc, 4) != 4) {
100 + error("Expected HTTP status code");
101 + return PARSE_ERROR;
102 + }
103 + if (rc[3] != ' ') {
104 + error("Expected space after HTTP return code");
105 + return PARSE_ERROR;
106 + }
107 + rc[3] = 0;
108 + parse_ctx->http_code = atoi(rc);
109 + if (parse_ctx->http_code < 100 || parse_ctx->http_code >= 600) {
110 + error("HTTP code not in range 100 to 599");
111 + return PARSE_ERROR;
112 + }
113 +
114 + rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
115 +
116 + rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
117 +
118 + parse_ctx->state = HTTP_PARSE_HEADERS;
119 + break;
120 + case HTTP_PARSE_HEADERS:
121 + if (!idx) {
122 + parse_ctx->state = HTTP_PARSE_CONTENT;
123 + rbuf_bump_tail(buf, strlen(HTTP_LINE_TERM));
124 + break;
125 + }
126 + if (parse_http_hdr(buf, parse_ctx))
127 + return PARSE_ERROR;
128 + rbuf_find_bytes(buf, HTTP_LINE_TERM, strlen(HTTP_LINE_TERM), &idx);
129 + rbuf_bump_tail(buf, idx + strlen(HTTP_LINE_TERM));
130 + break;
131 + case HTTP_PARSE_CONTENT:
132 + if (parse_ctx->content_length < 0) {
133 + error("content-length missing and http headers ended");
134 + return PARSE_ERROR;
135 + }
136 + if (rbuf_bytes_available(buf) >= (size_t)parse_ctx->content_length)
137 + return PARSE_SUCCESS;
138 + return NEED_MORE_DATA;
139 + }
140 + } while(1);
141 +}
142 +
143 +int https_request(http_req_type_t method, char *host, int port, char *url, char *b, size_t b_size, char *payload)
144 +{
145 + struct timeval timeout = { .tv_sec = 30, .tv_usec = 0 };
146 + char sport[PORT_STR_MAX_BYTES];
147 + size_t len = 0;
148 + int rc = 1;
149 + int ret;
150 + char *ptr;
151 + http_parse_ctx parse_ctx = HTTP_PARSE_CTX_INITIALIZER;
152 +
153 + rbuf_t buffer = rbuf_create(b_size);
154 + if (!buffer)
155 + return 1;
156 +
157 + snprintf(sport, PORT_STR_MAX_BYTES, "%d", port);
158 +
159 + if (payload != NULL)
160 + len = strlen(payload);
161 +
162 + snprintf(
163 + b,
164 + b_size,
165 + "%s %s HTTP/1.1\r\nHost: %s\r\nAccept: application/json\r\nContent-length: %zu\r\nAccept-Language: en-us\r\n"
166 + "User-Agent: Netdata/rocks\r\n\r\n",
167 + (method == HTTP_REQ_GET ? "GET" : "POST"), url, host, len);
168 +
169 + if (payload != NULL)
170 + strncat(b, payload, b_size - len);
171 +
172 + len = strlen(b);
173 +
174 + debug(D_ACLK, "Sending HTTPS req (%zu bytes): '%s'", len, b);
175 + int sock = connect_to_this_ip46(IPPROTO_TCP, SOCK_STREAM, host, 0, sport, &timeout);
176 +
177 + if (unlikely(sock == -1)) {
178 + error("Handshake failed");
179 + goto exit_buf;
180 + }
181 +
182 + SSL_CTX *ctx = security_initialize_openssl_client();
183 + if (ctx==NULL) {
184 + error("Cannot allocate SSL context");
185 + goto exit_sock;
186 + }
187 + // Certificate chain: not updating the stores - do we need private CA roots?
188 + // Calls to SSL_CTX_load_verify_locations would go here.
189 + SSL *ssl = SSL_new(ctx);
190 + if (ssl==NULL) {
191 + error("Cannot allocate SSL");
192 + goto exit_CTX;
193 + }
194 + SSL_set_fd(ssl, sock);
195 + ret = SSL_connect(ssl);
196 + if (ret != 1) {
197 + error("SSL_connect() failed with err=%d", ret);
198 + goto exit_SSL;
199 + }
200 +
201 + ret = SSL_write(ssl, b, len);
202 + if (ret <= 0)
203 + {
204 + error("SSL_write() failed with err=%d", ret);
205 + goto exit_SSL;
206 + }
207 +
208 + b[0] = 0;
209 +
210 + do {
211 + ptr = rbuf_get_linear_insert_range(buffer, &len);
212 + ret = SSL_read(ssl, ptr, len - 1);
213 + if (ret)
214 + rbuf_bump_head(buffer, ret);
215 + if (ret <= 0)
216 + {
217 + error("No response available - SSL_read()=%d", ret);
218 + goto exit_FULL;
219 + }
220 + } while (!(ret = parse_http_response(buffer, &parse_ctx)));
221 +
222 + if (ret != PARSE_SUCCESS) {
223 + error("Error parsing HTTP response");
224 + goto exit_FULL;
225 + }
226 +
227 + if (parse_ctx.http_code < 200 || parse_ctx.http_code >= 300) {
228 + error("HTTP Response not Success (got %d)", parse_ctx.http_code);
229 + goto exit_FULL;
230 + }
231 +
232 + len = rbuf_pop(buffer, b, b_size);
233 + b[MIN(len, b_size-1)] = 0;
234 +
235 + rc = 0;
236 +exit_FULL:
237 +exit_SSL:
238 + SSL_free(ssl);
239 +exit_CTX:
240 + SSL_CTX_free(ctx);
241 +exit_sock:
242 + close(sock);
243 +exit_buf:
244 + rbuf_free(buffer);
245 + return rc;
246 +}
aclk/https_client.h new
+11
@@ -0,0 +1,11 @@
1 +#ifndef NETDATA_HTTPS_CLIENT_H
2 +#define NETDATA_HTTPS_CLIENT_H
3 +
4 +typedef enum http_req_type {
5 + HTTP_REQ_GET,
6 + HTTP_REQ_POST
7 +} http_req_type_t;
8 +
9 +int https_request(http_req_type_t method, char *host, int port, char *url, char *b, size_t b_size, char *payload);
10 +
11 +#endif /* NETDATA_HTTPS_CLIENT_H */