master
c 190 lines 5.96 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "websocket-internal.h"
4
5 // Helper function to determine if an opcode is a control opcode
6 bool websocket_frame_is_control_opcode(WEBSOCKET_OPCODE opcode) {
7 return (opcode == WS_OPCODE_CLOSE ||
8 opcode == WS_OPCODE_PING ||
9 opcode == WS_OPCODE_PONG);
10 }
11
12 // Validates that a buffer contains valid UTF-8 encoded data
13 // Returns true if the data is valid UTF-8, false otherwise
14 bool websocket_validate_utf8(const char *data, size_t length) {
15 if (!data)
16 return length == 0; // Empty data is valid
17
18 const unsigned char *bytes = (const unsigned char *)data;
19 size_t i = 0;
20
21 while (i < length) {
22 // Check for ASCII (single-byte character)
23 if (bytes[i] <= 0x7F) {
24 i++;
25 continue;
26 }
27
28 // Check for 2-byte sequence
29 else if ((bytes[i] & 0xE0) == 0xC0) {
30 // Need at least 2 bytes
31 if (i + 1 >= length)
32 return false;
33
34 // Second byte must be a continuation byte
35 if ((bytes[i+1] & 0xC0) != 0x80)
36 return false;
37
38 // Must not be overlong encoding
39 if (bytes[i] < 0xC2)
40 return false;
41
42 i += 2;
43 }
44
45 // Check for 3-byte sequence
46 else if ((bytes[i] & 0xF0) == 0xE0) {
47 // Need at least 3 bytes
48 if (i + 2 >= length)
49 return false;
50
51 // Second and third bytes must be continuation bytes
52 if ((bytes[i+1] & 0xC0) != 0x80 || (bytes[i+2] & 0xC0) != 0x80)
53 return false;
54
55 // Check for overlong encoding
56 if (bytes[i] == 0xE0 && (bytes[i+1] & 0xE0) == 0x80)
57 return false;
58
59 // Check for UTF-16 surrogates (not allowed in UTF-8)
60 if (bytes[i] == 0xED && (bytes[i+1] & 0xE0) == 0xA0)
61 return false;
62
63 i += 3;
64 }
65
66 // Check for 4-byte sequence
67 else if ((bytes[i] & 0xF8) == 0xF0) {
68 // Need at least 4 bytes
69 if (i + 3 >= length)
70 return false;
71
72 // Second, third, and fourth bytes must be continuation bytes
73 if ((bytes[i+1] & 0xC0) != 0x80 ||
74 (bytes[i+2] & 0xC0) != 0x80 ||
75 (bytes[i+3] & 0xC0) != 0x80)
76 return false;
77
78 // Check for overlong encoding
79 if (bytes[i] == 0xF0 && (bytes[i+1] & 0xF0) == 0x80)
80 return false;
81
82 // Check for values outside Unicode range
83 if (bytes[i] > 0xF4 || (bytes[i] == 0xF4 && bytes[i+1] > 0x8F))
84 return false;
85
86 i += 4;
87 }
88
89 // Invalid UTF-8 leading byte
90 else {
91 return false;
92 }
93 }
94
95 return true;
96 }
97
98 // Reset a client's message state for a new message
99 void websocket_client_message_reset(WS_CLIENT *wsc) {
100 if (!wsc)
101 return;
102
103 // Reset message buffer
104 wsb_reset(&wsc->payload);
105
106 // Also reset uncompressed buffer to avoid keeping stale data
107 wsb_reset(&wsc->u_payload);
108
109 // Reset client's message state
110 // We set message_complete to true by default (no fragmented message in progress),
111 // but this will be overridden based on the FIN bit for actual frames
112 wsc->message_complete = true;
113 wsc->is_compressed = false;
114 wsc->opcode = WS_OPCODE_TEXT; // Default opcode
115 wsc->frame_id = 0;
116 }
117
118 // Process a complete message (decompress if needed and call handler)
119 bool websocket_client_process_message(WS_CLIENT *wsc) {
120 if (!wsc || !wsc->message_complete)
121 return false;
122
123 worker_is_busy(WORKERS_WEBSOCKET_MESSAGE);
124
125 websocket_debug(wsc, "Processing message (opcode=0x%x, is_compressed=%d, length=%zu)",
126 wsc->opcode, wsc->is_compressed,
127 wsb_length(&wsc->payload));
128
129 // Handle control frames immediately
130 if (wsc->opcode != WS_OPCODE_TEXT && wsc->opcode != WS_OPCODE_BINARY) {
131 websocket_debug(wsc, "Control frame (opcode=0x%x) should not be handled by %s()", wsc->opcode, __FUNCTION__);
132 return false;
133 }
134
135 // At this point, we know we're dealing with a data frame (text or binary)
136 WS_BUF *wsb;
137
138 // Handle decompression if needed
139 if (wsc->is_compressed) {
140 if (!websocket_client_decompress_message(wsc)) {
141 websocket_protocol_exception(wsc, WS_CLOSE_INTERNAL_ERROR, "Decompression failed");
142 return false;
143 }
144 wsb = &wsc->u_payload;
145 }
146 else
147 wsb = &wsc->payload;
148
149 // For uncompressed messages, we just use payload buffer directly
150 if (wsc->opcode == WS_OPCODE_TEXT) {
151 wsb_null_terminate(wsb);
152
153 if (!websocket_validate_utf8(wsb_data(wsb), wsb_length(wsb))) {
154 websocket_protocol_exception(wsc, WS_CLOSE_INVALID_PAYLOAD,
155 "Invalid UTF-8 data in text message");
156 return false;
157 }
158 }
159
160 // Now handle the uncompressed message - using the new function
161 // that contains the actual handler logic
162
163 websocket_debug(wsc, "Handling message: type=%s, length=%zu, protocol=%d",
164 (wsc->opcode == WS_OPCODE_BINARY) ? "binary" : "text",
165 wsb_length(wsb), wsc->protocol);
166
167 // Ensure text messages are null-terminated
168 if (wsc->opcode == WS_OPCODE_TEXT)
169 wsb_null_terminate(wsb);
170
171 // Call the message callback if set - this allows protocols to be handled dynamically
172 if (wsc->on_message) {
173 websocket_debug(wsc, "Calling client message handler for protocol %d", wsc->protocol);
174 wsc->on_message(wsc, wsb_data(wsb), wsb_length(wsb), wsc->opcode);
175 }
176 else {
177 // No handler registered - this should not happen as we check during handshake
178 websocket_error(wsc, "No message handler registered for protocol %d", wsc->protocol);
179 return false;
180 }
181
182 // Update client message stats
183 wsc->message_id++;
184 wsc->frame_id = 0;
185
186 // Reset for the next message
187 websocket_client_message_reset(wsc);
188
189 return true;
190 }