1 /*************************************************************************/
2 /*  wsl_peer.cpp                                                         */
3 /*************************************************************************/
4 /*                       This file is part of:                           */
5 /*                           GODOT ENGINE                                */
6 /*                      https://godotengine.org                          */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
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 #ifndef JAVASCRIPT_ENABLED
32 
33 #include "wsl_peer.h"
34 
35 #include "wsl_client.h"
36 #include "wsl_server.h"
37 
38 #include "core/crypto/crypto_core.h"
39 #include "core/math/random_number_generator.h"
40 #include "core/os/os.h"
41 
generate_key()42 String WSLPeer::generate_key() {
43 	// Random key
44 	RandomNumberGenerator rng;
45 	rng.set_seed(OS::get_singleton()->get_unix_time());
46 	PoolVector<uint8_t> bkey;
47 	int len = 16; // 16 bytes, as per RFC
48 	bkey.resize(len);
49 	PoolVector<uint8_t>::Write w = bkey.write();
50 	for (int i = 0; i < len; i++) {
51 		w[i] = (uint8_t)rng.randi_range(0, 255);
52 	}
53 	return CryptoCore::b64_encode_str(&w[0], len);
54 }
55 
compute_key_response(String p_key)56 String WSLPeer::compute_key_response(String p_key) {
57 	String key = p_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Magic UUID as per RFC
58 	Vector<uint8_t> sha = key.sha1_buffer();
59 	return CryptoCore::b64_encode_str(sha.ptr(), sha.size());
60 }
61 
_wsl_destroy(struct PeerData ** p_data)62 void WSLPeer::_wsl_destroy(struct PeerData **p_data) {
63 	if (!p_data || !(*p_data))
64 		return;
65 	struct PeerData *data = *p_data;
66 	if (data->polling) {
67 		data->destroy = true;
68 		return;
69 	}
70 	wslay_event_context_free(data->ctx);
71 	memdelete(data);
72 	*p_data = NULL;
73 }
74 
_wsl_poll(struct PeerData * p_data)75 bool WSLPeer::_wsl_poll(struct PeerData *p_data) {
76 	p_data->polling = true;
77 	int err = 0;
78 	if ((err = wslay_event_recv(p_data->ctx)) != 0 || (err = wslay_event_send(p_data->ctx)) != 0) {
79 		print_verbose("Websocket (wslay) poll error: " + itos(err));
80 		p_data->destroy = true;
81 	}
82 	p_data->polling = false;
83 
84 	if (p_data->destroy || (wslay_event_get_close_sent(p_data->ctx) && wslay_event_get_close_received(p_data->ctx))) {
85 		bool valid = p_data->valid;
86 		_wsl_destroy(&p_data);
87 		return valid;
88 	}
89 	return false;
90 }
91 
wsl_recv_callback(wslay_event_context_ptr ctx,uint8_t * data,size_t len,int flags,void * user_data)92 ssize_t wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) {
93 	struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
94 	if (!peer_data->valid) {
95 		wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
96 		return -1;
97 	}
98 	Ref<StreamPeer> conn = peer_data->conn;
99 	int read = 0;
100 	Error err = conn->get_partial_data(data, len, read);
101 	if (err != OK) {
102 		print_verbose("Websocket get data error: " + itos(err) + ", read (should be 0!): " + itos(read));
103 		wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
104 		return -1;
105 	}
106 	if (read == 0) {
107 		wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
108 		return -1;
109 	}
110 	return read;
111 }
112 
wsl_send_callback(wslay_event_context_ptr ctx,const uint8_t * data,size_t len,int flags,void * user_data)113 ssize_t wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) {
114 	struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
115 	if (!peer_data->valid) {
116 		wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
117 		return -1;
118 	}
119 	Ref<StreamPeer> conn = peer_data->conn;
120 	int sent = 0;
121 	Error err = conn->put_partial_data(data, len, sent);
122 	if (err != OK) {
123 		wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
124 		return -1;
125 	}
126 	if (sent == 0) {
127 		wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
128 		return -1;
129 	}
130 	return sent;
131 }
132 
wsl_genmask_callback(wslay_event_context_ptr ctx,uint8_t * buf,size_t len,void * user_data)133 int wsl_genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) {
134 	RandomNumberGenerator rng;
135 	// TODO maybe use crypto in the future?
136 	rng.set_seed(OS::get_singleton()->get_unix_time());
137 	for (unsigned int i = 0; i < len; i++) {
138 		buf[i] = (uint8_t)rng.randi_range(0, 255);
139 	}
140 	return 0;
141 }
142 
wsl_msg_recv_callback(wslay_event_context_ptr ctx,const struct wslay_event_on_msg_recv_arg * arg,void * user_data)143 void wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) {
144 	struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
145 	if (!peer_data->valid || peer_data->closing) {
146 		return;
147 	}
148 	WSLPeer *peer = (WSLPeer *)peer_data->peer;
149 
150 	if (peer->parse_message(arg) != OK)
151 		return;
152 
153 	if (peer_data->is_server) {
154 		WSLServer *helper = (WSLServer *)peer_data->obj;
155 		helper->_on_peer_packet(peer_data->id);
156 	} else {
157 		WSLClient *helper = (WSLClient *)peer_data->obj;
158 		helper->_on_peer_packet();
159 	}
160 }
161 
162 wslay_event_callbacks wsl_callbacks = {
163 	wsl_recv_callback,
164 	wsl_send_callback,
165 	wsl_genmask_callback,
166 	NULL, /* on_frame_recv_start_callback */
167 	NULL, /* on_frame_recv_callback */
168 	NULL, /* on_frame_recv_end_callback */
169 	wsl_msg_recv_callback
170 };
171 
parse_message(const wslay_event_on_msg_recv_arg * arg)172 Error WSLPeer::parse_message(const wslay_event_on_msg_recv_arg *arg) {
173 	uint8_t is_string = 0;
174 	if (arg->opcode == WSLAY_TEXT_FRAME) {
175 		is_string = 1;
176 	} else if (arg->opcode == WSLAY_CONNECTION_CLOSE) {
177 		close_code = arg->status_code;
178 		size_t len = arg->msg_length;
179 		close_reason = "";
180 		if (len > 2 /* first 2 bytes = close code */) {
181 			close_reason.parse_utf8((char *)arg->msg + 2, len - 2);
182 		}
183 		if (!wslay_event_get_close_sent(_data->ctx)) {
184 			if (_data->is_server) {
185 				WSLServer *helper = (WSLServer *)_data->obj;
186 				helper->_on_close_request(_data->id, close_code, close_reason);
187 			} else {
188 				WSLClient *helper = (WSLClient *)_data->obj;
189 				helper->_on_close_request(close_code, close_reason);
190 			}
191 		}
192 		return ERR_FILE_EOF;
193 	} else if (arg->opcode != WSLAY_BINARY_FRAME) {
194 		// Ping or pong
195 		return ERR_SKIP;
196 	}
197 	_in_buffer.write_packet(arg->msg, arg->msg_length, &is_string);
198 	return OK;
199 }
200 
make_context(PeerData * p_data,unsigned int p_in_buf_size,unsigned int p_in_pkt_size,unsigned int p_out_buf_size,unsigned int p_out_pkt_size)201 void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size) {
202 	ERR_FAIL_COND(_data != NULL);
203 	ERR_FAIL_COND(p_data == NULL);
204 
205 	_in_buffer.resize(p_in_pkt_size, p_in_buf_size);
206 	_packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)));
207 
208 	_data = p_data;
209 	_data->peer = this;
210 	_data->valid = true;
211 
212 	if (_data->is_server)
213 		wslay_event_context_server_init(&(_data->ctx), &wsl_callbacks, _data);
214 	else
215 		wslay_event_context_client_init(&(_data->ctx), &wsl_callbacks, _data);
216 	wslay_event_config_set_max_recv_msg_length(_data->ctx, (1ULL << p_in_buf_size));
217 }
218 
set_write_mode(WriteMode p_mode)219 void WSLPeer::set_write_mode(WriteMode p_mode) {
220 	write_mode = p_mode;
221 }
222 
get_write_mode() const223 WSLPeer::WriteMode WSLPeer::get_write_mode() const {
224 	return write_mode;
225 }
226 
poll()227 void WSLPeer::poll() {
228 	if (!_data)
229 		return;
230 
231 	if (_wsl_poll(_data)) {
232 		_data = NULL;
233 	}
234 }
235 
put_packet(const uint8_t * p_buffer,int p_buffer_size)236 Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
237 
238 	ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
239 
240 	struct wslay_event_msg msg; // Should I use fragmented?
241 	msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME;
242 	msg.msg = p_buffer;
243 	msg.msg_length = p_buffer_size;
244 
245 	wslay_event_queue_msg(_data->ctx, &msg);
246 	if (wslay_event_send(_data->ctx) < 0) {
247 		close_now();
248 		return FAILED;
249 	}
250 	return OK;
251 }
252 
get_packet(const uint8_t ** r_buffer,int & r_buffer_size)253 Error WSLPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
254 
255 	r_buffer_size = 0;
256 
257 	ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
258 
259 	if (_in_buffer.packets_left() == 0)
260 		return ERR_UNAVAILABLE;
261 
262 	int read = 0;
263 	PoolVector<uint8_t>::Write rw = _packet_buffer.write();
264 	_in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read);
265 
266 	*r_buffer = rw.ptr();
267 	r_buffer_size = read;
268 
269 	return OK;
270 }
271 
get_available_packet_count() const272 int WSLPeer::get_available_packet_count() const {
273 
274 	if (!is_connected_to_host())
275 		return 0;
276 
277 	return _in_buffer.packets_left();
278 }
279 
was_string_packet() const280 bool WSLPeer::was_string_packet() const {
281 
282 	return _is_string;
283 }
284 
is_connected_to_host() const285 bool WSLPeer::is_connected_to_host() const {
286 
287 	return _data != NULL;
288 }
289 
close_now()290 void WSLPeer::close_now() {
291 	close(1000, "");
292 	_wsl_destroy(&_data);
293 }
294 
close(int p_code,String p_reason)295 void WSLPeer::close(int p_code, String p_reason) {
296 	if (_data && !wslay_event_get_close_sent(_data->ctx)) {
297 		CharString cs = p_reason.utf8();
298 		wslay_event_queue_close(_data->ctx, p_code, (uint8_t *)cs.ptr(), cs.size());
299 		wslay_event_send(_data->ctx);
300 		_data->closing = true;
301 	}
302 
303 	_in_buffer.clear();
304 	_packet_buffer.resize(0);
305 }
306 
get_connected_host() const307 IP_Address WSLPeer::get_connected_host() const {
308 
309 	ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IP_Address());
310 
311 	return _data->tcp->get_connected_host();
312 }
313 
get_connected_port() const314 uint16_t WSLPeer::get_connected_port() const {
315 
316 	ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), 0);
317 
318 	return _data->tcp->get_connected_port();
319 }
320 
set_no_delay(bool p_enabled)321 void WSLPeer::set_no_delay(bool p_enabled) {
322 
323 	ERR_FAIL_COND(!is_connected_to_host() || _data->tcp.is_null());
324 	_data->tcp->set_no_delay(p_enabled);
325 }
326 
invalidate()327 void WSLPeer::invalidate() {
328 	if (_data)
329 		_data->valid = false;
330 }
331 
WSLPeer()332 WSLPeer::WSLPeer() {
333 	_data = NULL;
334 	_is_string = 0;
335 	close_code = -1;
336 	write_mode = WRITE_MODE_BINARY;
337 }
338 
~WSLPeer()339 WSLPeer::~WSLPeer() {
340 
341 	close();
342 	invalidate();
343 	_wsl_destroy(&_data);
344 	_data = NULL;
345 }
346 
347 #endif // JAVASCRIPT_ENABLED
348