master
c 696 lines 27.5 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "libnetdata/libnetdata.h"
4
5 #include "ws_client.h"
6 #include "common_internal.h"
7 #include "aclk_mqtt_workers.h"
8
9 const char *websocket_upgrage_hdr = "GET /mqtt HTTP/1.1\x0D\x0A"
10 "Host: %s\x0D\x0A"
11 "Upgrade: websocket\x0D\x0A"
12 "Connection: Upgrade\x0D\x0A"
13 "Sec-WebSocket-Key: %s\x0D\x0A"
14 "Origin: \x0D\x0A"
15 "Sec-WebSocket-Protocol: mqtt\x0D\x0A"
16 "Sec-WebSocket-Version: 13\x0D\x0A\x0D\x0A";
17
18 const char *mqtt_protoid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
19
20 #define DEFAULT_RINGBUFFER_SIZE (1024*128)
21
22 ws_client *ws_client_new(size_t buf_size, char **host)
23 {
24 if(!host)
25 return NULL;
26
27 ws_client *client = callocz(1, sizeof(ws_client));
28 client->host = host;
29 client->buf_read = rbuf_create(buf_size ? buf_size : DEFAULT_RINGBUFFER_SIZE);
30 client->buf_write = rbuf_create(buf_size ? buf_size : DEFAULT_RINGBUFFER_SIZE);
31 client->buf_to_mqtt = rbuf_create(buf_size ? buf_size : DEFAULT_RINGBUFFER_SIZE);
32
33 return client;
34 }
35
36 void ws_client_free_headers(ws_client *client)
37 {
38 struct http_header *ptr = client->hs.headers;
39
40 while (ptr) {
41 struct http_header *tmp = ptr;
42 ptr = ptr->next;
43 freez(tmp);
44 }
45
46 client->hs.headers = NULL;
47 client->hs.headers_tail = NULL;
48 client->hs.hdr_count = 0;
49 }
50
51 void ws_client_destroy(ws_client *client)
52 {
53 ws_client_free_headers(client);
54 freez(client->hs.nonce_reply);
55 freez(client->hs.http_reply_msg);
56 rbuf_free(client->buf_read);
57 rbuf_free(client->buf_write);
58 rbuf_free(client->buf_to_mqtt);
59 freez(client);
60 }
61
62 void ws_client_reset(ws_client *client)
63 {
64 ws_client_free_headers(client);
65 freez(client->hs.nonce_reply);
66 client->hs.nonce_reply = NULL;
67
68 freez(client->hs.http_reply_msg);
69 client->hs.http_reply_msg = NULL;
70
71 rbuf_flush(client->buf_read);
72 rbuf_flush(client->buf_write);
73 rbuf_flush(client->buf_to_mqtt);
74
75 client->state = WS_RAW;
76 client->hs.hdr_state = WS_HDR_HTTP;
77 client->rx.parse_state = WS_FIRST_2BYTES;
78 client->rx.remote_closed = false;
79 }
80
81 #define MAX_HTTP_HDR_COUNT 128
82 int ws_client_add_http_header(ws_client *client, struct http_header *hdr)
83 {
84 if (client->hs.hdr_count > MAX_HTTP_HDR_COUNT) {
85 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Too many HTTP response header fields");
86 return -1;
87 }
88
89 if (client->hs.headers)
90 client->hs.headers_tail->next = hdr;
91 else
92 client->hs.headers = hdr;
93
94 client->hs.headers_tail = hdr;
95 client->hs.hdr_count++;
96
97 return 0;
98 }
99
100 int ws_client_want_write(const ws_client *client)
101 {
102 return rbuf_bytes_available(client->buf_write);
103 }
104
105 #define WEBSOCKET_NONCE_SIZE 16
106 #define TEMP_BUF_SIZE 4096
107 int ws_client_start_handshake(ws_client *client)
108 {
109 unsigned char nonce[WEBSOCKET_NONCE_SIZE];
110 char nonce_b64[256];
111 char second[TEMP_BUF_SIZE];
112 unsigned int md_len;
113 unsigned char digest[EVP_MAX_MD_SIZE]; // EVP_MAX_MD_SIZE ensures enough space
114 EVP_MD_CTX *md_ctx;
115 const EVP_MD *md;
116 int rc = 1;
117
118 client->rx.remote_closed = false;
119
120 if(!client->host || !*client->host) {
121 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Hostname has not been set. We should not be able to come here!");
122 return 1;
123 }
124
125 // Generate a random 16-byte nonce
126 os_random_bytes(nonce, sizeof(nonce));
127
128 // Initialize the digest context
129 #if (OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110)
130 md_ctx = EVP_MD_CTX_create();
131 #else
132 md_ctx = EVP_MD_CTX_new();
133 #endif
134 if (md_ctx == NULL) {
135 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Can't create EVP_MD context");
136 return 1;
137 }
138
139 md = EVP_sha1(); // Use SHA-1 for WebSocket handshake
140 if (!md) {
141 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Unknown message digest SHA-1");
142 goto exit_with_error;
143 }
144
145 (void) netdata_base64_encode((unsigned char *) nonce_b64, nonce, WEBSOCKET_NONCE_SIZE);
146
147 // Format and push the upgrade header to the write buffer
148 size_t bytes = snprintf(second, TEMP_BUF_SIZE, websocket_upgrage_hdr, *client->host, nonce_b64);
149 if(rbuf_bytes_free(client->buf_write) < bytes) {
150 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Write buffer capacity too low.");
151 goto exit_with_error;
152 }
153 rbuf_push(client->buf_write, second, bytes);
154
155 client->state = WS_HANDSHAKE;
156
157 // Create the expected Sec-WebSocket-Accept value
158 bytes = snprintf(second, TEMP_BUF_SIZE, "%s%s", nonce_b64, mqtt_protoid);
159
160 if (!EVP_DigestInit_ex(md_ctx, md, NULL)) {
161 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Failed to initialize digest context");
162 goto exit_with_error;
163 }
164
165 if (!EVP_DigestUpdate(md_ctx, second, bytes)) {
166 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Failed to update digest");
167 goto exit_with_error;
168 }
169
170 if (!EVP_DigestFinal_ex(md_ctx, digest, &md_len)) {
171 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Failed to finalize digest");
172 goto exit_with_error;
173 }
174
175 (void) netdata_base64_encode((unsigned char *) nonce_b64, digest, md_len);
176
177 freez(client->hs.nonce_reply);
178 client->hs.nonce_reply = strdupz(nonce_b64);
179 rc = 0;
180
181 exit_with_error:
182 #if (OPENSSL_VERSION_NUMBER < OPENSSL_VERSION_110)
183 EVP_MD_CTX_destroy(md_ctx);
184 #else
185 EVP_MD_CTX_free(md_ctx);
186 #endif
187
188 return rc;
189 }
190
191 #define BUF_READ_MEMCMP_CONST(const, err) \
192 if (rbuf_memcmp_n(client->buf_read, const, strlen(const))) { \
193 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: %s", err); \
194 rbuf_flush(client->buf_read); \
195 return WS_CLIENT_PROTOCOL_ERROR; \
196 }
197
198 #define BUF_READ_CHECK_AT_LEAST(x) \
199 if (rbuf_bytes_available(client->buf_read) < x) \
200 return WS_CLIENT_NEED_MORE_BYTES;
201
202 #define MAX_HTTP_LINE_LENGTH (1024 * 4)
203 #define HTTP_SC_LENGTH 4 // "XXX " http status code as C string
204 #define WS_CLIENT_HTTP_HDR "HTTP/1.1 "
205 #define WS_CONN_ACCEPT "sec-websocket-accept"
206 #define HTTP_HDR_SEPARATOR ": "
207 #define WS_NONCE_STRLEN_B64 28
208 #define WS_HTTP_NEWLINE "\r\n"
209 #define HTTP_HEADER_NAME_MAX_LEN 256
210 #if HTTP_HEADER_NAME_MAX_LEN > MAX_HTTP_LINE_LENGTH
211 #error "Buffer too small"
212 #endif
213 #if WS_NONCE_STRLEN_B64 > MAX_HTTP_LINE_LENGTH
214 #error "Buffer too small"
215 #endif
216
217 #define HTTP_HDR_LINE_CHECK_LIMIT(x) \
218 if ((x) >= MAX_HTTP_LINE_LENGTH) { \
219 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: HTTP line received is too long. Maximum is %d", MAX_HTTP_LINE_LENGTH); \
220 return WS_CLIENT_PROTOCOL_ERROR; \
221 }
222
223 int ws_client_parse_handshake_resp(ws_client *client)
224 {
225 char buf[HTTP_SC_LENGTH];
226 int idx_crlf, idx_sep;
227 char *ptr;
228 size_t bytes;
229
230 switch (client->hs.hdr_state) {
231 case WS_HDR_HTTP:
232 BUF_READ_CHECK_AT_LEAST(strlen(WS_CLIENT_HTTP_HDR))
233 BUF_READ_MEMCMP_CONST(WS_CLIENT_HTTP_HDR, "Expected \"HTTP1.1\" header");
234 rbuf_bump_tail(client->buf_read, strlen(WS_CLIENT_HTTP_HDR));
235 client->hs.hdr_state = WS_HDR_RC;
236 break;
237
238 case WS_HDR_RC:
239 BUF_READ_CHECK_AT_LEAST(HTTP_SC_LENGTH); // "XXX " http return code
240 rbuf_pop(client->buf_read, buf, HTTP_SC_LENGTH);
241 if (buf[HTTP_SC_LENGTH - 1] != 0x20) {
242 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: HTTP status code received is not terminated by space (0x20)");
243 return WS_CLIENT_PROTOCOL_ERROR;
244 }
245 buf[HTTP_SC_LENGTH - 1] = 0;
246 client->hs.http_code = atoi(buf);
247 if (client->hs.http_code < 100 || client->hs.http_code >= 600) {
248 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: HTTP status code received not in valid range 100-600");
249 return WS_CLIENT_PROTOCOL_ERROR;
250 }
251 client->hs.hdr_state = WS_HDR_ENDLINE;
252 break;
253
254 case WS_HDR_ENDLINE:
255 ptr = rbuf_find_bytes(client->buf_read, WS_HTTP_NEWLINE, strlen(WS_HTTP_NEWLINE), &idx_crlf);
256 if (!ptr) {
257 bytes = rbuf_bytes_available(client->buf_read);
258 HTTP_HDR_LINE_CHECK_LIMIT(bytes);
259 return WS_CLIENT_NEED_MORE_BYTES;
260 }
261 HTTP_HDR_LINE_CHECK_LIMIT(idx_crlf);
262
263 client->hs.http_reply_msg = mallocz(idx_crlf+1);
264 rbuf_pop(client->buf_read, client->hs.http_reply_msg, idx_crlf);
265 client->hs.http_reply_msg[idx_crlf] = 0;
266 rbuf_bump_tail(client->buf_read, strlen(WS_HTTP_NEWLINE));
267 client->hs.hdr_state = WS_HDR_PARSE_HEADERS;
268 break;
269
270 case WS_HDR_PARSE_HEADERS:
271 ptr = rbuf_find_bytes(client->buf_read, WS_HTTP_NEWLINE, strlen(WS_HTTP_NEWLINE), &idx_crlf);
272 if (!ptr) {
273 bytes = rbuf_bytes_available(client->buf_read);
274 HTTP_HDR_LINE_CHECK_LIMIT(bytes);
275 return WS_CLIENT_NEED_MORE_BYTES;
276 }
277 HTTP_HDR_LINE_CHECK_LIMIT(idx_crlf);
278
279 if (!idx_crlf) { // empty line, header end
280 rbuf_bump_tail(client->buf_read, strlen(WS_HTTP_NEWLINE));
281 client->hs.hdr_state = WS_HDR_PARSE_DONE;
282 return 0;
283 }
284
285 ptr = rbuf_find_bytes(client->buf_read, HTTP_HDR_SEPARATOR, strlen(HTTP_HDR_SEPARATOR), &idx_sep);
286 if (!ptr || idx_sep > idx_crlf) {
287 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Expected HTTP hdr field key/value separator \": \" before endline in non empty HTTP header line");
288 return WS_CLIENT_PROTOCOL_ERROR;
289 }
290 if (idx_crlf == idx_sep + (int)strlen(HTTP_HDR_SEPARATOR)) {
291 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: HTTP Header value cannot be empty");
292 return WS_CLIENT_PROTOCOL_ERROR;
293 }
294
295 if (idx_sep > HTTP_HEADER_NAME_MAX_LEN) {
296 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: HTTP header too long (%d)", idx_sep);
297 return WS_CLIENT_PROTOCOL_ERROR;
298 }
299
300 struct http_header *hdr = callocz(1, sizeof(struct http_header) + idx_crlf); //idx_crlf includes ": " that will be used as 2 \0 bytes
301 hdr->key = ((char*)hdr) + sizeof(struct http_header);
302 hdr->value = hdr->key + idx_sep + 1;
303
304 rbuf_pop(client->buf_read, hdr->key, idx_sep);
305 rbuf_bump_tail(client->buf_read, strlen(HTTP_HDR_SEPARATOR));
306
307 rbuf_pop(client->buf_read, hdr->value, idx_crlf - idx_sep - strlen(HTTP_HDR_SEPARATOR));
308 rbuf_bump_tail(client->buf_read, strlen(WS_HTTP_NEWLINE));
309
310 for (int i = 0; hdr->key[i]; i++)
311 hdr->key[i] = tolower(hdr->key[i]);
312
313 if (ws_client_add_http_header(client, hdr))
314 return WS_CLIENT_PROTOCOL_ERROR;
315
316 if (!strcmp(hdr->key, WS_CONN_ACCEPT)) {
317 if (strcmp(client->hs.nonce_reply, hdr->value)) {
318 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Received NONCE \"%s\" does not match expected nonce of \"%s\"", hdr->value, client->hs.nonce_reply);
319 return WS_CLIENT_PROTOCOL_ERROR;
320 }
321 client->hs.nonce_matched = 1;
322 }
323
324 break;
325
326 case WS_HDR_PARSE_DONE:
327 if (!client->hs.nonce_matched) {
328 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Missing " WS_CONN_ACCEPT " header");
329 return WS_CLIENT_PROTOCOL_ERROR;
330 }
331 if (client->hs.http_code != 101) {
332 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: HTTP return code not 101. Received %d with msg \"%s\".", client->hs.http_code, client->hs.http_reply_msg);
333 return WS_CLIENT_PROTOCOL_ERROR;
334 }
335
336 client->state = WS_ESTABLISHED;
337 client->hs.hdr_state = WS_HDR_ALL_DONE;
338 nd_log(NDLS_DAEMON, NDLP_INFO, "ACLK: Websocket Connection Accepted By Server");
339 return WS_CLIENT_PARSING_DONE;
340
341 case WS_HDR_ALL_DONE:
342 nd_log(NDLS_DAEMON, NDLP_CRIT, "ACLK: This is error we should never come here!");
343 return WS_CLIENT_PROTOCOL_ERROR;
344 }
345 return 0;
346 }
347
348 #define BYTE_MSB 0x80
349 #define WS_FINAL_FRAG BYTE_MSB
350 #define WS_PAYLOAD_MASKED BYTE_MSB
351
352 static size_t get_ws_hdr_size(size_t payload_size)
353 {
354 size_t hdr_len = 2 + 4 /*mask*/;
355 if(payload_size > 125)
356 hdr_len += 2;
357 if(payload_size > 65535)
358 hdr_len += 6;
359 return hdr_len;
360 }
361
362 #define MAX_POSSIBLE_HDR_LEN 14
363 int ws_client_send(const ws_client *client, enum websocket_opcode frame_type, const char *data, size_t size)
364 {
365 // TODO maybe? implement fragmenting, it is not necessary though
366 // as both tested MQTT brokers have no reuirement of one MQTT envelope
367 // be equal to one WebSockets envelope. Therefore there is no need to send
368 // one big MQTT message as single fragmented WebSocket envelope
369 char hdr[MAX_POSSIBLE_HDR_LEN];
370 char *ptr = hdr;
371 int size_written = 0;
372 size_t j = 0;
373
374 size_t w_buff_free = rbuf_bytes_free(client->buf_write);
375 size_t hdr_len = get_ws_hdr_size(size);
376
377 if (w_buff_free < hdr_len * 2)
378 return 0;
379
380 if (w_buff_free < (hdr_len + size)) {
381 size = w_buff_free - hdr_len;
382 hdr_len = get_ws_hdr_size(size);
383 // the actual needed header size might decrease if we cut number of bytes
384 // if decrease of size crosses 65535 or 125 boundary
385 // but I can live with that at least for now
386 // worst case is we have 6 more bytes we could have written
387 // no bigus dealus
388 }
389
390 *ptr++ = frame_type | WS_FINAL_FRAG;
391
392 //generate length
393 *ptr = WS_PAYLOAD_MASKED;
394 if (size > 65535) {
395 *ptr++ |= 0x7f;
396 uint64_t be = htobe64(size);
397 memcpy(ptr, (void *)&be, sizeof(be));
398 ptr += sizeof(be);
399 } else if (size > 125) {
400 *ptr++ |= 0x7e;
401 uint16_t be = htobe16(size);
402 memcpy(ptr, (void *)&be, sizeof(be));
403 ptr += sizeof(be);
404 } else
405 *ptr++ |= size;
406
407 char *mask = ptr;
408 uint32_t mask32 = os_random32() + 1;
409 memcpy(mask, &mask32, sizeof(mask32));
410
411 rbuf_push(client->buf_write, hdr, hdr_len);
412
413 if (!size)
414 return 0;
415
416 // copy and mask data in the write ringbuffer
417 while (size - size_written) {
418 size_t writable_bytes;
419 char *w_ptr = rbuf_get_linear_insert_range(client->buf_write, &writable_bytes);
420 if(!writable_bytes)
421 break;
422
423 writable_bytes = (writable_bytes > size) ? (size - size_written) : writable_bytes;
424
425 memcpy(w_ptr, &data[size_written], writable_bytes);
426 rbuf_bump_head(client->buf_write, writable_bytes);
427
428 for (size_t i = 0; i < writable_bytes; i++, j++)
429 w_ptr[i] ^= mask[j % 4];
430 size_written += writable_bytes;
431 }
432 return size_written;
433 }
434
435 static int check_opcode(enum websocket_opcode oc)
436 {
437 switch(oc) {
438 case WS_OP_BINARY_FRAME:
439 case WS_OP_CONNECTION_CLOSE:
440 case WS_OP_PING:
441 return 0;
442 case WS_OP_CONTINUATION_FRAME:
443 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: WS_OP_CONTINUATION_FRAME NOT IMPLEMENTED YET!!!!");
444 return 0;
445 case WS_OP_TEXT_FRAME:
446 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: WS_OP_TEXT_FRAME NOT IMPLEMENTED YET!!!!");
447 return 0;
448 case WS_OP_PONG:
449 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: WS_OP_PONG NOT IMPLEMENTED YET!!!!");
450 return 0;
451 default:
452 return WS_CLIENT_PROTOCOL_ERROR;
453 }
454 }
455
456 static void ws_client_rx_post_hdr_state(ws_client *client)
457 {
458 switch(client->rx.opcode) {
459 case WS_OP_BINARY_FRAME:
460 client->rx.parse_state = WS_PAYLOAD_DATA;
461 break;
462 case WS_OP_CONNECTION_CLOSE:
463 client->rx.parse_state = WS_PAYLOAD_CONNECTION_CLOSE;
464 break;
465 case WS_OP_PING:
466 client->rx.parse_state = WS_PAYLOAD_PING_REQ_PAYLOAD;
467 break;
468 default:
469 client->rx.parse_state = WS_PAYLOAD_SKIP_UNKNOWN_PAYLOAD;
470 break;
471 }
472 }
473
474 #define LONGEST_POSSIBLE_HDR_PART 8
475 int ws_client_process_rx_ws(ws_client *client)
476 {
477 char buf[LONGEST_POSSIBLE_HDR_PART];
478 size_t size;
479 switch (client->rx.parse_state) {
480 case WS_FIRST_2BYTES:
481 BUF_READ_CHECK_AT_LEAST(2);
482 rbuf_pop(client->buf_read, buf, 2);
483 client->rx.opcode = buf[0] & (char)~BYTE_MSB;
484
485 if (!(buf[0] & (char)~WS_FINAL_FRAG)) {
486 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Not supporting fragmented messages yet!");
487 return WS_CLIENT_PROTOCOL_ERROR;
488 }
489
490 if (check_opcode(client->rx.opcode) == WS_CLIENT_PROTOCOL_ERROR)
491 return WS_CLIENT_PROTOCOL_ERROR;
492
493 if (buf[1] & (char)WS_PAYLOAD_MASKED) {
494 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Mask is not allowed in Server->Client Websocket direction.");
495 return WS_CLIENT_PROTOCOL_ERROR;
496 }
497
498 switch (buf[1]) {
499 case 127:
500 client->rx.parse_state = WS_PAYLOAD_EXTENDED_64;
501 break;
502 case 126:
503 client->rx.parse_state = WS_PAYLOAD_EXTENDED_16;
504 break;
505 default:
506 client->rx.payload_length = buf[1];
507 ws_client_rx_post_hdr_state(client);
508 }
509 break;
510 case WS_PAYLOAD_EXTENDED_16:
511 BUF_READ_CHECK_AT_LEAST(2);
512 rbuf_pop(client->buf_read, buf, 2);
513 client->rx.payload_length = be16toh(*((uint16_t *)buf));
514 ws_client_rx_post_hdr_state(client);
515 break;
516 case WS_PAYLOAD_EXTENDED_64:
517 BUF_READ_CHECK_AT_LEAST(LONGEST_POSSIBLE_HDR_PART);
518 rbuf_pop(client->buf_read, buf, LONGEST_POSSIBLE_HDR_PART);
519 client->rx.payload_length = be64toh(*((uint64_t *)buf));
520 ws_client_rx_post_hdr_state(client);
521 break;
522 case WS_PAYLOAD_DATA:
523 // TODO not pretty?
524 while (client->rx.payload_processed < client->rx.payload_length) {
525 size_t remaining = client->rx.payload_length - client->rx.payload_processed;
526 if (!rbuf_bytes_available(client->buf_read))
527 return WS_CLIENT_NEED_MORE_BYTES;
528 char *insert = rbuf_get_linear_insert_range(client->buf_to_mqtt, &size);
529 if (!insert) {
530 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: WebSocket buffer full! Cannot process payload of %"PRIu64" bytes (processed %"PRIu64"/%"PRIu64"). Buffer capacity: %zu bytes",
531 remaining, client->rx.payload_processed, client->rx.payload_length, rbuf_get_capacity(client->buf_to_mqtt));
532 return WS_CLIENT_BUFFER_FULL;
533 }
534 size = (size > remaining) ? remaining : size;
535 size = rbuf_pop(client->buf_read, insert, size);
536 rbuf_bump_head(client->buf_to_mqtt, size);
537 client->rx.payload_processed += size;
538 }
539 client->rx.parse_state = WS_PACKET_DONE;
540 break;
541 case WS_PAYLOAD_CONNECTION_CLOSE:
542 // for WS_OP_CONNECTION_CLOSE allowed is
543 // a) empty payload
544 // b) 2byte reason code
545 // c) 2byte reason code followed by message
546 if (client->rx.payload_length == 1) {
547 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: WebScoket CONNECTION_CLOSE can't have payload of size 1");
548 return WS_CLIENT_PROTOCOL_ERROR;
549 }
550 client->rx.remote_closed = true;
551 if (!client->rx.payload_length) {
552 nd_log(NDLS_DAEMON, NDLP_INFO, "ACLK: WebSocket server closed the connection without giving reason.");
553 client->rx.parse_state = WS_PACKET_DONE;
554 break;
555 }
556 client->rx.parse_state = WS_PAYLOAD_CONNECTION_CLOSE_EC;
557 break;
558 case WS_PAYLOAD_CONNECTION_CLOSE_EC:
559 BUF_READ_CHECK_AT_LEAST(sizeof(uint16_t));
560
561 rbuf_pop(client->buf_read, buf, sizeof(uint16_t));
562 client->rx.specific_data.op_close.ec = be16toh(*((uint16_t *)buf));
563 client->rx.payload_processed += sizeof(uint16_t);
564
565 client->rx.remote_closed = true;
566 if(client->rx.payload_processed == client->rx.payload_length) {
567 nd_log(NDLS_DAEMON, NDLP_INFO, "ACLK: WebSocket server closed the connection with EC=%d. Without message.",
568 client->rx.specific_data.op_close.ec);
569 client->rx.parse_state = WS_PACKET_DONE;
570 break;
571 }
572 client->rx.parse_state = WS_PAYLOAD_CONNECTION_CLOSE_MSG;
573 break;
574 case WS_PAYLOAD_CONNECTION_CLOSE_MSG:
575 if (!client->rx.specific_data.op_close.reason)
576 client->rx.specific_data.op_close.reason = mallocz(client->rx.payload_length + 1);
577
578 while (client->rx.payload_processed < client->rx.payload_length) {
579 if (!rbuf_bytes_available(client->buf_read))
580 return WS_CLIENT_NEED_MORE_BYTES;
581 client->rx.payload_processed += rbuf_pop(client->buf_read,
582 &client->rx.specific_data.op_close.reason[client->rx.payload_processed - sizeof(uint16_t)],
583 client->rx.payload_length - client->rx.payload_processed);
584 }
585 client->rx.specific_data.op_close.reason[client->rx.payload_length] = 0;
586 nd_log(NDLS_DAEMON, NDLP_INFO, "ACLK: WebSocket server closed the connection with EC=%d and reason \"%s\"",
587 client->rx.specific_data.op_close.ec,
588 client->rx.specific_data.op_close.reason);
589 freez(client->rx.specific_data.op_close.reason);
590 client->rx.remote_closed = true;
591 client->rx.specific_data.op_close.reason = NULL;
592 client->rx.parse_state = WS_PACKET_DONE;
593 break;
594 case WS_PAYLOAD_SKIP_UNKNOWN_PAYLOAD:
595 BUF_READ_CHECK_AT_LEAST(client->rx.payload_length);
596 nd_log(NDLS_DAEMON, NDLP_WARNING, "ACLK: Skipping Websocket Packet of unsupported/unknown type");
597 if (client->rx.payload_length)
598 rbuf_bump_tail(client->buf_read, client->rx.payload_length);
599 client->rx.parse_state = WS_PACKET_DONE;
600 return WS_CLIENT_PARSING_DONE;
601 case WS_PAYLOAD_PING_REQ_PAYLOAD:
602 if (client->rx.payload_length > rbuf_get_capacity(client->buf_read) / 2) {
603 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Ping payload too big! Received %"PRIu64" bytes, maximum allowed %zu bytes (buffer capacity: %zu bytes)",
604 client->rx.payload_length, rbuf_get_capacity(client->buf_read) / 2, rbuf_get_capacity(client->buf_read));
605 return WS_CLIENT_INTERNAL_ERROR;
606 }
607 BUF_READ_CHECK_AT_LEAST(client->rx.payload_length);
608 client->rx.specific_data.ping_msg = mallocz(client->rx.payload_length);
609 rbuf_pop(client->buf_read, client->rx.specific_data.ping_msg, client->rx.payload_length);
610 // TODO schedule this instead of sending right away
611 // then attempt to send as soon as buffer space clears up
612 size = ws_client_send(client, WS_OP_PONG, client->rx.specific_data.ping_msg, client->rx.payload_length);
613 if (size != client->rx.payload_length) {
614 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Unable to send the PONG as one packet back. Closing connection.");
615 return WS_CLIENT_PROTOCOL_ERROR;
616 }
617 client->rx.parse_state = WS_PACKET_DONE;
618 return WS_CLIENT_PARSING_DONE;
619 case WS_PACKET_DONE:
620 client->rx.parse_state = WS_FIRST_2BYTES;
621 client->rx.payload_processed = 0;
622 if (client->rx.opcode == WS_OP_CONNECTION_CLOSE) {
623 if(client->rx.remote_closed)
624 return WS_CLIENT_CONNECTION_REMOTE_CLOSED;
625 else
626 return WS_CLIENT_CONNECTION_CLOSED;
627 }
628 return WS_CLIENT_PARSING_DONE;
629 default:
630 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Unknown parse state");
631 return WS_CLIENT_INTERNAL_ERROR;
632 }
633 return 0;
634 }
635
636 int ws_client_process(ws_client *client)
637 {
638 int ret;
639 switch(client->state) {
640 case WS_RAW:
641 worker_is_busy(WORKER_ACLK_PROCESS_RAW);
642 if (ws_client_start_handshake(client))
643 return WS_CLIENT_INTERNAL_ERROR;
644 return WS_CLIENT_NEED_MORE_BYTES;
645 case WS_HANDSHAKE:
646 worker_is_busy(WORKER_ACLK_PROCESS_HANDSHAKE);
647 do {
648 ret = ws_client_parse_handshake_resp(client);
649 if (ret == WS_CLIENT_PROTOCOL_ERROR)
650 client->state = WS_ERROR;
651 if (ret == WS_CLIENT_PARSING_DONE && client->state == WS_ESTABLISHED)
652 ret = WS_CLIENT_NEED_MORE_BYTES;
653 } while (!ret);
654 break;
655 case WS_ESTABLISHED:
656 worker_is_busy(WORKER_ACLK_PROCESS_ESTABLISHED);
657 do {
658 ret = ws_client_process_rx_ws(client);
659 switch(ret) {
660 case WS_CLIENT_PROTOCOL_ERROR:
661 client->state = WS_ERROR;
662 break;
663 case WS_CLIENT_CONNECTION_REMOTE_CLOSED:
664 client->state = WS_CONN_CLOSED_GRACEFUL_BY_REMOTE;
665 break;
666 case WS_CLIENT_CONNECTION_CLOSED:
667 client->state = WS_CONN_CLOSED_GRACEFUL;
668 break;
669 default:
670 break;
671 }
672 // if ret == 0 we can continue parsing
673 // if ret == WS_CLIENT_PARSING_DONE we processed
674 // one websocket packet and attempt processing
675 // next one if data available in the buffer
676 } while (!ret || ret == WS_CLIENT_PARSING_DONE);
677 break;
678 case WS_ERROR:
679 worker_is_busy(WORKER_ACLK_PROCESS_ERROR);
680 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: ws_client is in error state. Restart the connection!");
681 return WS_CLIENT_PROTOCOL_ERROR;
682 case WS_CONN_CLOSED_GRACEFUL:
683 worker_is_busy(WORKER_ACLK_PROCESS_CLOSED_GRACEFULLY);
684 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Connection has been gracefully closed.");
685 return WS_CLIENT_CONNECTION_CLOSED;
686 case WS_CONN_CLOSED_GRACEFUL_BY_REMOTE:
687 worker_is_busy(WORKER_ACLK_PROCESS_CLOSED_GRACEFULLY);
688 nd_log(NDLS_DAEMON, NDLP_ERR, "ACLK: Connection has been gracefully closed by remote end.");
689 return WS_CLIENT_CONNECTION_REMOTE_CLOSED;
690 default:
691 worker_is_busy(WORKER_ACLK_PROCESS_UNKNOWN);
692 nd_log(NDLS_DAEMON, NDLP_CRIT, "ACLK: Unknown connection state! Probably memory corruption.");
693 return WS_CLIENT_INTERNAL_ERROR;
694 }
695 return ret;
696 }