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 */