1/**************************************************************************/
2/* wsl_peer.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "wsl_peer.h"
32
33#ifndef WEB_ENABLED
34
35#include "core/io/stream_peer_tls.h"
36
37CryptoCore::RandomGenerator *WSLPeer::_static_rng = nullptr;
38
39void WSLPeer::initialize() {
40 WebSocketPeer::_create = WSLPeer::_create;
41 _static_rng = memnew(CryptoCore::RandomGenerator);
42 _static_rng->init();
43}
44
45void WSLPeer::deinitialize() {
46 if (_static_rng) {
47 memdelete(_static_rng);
48 _static_rng = nullptr;
49 }
50}
51
52///
53/// Resolver
54///
55void WSLPeer::Resolver::start(const String &p_host, int p_port) {
56 stop();
57
58 port = p_port;
59 if (p_host.is_valid_ip_address()) {
60 ip_candidates.push_back(IPAddress(p_host));
61 } else {
62 // Queue hostname for resolution.
63 resolver_id = IP::get_singleton()->resolve_hostname_queue_item(p_host);
64 ERR_FAIL_COND(resolver_id == IP::RESOLVER_INVALID_ID);
65 // Check if it was found in cache.
66 IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(resolver_id);
67 if (ip_status == IP::RESOLVER_STATUS_DONE) {
68 ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolver_id);
69 IP::get_singleton()->erase_resolve_item(resolver_id);
70 resolver_id = IP::RESOLVER_INVALID_ID;
71 }
72 }
73}
74
75void WSLPeer::Resolver::stop() {
76 if (resolver_id != IP::RESOLVER_INVALID_ID) {
77 IP::get_singleton()->erase_resolve_item(resolver_id);
78 resolver_id = IP::RESOLVER_INVALID_ID;
79 }
80 port = 0;
81}
82
83void WSLPeer::Resolver::try_next_candidate(Ref<StreamPeerTCP> &p_tcp) {
84 // Check if we still need resolving.
85 if (resolver_id != IP::RESOLVER_INVALID_ID) {
86 IP::ResolverStatus ip_status = IP::get_singleton()->get_resolve_item_status(resolver_id);
87 if (ip_status == IP::RESOLVER_STATUS_WAITING) {
88 return;
89 }
90 if (ip_status == IP::RESOLVER_STATUS_DONE) {
91 ip_candidates = IP::get_singleton()->get_resolve_item_addresses(resolver_id);
92 }
93 IP::get_singleton()->erase_resolve_item(resolver_id);
94 resolver_id = IP::RESOLVER_INVALID_ID;
95 }
96
97 // Try the current candidate if we have one.
98 if (p_tcp->get_status() != StreamPeerTCP::STATUS_NONE) {
99 p_tcp->poll();
100 StreamPeerTCP::Status status = p_tcp->get_status();
101 if (status == StreamPeerTCP::STATUS_CONNECTED) {
102 p_tcp->set_no_delay(true);
103 ip_candidates.clear();
104 return;
105 } else if (status == StreamPeerTCP::STATUS_CONNECTING) {
106 return; // Keep connecting.
107 } else {
108 p_tcp->disconnect_from_host();
109 }
110 }
111
112 // Keep trying next candidate.
113 while (ip_candidates.size()) {
114 Error err = p_tcp->connect_to_host(ip_candidates.pop_front(), port);
115 if (err == OK) {
116 return;
117 } else {
118 p_tcp->disconnect_from_host();
119 }
120 }
121}
122
123///
124/// Server functions
125///
126Error WSLPeer::accept_stream(Ref<StreamPeer> p_stream) {
127 ERR_FAIL_COND_V(wsl_ctx || tcp.is_valid(), ERR_ALREADY_IN_USE);
128 ERR_FAIL_COND_V(p_stream.is_null(), ERR_INVALID_PARAMETER);
129
130 _clear();
131
132 if (p_stream->is_class_ptr(StreamPeerTCP::get_class_ptr_static())) {
133 tcp = p_stream;
134 connection = p_stream;
135 use_tls = false;
136 } else if (p_stream->is_class_ptr(StreamPeerTLS::get_class_ptr_static())) {
137 Ref<StreamPeer> base_stream = static_cast<Ref<StreamPeerTLS>>(p_stream)->get_stream();
138 ERR_FAIL_COND_V(base_stream.is_null() || !base_stream->is_class_ptr(StreamPeerTCP::get_class_ptr_static()), ERR_INVALID_PARAMETER);
139 tcp = static_cast<Ref<StreamPeerTCP>>(base_stream);
140 connection = p_stream;
141 use_tls = true;
142 }
143 ERR_FAIL_COND_V(connection.is_null() || tcp.is_null(), ERR_INVALID_PARAMETER);
144 is_server = true;
145 ready_state = STATE_CONNECTING;
146 handshake_buffer->resize(WSL_MAX_HEADER_SIZE);
147 handshake_buffer->seek(0);
148 return OK;
149}
150
151bool WSLPeer::_parse_client_request() {
152 Vector<String> psa = String((const char *)handshake_buffer->get_data_array().ptr(), handshake_buffer->get_position() - 4).split("\r\n");
153 int len = psa.size();
154 ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4.");
155
156 Vector<String> req = psa[0].split(" ", false);
157 ERR_FAIL_COND_V_MSG(req.size() < 2, false, "Invalid protocol or status code.");
158
159 // Wrong protocol
160 ERR_FAIL_COND_V_MSG(req[0] != "GET" || req[2] != "HTTP/1.1", false, "Invalid method or HTTP version.");
161
162 HashMap<String, String> headers;
163 for (int i = 1; i < len; i++) {
164 Vector<String> header = psa[i].split(":", false, 1);
165 ERR_FAIL_COND_V_MSG(header.size() != 2, false, "Invalid header -> " + psa[i]);
166 String name = header[0].to_lower();
167 String value = header[1].strip_edges();
168 if (headers.has(name)) {
169 headers[name] += "," + value;
170 } else {
171 headers[name] = value;
172 }
173 }
174 requested_host = headers.has("host") ? headers.get("host") : "";
175 requested_url = (use_tls ? "wss://" : "ws://") + requested_host + req[1];
176#define WSL_CHECK(NAME, VALUE) \
177 ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
178 "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
179#define WSL_CHECK_EX(NAME) \
180 ERR_FAIL_COND_V_MSG(!headers.has(NAME), false, "Missing header '" + String(NAME) + "'.");
181 WSL_CHECK("upgrade", "websocket");
182 WSL_CHECK("sec-websocket-version", "13");
183 WSL_CHECK_EX("sec-websocket-key");
184 WSL_CHECK_EX("connection");
185#undef WSL_CHECK_EX
186#undef WSL_CHECK
187 session_key = headers["sec-websocket-key"];
188 if (headers.has("sec-websocket-protocol")) {
189 Vector<String> protos = headers["sec-websocket-protocol"].split(",");
190 for (int i = 0; i < protos.size(); i++) {
191 String proto = protos[i].strip_edges();
192 // Check if we have the given protocol
193 for (int j = 0; j < supported_protocols.size(); j++) {
194 if (proto != supported_protocols[j]) {
195 continue;
196 }
197 selected_protocol = proto;
198 break;
199 }
200 // Found a protocol
201 if (!selected_protocol.is_empty()) {
202 break;
203 }
204 }
205 if (selected_protocol.is_empty()) { // Invalid protocol(s) requested
206 return false;
207 }
208 } else if (supported_protocols.size() > 0) { // No protocol requested, but we need one
209 return false;
210 }
211 return true;
212}
213
214Error WSLPeer::_do_server_handshake() {
215 if (use_tls) {
216 Ref<StreamPeerTLS> tls = static_cast<Ref<StreamPeerTLS>>(connection);
217 if (tls.is_null()) {
218 ERR_FAIL_V_MSG(ERR_BUG, "Couldn't get StreamPeerTLS for WebSocket handshake.");
219 close(-1);
220 return FAILED;
221 }
222 tls->poll();
223 if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) {
224 return OK; // Pending handshake
225 } else if (tls->get_status() != StreamPeerTLS::STATUS_CONNECTED) {
226 print_verbose(vformat("WebSocket SSL connection error during handshake (StreamPeerTLS status code %d).", tls->get_status()));
227 close(-1);
228 return FAILED;
229 }
230 }
231
232 if (pending_request) {
233 int read = 0;
234 while (true) {
235 ERR_FAIL_COND_V_MSG(handshake_buffer->get_available_bytes() < 1, ERR_OUT_OF_MEMORY, "WebSocket response headers are too big.");
236 int pos = handshake_buffer->get_position();
237 uint8_t byte;
238 Error err = connection->get_partial_data(&byte, 1, read);
239 if (err != OK) { // Got an error
240 print_verbose(vformat("WebSocket error while getting partial data (StreamPeer error code %d).", err));
241 close(-1);
242 return FAILED;
243 } else if (read != 1) { // Busy, wait next poll
244 return OK;
245 }
246 handshake_buffer->put_u8(byte);
247 const char *r = (const char *)handshake_buffer->get_data_array().ptr();
248 int l = pos;
249 if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
250 if (!_parse_client_request()) {
251 close(-1);
252 return FAILED;
253 }
254 String s = "HTTP/1.1 101 Switching Protocols\r\n";
255 s += "Upgrade: websocket\r\n";
256 s += "Connection: Upgrade\r\n";
257 s += "Sec-WebSocket-Accept: " + _compute_key_response(session_key) + "\r\n";
258 if (!selected_protocol.is_empty()) {
259 s += "Sec-WebSocket-Protocol: " + selected_protocol + "\r\n";
260 }
261 for (int i = 0; i < handshake_headers.size(); i++) {
262 s += handshake_headers[i] + "\r\n";
263 }
264 s += "\r\n";
265 CharString cs = s.utf8();
266 handshake_buffer->clear();
267 handshake_buffer->put_data((const uint8_t *)cs.get_data(), cs.length());
268 handshake_buffer->seek(0);
269 pending_request = false;
270 break;
271 }
272 }
273 }
274
275 if (pending_request) { // Still pending.
276 return OK;
277 }
278
279 int left = handshake_buffer->get_available_bytes();
280 if (left) {
281 Vector<uint8_t> data = handshake_buffer->get_data_array();
282 int pos = handshake_buffer->get_position();
283 int sent = 0;
284 Error err = connection->put_partial_data(data.ptr() + pos, left, sent);
285 if (err != OK) {
286 print_verbose(vformat("WebSocket error while putting partial data (StreamPeer error code %d).", err));
287 close(-1);
288 return err;
289 }
290 handshake_buffer->seek(pos + sent);
291 left -= sent;
292 if (left == 0) {
293 resolver.stop();
294 // Response sent, initialize wslay context.
295 wslay_event_context_server_init(&wsl_ctx, &_wsl_callbacks, this);
296 wslay_event_config_set_max_recv_msg_length(wsl_ctx, inbound_buffer_size);
297 in_buffer.resize(nearest_shift(inbound_buffer_size), max_queued_packets);
298 packet_buffer.resize(inbound_buffer_size);
299 ready_state = STATE_OPEN;
300 }
301 }
302
303 return OK;
304}
305
306///
307/// Client functions
308///
309void WSLPeer::_do_client_handshake() {
310 ERR_FAIL_COND(tcp.is_null());
311
312 // Try to connect to candidates.
313 if (resolver.has_more_candidates()) {
314 resolver.try_next_candidate(tcp);
315 if (resolver.has_more_candidates()) {
316 return; // Still pending.
317 }
318 }
319
320 tcp->poll();
321 if (tcp->get_status() == StreamPeerTCP::STATUS_CONNECTING) {
322 return; // Keep connecting.
323 } else if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
324 close(-1); // Failed to connect.
325 return;
326 }
327
328 if (use_tls) {
329 Ref<StreamPeerTLS> tls;
330 if (connection == tcp) {
331 // Start SSL handshake
332 tls = Ref<StreamPeerTLS>(StreamPeerTLS::create());
333 ERR_FAIL_COND(tls.is_null());
334 if (tls->connect_to_stream(tcp, requested_host, tls_options) != OK) {
335 close(-1);
336 return; // Error.
337 }
338 connection = tls;
339 } else {
340 tls = static_cast<Ref<StreamPeerTLS>>(connection);
341 ERR_FAIL_COND(tls.is_null());
342 tls->poll();
343 }
344 if (tls->get_status() == StreamPeerTLS::STATUS_HANDSHAKING) {
345 return; // Need more polling.
346 } else if (tls->get_status() != StreamPeerTLS::STATUS_CONNECTED) {
347 close(-1);
348 return; // Error.
349 }
350 }
351
352 // Do websocket handshake.
353 if (pending_request) {
354 int left = handshake_buffer->get_available_bytes();
355 int pos = handshake_buffer->get_position();
356 const Vector<uint8_t> data = handshake_buffer->get_data_array();
357 int sent = 0;
358 Error err = connection->put_partial_data(data.ptr() + pos, left, sent);
359 // Sending handshake failed
360 if (err != OK) {
361 close(-1);
362 return; // Error.
363 }
364 handshake_buffer->seek(pos + sent);
365 if (handshake_buffer->get_available_bytes() == 0) {
366 pending_request = false;
367 handshake_buffer->clear();
368 handshake_buffer->resize(WSL_MAX_HEADER_SIZE);
369 handshake_buffer->seek(0);
370 }
371 } else {
372 int read = 0;
373 while (true) {
374 int left = handshake_buffer->get_available_bytes();
375 int pos = handshake_buffer->get_position();
376 if (left == 0) {
377 // Header is too big
378 close(-1);
379 ERR_FAIL_MSG("Response headers too big.");
380 }
381
382 uint8_t byte;
383 Error err = connection->get_partial_data(&byte, 1, read);
384 if (err != OK) {
385 // Got some error.
386 close(-1);
387 return;
388 } else if (read != 1) {
389 // Busy, wait next poll.
390 break;
391 }
392 handshake_buffer->put_u8(byte);
393
394 // Check "\r\n\r\n" header terminator
395 const char *r = (const char *)handshake_buffer->get_data_array().ptr();
396 int l = pos;
397 if (l > 3 && r[l] == '\n' && r[l - 1] == '\r' && r[l - 2] == '\n' && r[l - 3] == '\r') {
398 // Response is over, verify headers and initialize wslay context/
399 if (!_verify_server_response()) {
400 close(-1);
401 ERR_FAIL_MSG("Invalid response headers.");
402 }
403 wslay_event_context_client_init(&wsl_ctx, &_wsl_callbacks, this);
404 wslay_event_config_set_max_recv_msg_length(wsl_ctx, inbound_buffer_size);
405 in_buffer.resize(nearest_shift(inbound_buffer_size), max_queued_packets);
406 packet_buffer.resize(inbound_buffer_size);
407 ready_state = STATE_OPEN;
408 break;
409 }
410 }
411 }
412}
413
414bool WSLPeer::_verify_server_response() {
415 Vector<String> psa = String((const char *)handshake_buffer->get_data_array().ptr(), handshake_buffer->get_position() - 4).split("\r\n");
416 int len = psa.size();
417 ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers. Got: " + itos(len) + ", expected >= 4.");
418
419 Vector<String> req = psa[0].split(" ", false);
420 ERR_FAIL_COND_V_MSG(req.size() < 2, false, "Invalid protocol or status code. Got '" + psa[0] + "', expected 'HTTP/1.1 101'.");
421
422 // Wrong protocol
423 ERR_FAIL_COND_V_MSG(req[0] != "HTTP/1.1", false, "Invalid protocol. Got: '" + req[0] + "', expected 'HTTP/1.1'.");
424 ERR_FAIL_COND_V_MSG(req[1] != "101", false, "Invalid status code. Got: '" + req[1] + "', expected '101'.");
425
426 HashMap<String, String> headers;
427 for (int i = 1; i < len; i++) {
428 Vector<String> header = psa[i].split(":", false, 1);
429 ERR_FAIL_COND_V_MSG(header.size() != 2, false, "Invalid header -> " + psa[i] + ".");
430 String name = header[0].to_lower();
431 String value = header[1].strip_edges();
432 if (headers.has(name)) {
433 headers[name] += "," + value;
434 } else {
435 headers[name] = value;
436 }
437 }
438
439#define WSL_CHECK(NAME, VALUE) \
440 ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false, \
441 "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
442#define WSL_CHECK_NC(NAME, VALUE) \
443 ERR_FAIL_COND_V_MSG(!headers.has(NAME) || headers[NAME] != VALUE, false, \
444 "Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'.");
445 WSL_CHECK("connection", "upgrade");
446 WSL_CHECK("upgrade", "websocket");
447 WSL_CHECK_NC("sec-websocket-accept", _compute_key_response(session_key));
448#undef WSL_CHECK_NC
449#undef WSL_CHECK
450 if (supported_protocols.size() == 0) {
451 // We didn't request a custom protocol
452 ERR_FAIL_COND_V_MSG(headers.has("sec-websocket-protocol"), false, "Received unrequested sub-protocol -> " + headers["sec-websocket-protocol"]);
453 } else {
454 // We requested at least one custom protocol but didn't receive one
455 ERR_FAIL_COND_V_MSG(!headers.has("sec-websocket-protocol"), false, "Requested sub-protocol(s) but received none.");
456 // Check received sub-protocol was one of those requested.
457 selected_protocol = headers["sec-websocket-protocol"];
458 bool valid = false;
459 for (int i = 0; i < supported_protocols.size(); i++) {
460 if (supported_protocols[i] != selected_protocol) {
461 continue;
462 }
463 valid = true;
464 break;
465 }
466 if (!valid) {
467 ERR_FAIL_V_MSG(false, "Received unrequested sub-protocol -> " + selected_protocol);
468 }
469 }
470 return true;
471}
472
473Error WSLPeer::connect_to_url(const String &p_url, Ref<TLSOptions> p_options) {
474 ERR_FAIL_COND_V(wsl_ctx || tcp.is_valid(), ERR_ALREADY_IN_USE);
475 ERR_FAIL_COND_V(p_url.is_empty(), ERR_INVALID_PARAMETER);
476 ERR_FAIL_COND_V(p_options.is_valid() && p_options->is_server(), ERR_INVALID_PARAMETER);
477
478 _clear();
479
480 String host;
481 String path;
482 String scheme;
483 int port = 0;
484 Error err = p_url.parse_url(scheme, host, port, path);
485 ERR_FAIL_COND_V_MSG(err != OK, err, "Invalid URL: " + p_url);
486 if (scheme.is_empty()) {
487 scheme = "ws://";
488 }
489 ERR_FAIL_COND_V_MSG(scheme != "ws://" && scheme != "wss://", ERR_INVALID_PARAMETER, vformat("Invalid protocol: \"%s\" (must be either \"ws://\" or \"wss://\").", scheme));
490
491 use_tls = false;
492 if (scheme == "wss://") {
493 use_tls = true;
494 }
495 if (port == 0) {
496 port = use_tls ? 443 : 80;
497 }
498 if (path.is_empty()) {
499 path = "/";
500 }
501
502 ERR_FAIL_COND_V_MSG(use_tls && !StreamPeerTLS::is_available(), ERR_UNAVAILABLE, "WSS is not available in this build.");
503
504 requested_url = p_url;
505 requested_host = host;
506
507 if (p_options.is_valid()) {
508 tls_options = p_options;
509 } else {
510 tls_options = TLSOptions::client();
511 }
512
513 tcp.instantiate();
514
515 resolver.start(host, port);
516 resolver.try_next_candidate(tcp);
517
518 if (tcp->get_status() != StreamPeerTCP::STATUS_CONNECTING && tcp->get_status() != StreamPeerTCP::STATUS_CONNECTED && !resolver.has_more_candidates()) {
519 _clear();
520 return FAILED;
521 }
522 connection = tcp;
523
524 // Prepare handshake request.
525 session_key = _generate_key();
526 String request = "GET " + path + " HTTP/1.1\r\n";
527 String port_string;
528 if ((port != 80 && !use_tls) || (port != 443 && use_tls)) {
529 port_string = ":" + itos(port);
530 }
531 request += "Host: " + host + port_string + "\r\n";
532 request += "Upgrade: websocket\r\n";
533 request += "Connection: Upgrade\r\n";
534 request += "Sec-WebSocket-Key: " + session_key + "\r\n";
535 request += "Sec-WebSocket-Version: 13\r\n";
536 if (supported_protocols.size() > 0) {
537 request += "Sec-WebSocket-Protocol: ";
538 for (int i = 0; i < supported_protocols.size(); i++) {
539 if (i != 0) {
540 request += ",";
541 }
542 request += supported_protocols[i];
543 }
544 request += "\r\n";
545 }
546 for (int i = 0; i < handshake_headers.size(); i++) {
547 request += handshake_headers[i] + "\r\n";
548 }
549 request += "\r\n";
550 CharString cs = request.utf8();
551 handshake_buffer->put_data((const uint8_t *)cs.get_data(), cs.length());
552 handshake_buffer->seek(0);
553 ready_state = STATE_CONNECTING;
554 is_server = false;
555 return OK;
556}
557
558///
559/// Callback functions.
560///
561ssize_t WSLPeer::_wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) {
562 WSLPeer *peer = (WSLPeer *)user_data;
563 Ref<StreamPeer> conn = peer->connection;
564 if (conn.is_null()) {
565 wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
566 return -1;
567 }
568 int read = 0;
569 Error err = conn->get_partial_data(data, len, read);
570 if (err != OK) {
571 print_verbose("Websocket get data error: " + itos(err) + ", read (should be 0!): " + itos(read));
572 wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
573 return -1;
574 }
575 if (read == 0) {
576 wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
577 return -1;
578 }
579 return read;
580}
581
582ssize_t WSLPeer::_wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) {
583 WSLPeer *peer = (WSLPeer *)user_data;
584 Ref<StreamPeer> conn = peer->connection;
585 if (conn.is_null()) {
586 wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
587 return -1;
588 }
589 int sent = 0;
590 Error err = conn->put_partial_data(data, len, sent);
591 if (err != OK) {
592 wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
593 return -1;
594 }
595 if (sent == 0) {
596 wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
597 return -1;
598 }
599 return sent;
600}
601
602int WSLPeer::_wsl_genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) {
603 ERR_FAIL_COND_V(!_static_rng, WSLAY_ERR_CALLBACK_FAILURE);
604 Error err = _static_rng->get_random_bytes(buf, len);
605 ERR_FAIL_COND_V(err != OK, WSLAY_ERR_CALLBACK_FAILURE);
606 return 0;
607}
608
609void WSLPeer::_wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) {
610 WSLPeer *peer = (WSLPeer *)user_data;
611 uint8_t op = arg->opcode;
612
613 if (op == WSLAY_CONNECTION_CLOSE) {
614 // Close request or confirmation.
615 peer->close_code = arg->status_code;
616 size_t len = arg->msg_length;
617 peer->close_reason = "";
618 if (len > 2 /* first 2 bytes = close code */) {
619 peer->close_reason.parse_utf8((char *)arg->msg + 2, len - 2);
620 }
621 if (peer->ready_state == STATE_OPEN) {
622 peer->ready_state = STATE_CLOSING;
623 }
624 return;
625 }
626
627 if (peer->ready_state == STATE_CLOSING) {
628 return;
629 }
630
631 if (op == WSLAY_TEXT_FRAME || op == WSLAY_BINARY_FRAME) {
632 // Message.
633 uint8_t is_string = arg->opcode == WSLAY_TEXT_FRAME ? 1 : 0;
634 peer->in_buffer.write_packet(arg->msg, arg->msg_length, &is_string);
635 }
636 // Ping or pong.
637}
638
639wslay_event_callbacks WSLPeer::_wsl_callbacks = {
640 _wsl_recv_callback,
641 _wsl_send_callback,
642 _wsl_genmask_callback,
643 nullptr, /* on_frame_recv_start_callback */
644 nullptr, /* on_frame_recv_callback */
645 nullptr, /* on_frame_recv_end_callback */
646 _wsl_msg_recv_callback
647};
648
649String WSLPeer::_generate_key() {
650 // Random key
651 Vector<uint8_t> bkey;
652 int len = 16; // 16 bytes, as per RFC
653 bkey.resize(len);
654 _wsl_genmask_callback(nullptr, bkey.ptrw(), len, nullptr);
655 return CryptoCore::b64_encode_str(bkey.ptrw(), len);
656}
657
658String WSLPeer::_compute_key_response(String p_key) {
659 String key = p_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Magic UUID as per RFC
660 Vector<uint8_t> sha = key.sha1_buffer();
661 return CryptoCore::b64_encode_str(sha.ptr(), sha.size());
662}
663
664void WSLPeer::poll() {
665 // Nothing to do.
666 if (ready_state == STATE_CLOSED) {
667 return;
668 }
669
670 if (ready_state == STATE_CONNECTING) {
671 if (is_server) {
672 _do_server_handshake();
673 } else {
674 _do_client_handshake();
675 }
676 }
677
678 if (ready_state == STATE_OPEN || ready_state == STATE_CLOSING) {
679 ERR_FAIL_COND(!wsl_ctx);
680 int err = 0;
681 if ((err = wslay_event_recv(wsl_ctx)) != 0 || (err = wslay_event_send(wsl_ctx)) != 0) {
682 // Error close.
683 print_verbose("Websocket (wslay) poll error: " + itos(err));
684 wslay_event_context_free(wsl_ctx);
685 wsl_ctx = nullptr;
686 close(-1);
687 return;
688 }
689 if (wslay_event_get_close_sent(wsl_ctx) && wslay_event_get_close_received(wsl_ctx)) {
690 // Clean close.
691 wslay_event_context_free(wsl_ctx);
692 wsl_ctx = nullptr;
693 close(-1);
694 return;
695 }
696 }
697}
698
699Error WSLPeer::_send(const uint8_t *p_buffer, int p_buffer_size, wslay_opcode p_opcode) {
700 ERR_FAIL_COND_V(ready_state != STATE_OPEN, FAILED);
701 ERR_FAIL_COND_V(wslay_event_get_queued_msg_count(wsl_ctx) >= (uint32_t)max_queued_packets, ERR_OUT_OF_MEMORY);
702 ERR_FAIL_COND_V(outbound_buffer_size > 0 && (wslay_event_get_queued_msg_length(wsl_ctx) + p_buffer_size > (uint32_t)outbound_buffer_size), ERR_OUT_OF_MEMORY);
703
704 struct wslay_event_msg msg;
705 msg.opcode = p_opcode;
706 msg.msg = p_buffer;
707 msg.msg_length = p_buffer_size;
708
709 // Queue & send message.
710 if (wslay_event_queue_msg(wsl_ctx, &msg) != 0 || wslay_event_send(wsl_ctx) != 0) {
711 close(-1);
712 return FAILED;
713 }
714 return OK;
715}
716
717Error WSLPeer::send(const uint8_t *p_buffer, int p_buffer_size, WriteMode p_mode) {
718 wslay_opcode opcode = p_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME;
719 return _send(p_buffer, p_buffer_size, opcode);
720}
721
722Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
723 return _send(p_buffer, p_buffer_size, WSLAY_BINARY_FRAME);
724}
725
726Error WSLPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
727 r_buffer_size = 0;
728
729 ERR_FAIL_COND_V(ready_state != STATE_OPEN, FAILED);
730
731 if (in_buffer.packets_left() == 0) {
732 return ERR_UNAVAILABLE;
733 }
734
735 int read = 0;
736 uint8_t *rw = packet_buffer.ptrw();
737 in_buffer.read_packet(rw, packet_buffer.size(), &was_string, read);
738
739 *r_buffer = rw;
740 r_buffer_size = read;
741
742 return OK;
743}
744
745int WSLPeer::get_available_packet_count() const {
746 if (ready_state != STATE_OPEN) {
747 return 0;
748 }
749
750 return in_buffer.packets_left();
751}
752
753int WSLPeer::get_current_outbound_buffered_amount() const {
754 if (ready_state != STATE_OPEN) {
755 return 0;
756 }
757
758 return wslay_event_get_queued_msg_length(wsl_ctx);
759}
760
761void WSLPeer::close(int p_code, String p_reason) {
762 if (p_code < 0) {
763 // Force immediate close.
764 ready_state = STATE_CLOSED;
765 }
766
767 if (ready_state == STATE_OPEN && !wslay_event_get_close_sent(wsl_ctx)) {
768 CharString cs = p_reason.utf8();
769 wslay_event_queue_close(wsl_ctx, p_code, (uint8_t *)cs.ptr(), cs.length());
770 wslay_event_send(wsl_ctx);
771 ready_state = STATE_CLOSING;
772 } else if (ready_state == STATE_CONNECTING || ready_state == STATE_CLOSED) {
773 ready_state = STATE_CLOSED;
774 connection.unref();
775 if (tcp.is_valid()) {
776 tcp->disconnect_from_host();
777 tcp.unref();
778 }
779 }
780
781 in_buffer.clear();
782 packet_buffer.resize(0);
783}
784
785IPAddress WSLPeer::get_connected_host() const {
786 ERR_FAIL_COND_V(tcp.is_null(), IPAddress());
787 return tcp->get_connected_host();
788}
789
790uint16_t WSLPeer::get_connected_port() const {
791 ERR_FAIL_COND_V(tcp.is_null(), 0);
792 return tcp->get_connected_port();
793}
794
795String WSLPeer::get_selected_protocol() const {
796 return selected_protocol;
797}
798
799String WSLPeer::get_requested_url() const {
800 return requested_url;
801}
802
803void WSLPeer::set_no_delay(bool p_enabled) {
804 ERR_FAIL_COND(tcp.is_null());
805 tcp->set_no_delay(p_enabled);
806}
807
808void WSLPeer::_clear() {
809 // Connection info.
810 ready_state = STATE_CLOSED;
811 is_server = false;
812 connection.unref();
813 if (tcp.is_valid()) {
814 tcp->disconnect_from_host();
815 tcp.unref();
816 }
817 if (wsl_ctx) {
818 wslay_event_context_free(wsl_ctx);
819 wsl_ctx = nullptr;
820 }
821
822 resolver.stop();
823 requested_url.clear();
824 requested_host.clear();
825 pending_request = true;
826 handshake_buffer->clear();
827 selected_protocol.clear();
828 session_key.clear();
829
830 // Pending packets info.
831 was_string = 0;
832 in_buffer.clear();
833 packet_buffer.clear();
834
835 // Close code info.
836 close_code = -1;
837 close_reason.clear();
838}
839
840WSLPeer::WSLPeer() {
841 handshake_buffer.instantiate();
842}
843
844WSLPeer::~WSLPeer() {
845 close(-1);
846}
847
848#endif // WEB_ENABLED
849