1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
// SPDX-License-Identifier: GPL-3.0-only
// Copyright (C) 2020 Timotej Šiškovič
#ifndef WS_CLIENT_H
#define WS_CLIENT_H
#include "c-rbuf/cringbuffer.h"
#include "mqtt_wss_log.h"
#include <stdint.h>
#define WS_CLIENT_NEED_MORE_BYTES 0x10
#define WS_CLIENT_PARSING_DONE 0x11
#define WS_CLIENT_CONNECTION_CLOSED 0x12
#define WS_CLIENT_PROTOCOL_ERROR -0x10
#define WS_CLIENT_BUFFER_FULL -0x11
#define WS_CLIENT_INTERNAL_ERROR -0x12
enum websocket_client_conn_state {
WS_RAW = 0,
WS_HANDSHAKE,
WS_ESTABLISHED,
WS_ERROR, // connection has to be restarted if this is reached
WS_CONN_CLOSED_GRACEFUL
};
enum websocket_client_hdr_parse_state {
WS_HDR_HTTP = 0, // need to check HTTP/1.1
WS_HDR_RC, // need to read HTTP code
WS_HDR_ENDLINE, // need to read rest of the first line
WS_HDR_PARSE_HEADERS, // rest of the header until CRLF CRLF
WS_HDR_PARSE_DONE,
WS_HDR_ALL_DONE
};
enum websocket_client_rx_ws_parse_state {
WS_FIRST_2BYTES = 0,
WS_PAYLOAD_EXTENDED_16,
WS_PAYLOAD_EXTENDED_64,
WS_PAYLOAD_DATA, // BINARY payload to be passed to MQTT
WS_PAYLOAD_CONNECTION_CLOSE,
WS_PAYLOAD_CONNECTION_CLOSE_EC,
WS_PAYLOAD_CONNECTION_CLOSE_MSG,
WS_PAYLOAD_SKIP_UNKNOWN_PAYLOAD,
WS_PAYLOAD_PING_REQ_PAYLOAD, // PING payload to be sent back as PONG
WS_PACKET_DONE
};
enum websocket_opcode {
WS_OP_CONTINUATION_FRAME = 0x0,
WS_OP_TEXT_FRAME = 0x1,
WS_OP_BINARY_FRAME = 0x2,
WS_OP_CONNECTION_CLOSE = 0x8,
WS_OP_PING = 0x9,
WS_OP_PONG = 0xA
};
struct ws_op_close_payload {
uint16_t ec;
char *reason;
};
struct http_header {
char *key;
char *value;
struct http_header *next;
};
typedef struct websocket_client {
enum websocket_client_conn_state state;
struct ws_handshake {
enum websocket_client_hdr_parse_state hdr_state;
char *nonce_reply;
int nonce_matched;
int http_code;
char *http_reply_msg;
struct http_header *headers;
struct http_header *headers_tail;
int hdr_count;
} hs;
struct ws_rx {
enum websocket_client_rx_ws_parse_state parse_state;
enum websocket_opcode opcode;
uint64_t payload_length;
uint64_t payload_processed;
union {
struct ws_op_close_payload op_close;
char *ping_msg;
} specific_data;
} rx;
rbuf_t buf_read; // from SSL
rbuf_t buf_write; // to SSL and then to socket
// TODO if ringbuffer gets multiple tail support
// we can work without buf_to_mqtt and thus reduce
// memory usage and remove one more memcpy buf_read->buf_to_mqtt
rbuf_t buf_to_mqtt; // RAW data for MQTT lib
int entropy_fd;
// careful host is borrowed, don't free
char **host;
mqtt_wss_log_ctx_t log;
} ws_client;
ws_client *ws_client_new(size_t buf_size, char **host, mqtt_wss_log_ctx_t log);
void ws_client_destroy(ws_client *client);
void ws_client_reset(ws_client *client);
int ws_client_start_handshake(ws_client *client);
int ws_client_want_write(ws_client *client);
int ws_client_process(ws_client *client);
int ws_client_send(ws_client *client, enum websocket_opcode frame_type, const char *data, size_t size);
#endif /* WS_CLIENT_H */
|