diff options
author | RĂ©mi Verschelde <rverschelde@gmail.com> | 2019-10-11 08:28:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-11 08:28:47 +0200 |
commit | be446038bb8231a16c1f1a915bab89f1b7e45368 (patch) | |
tree | c40d28f088ed4844d9e1bb0e9848b415438e73f9 | |
parent | be0da31f7a2e91b211b556ead5f1529f4427441a (diff) | |
parent | 2c557787c119ac629085cba2455132f5b82553dc (diff) |
Merge pull request #32683 from Faless/ws/improve_pr
WebSocket improvements, SSL server, custom headers.
-rw-r--r-- | modules/websocket/doc_classes/WebSocketClient.xml | 20 | ||||
-rw-r--r-- | modules/websocket/doc_classes/WebSocketServer.xml | 11 | ||||
-rw-r--r-- | modules/websocket/emws_client.cpp | 15 | ||||
-rw-r--r-- | modules/websocket/emws_client.h | 2 | ||||
-rw-r--r-- | modules/websocket/websocket_client.cpp | 24 | ||||
-rw-r--r-- | modules/websocket/websocket_client.h | 8 | ||||
-rw-r--r-- | modules/websocket/websocket_server.cpp | 41 | ||||
-rw-r--r-- | modules/websocket/websocket_server.h | 16 | ||||
-rw-r--r-- | modules/websocket/wsl_client.cpp | 19 | ||||
-rw-r--r-- | modules/websocket/wsl_client.h | 4 | ||||
-rw-r--r-- | modules/websocket/wsl_peer.cpp | 11 | ||||
-rw-r--r-- | modules/websocket/wsl_peer.h | 3 | ||||
-rw-r--r-- | modules/websocket/wsl_server.cpp | 34 | ||||
-rw-r--r-- | modules/websocket/wsl_server.h | 11 |
14 files changed, 181 insertions, 38 deletions
diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml index c3baf9de83..705e3485f5 100644 --- a/modules/websocket/doc_classes/WebSocketClient.xml +++ b/modules/websocket/doc_classes/WebSocketClient.xml @@ -21,10 +21,13 @@ </argument> <argument index="2" name="gd_mp_api" type="bool" default="false"> </argument> + <argument index="3" name="custom_headers" type="PoolStringArray" default="PoolStringArray( )"> + </argument> <description> Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested. If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted. If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]). + You can optionally pass a list of [code]custom_headers[/code] to be added to the handshake HTTP request (not supported in HTML5 platform). </description> </method> <method name="disconnect_from_host"> @@ -38,8 +41,25 @@ Disconnects this client from the connected host. See [method WebSocketPeer.close] for more information. </description> </method> + <method name="get_connected_host" qualifiers="const"> + <return type="String"> + </return> + <description> + Return the IP address of the currently connected host. + </description> + </method> + <method name="get_connected_port" qualifiers="const"> + <return type="int"> + </return> + <description> + Return the IP port of the currently connected host. + </description> + </method> </methods> <members> + <member name="trusted_ssl_certificate" type="X509Certificate" setter="set_trusted_ssl_certificate" getter="get_trusted_ssl_certificate"> + If specified, this [X509Certificate] will be the only one accepted when connecting to an SSL host. Any other certificate provided by the server will be regarded as invalid. + </member> <member name="verify_ssl" type="bool" setter="set_verify_ssl_enabled" getter="is_verify_ssl_enabled"> If [code]true[/code], SSL certificate verification is enabled. [b]Note:[/b] You must specify the certificates to be used in the Project Settings for it to work when exported. diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml index 63318e5874..651048316c 100644 --- a/modules/websocket/doc_classes/WebSocketServer.xml +++ b/modules/websocket/doc_classes/WebSocketServer.xml @@ -82,6 +82,17 @@ </description> </method> </methods> + <members> + <member name="ca_chain" type="X509Certificate" setter="set_ca_chain" getter="get_ca_chain"> + When using SSL (see [member private_key] and [member ssl_certificate]), you can set this to a valid [X509Certificate] to be provided as additional CA chain information during the SSL handshake. + </member> + <member name="private_key" type="CryptoKey" setter="set_private_key" getter="get_private_key"> + When set to a valid [CryptoKey] (along with [member ssl_certificate]) will cause the server to require SSL instead of regular TCP (i.e. the `wss://` protocol). + </member> + <member name="ssl_certificate" type="X509Certificate" setter="set_ssl_certificate" getter="get_ssl_certificate"> + When set to a valid [X509Certificate] (along with [member private_key]) will cause the server to require SSL instead of regular TCP (i.e. the `wss://` protocol). + </member> + </members> <signals> <signal name="client_close_request"> <argument index="0" name="id" type="int"> diff --git a/modules/websocket/emws_client.cpp b/modules/websocket/emws_client.cpp index 409cc9f699..fad766ea5d 100644 --- a/modules/websocket/emws_client.cpp +++ b/modules/websocket/emws_client.cpp @@ -64,13 +64,20 @@ EMSCRIPTEN_KEEPALIVE void _esws_on_close(void *obj, int code, char *reason, int } } -Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { +Error EMWSClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocols, const Vector<String> p_custom_headers) { String proto_string = p_protocols.join(","); String str = "ws://"; - if (p_ssl) + if (p_custom_headers.size()) { + WARN_PRINT_ONCE("Custom headers are not supported in in HTML5 platform."); + } + if (p_ssl) { str = "wss://"; + if (ssl_cert.is_valid()) { + WARN_PRINT_ONCE("Custom SSL certificate is not supported in HTML5 platform."); + } + } str += p_host + ":" + itos(p_port) + p_path; _is_connecting = true; @@ -193,12 +200,12 @@ void EMWSClient::disconnect_from_host(int p_code, String p_reason) { IP_Address EMWSClient::get_connected_host() const { - return IP_Address(); + ERR_FAIL_V_MSG(IP_Address(), "Not supported in HTML5 export."); }; uint16_t EMWSClient::get_connected_port() const { - return 1025; + ERR_FAIL_V_MSG(0, "Not supported in HTML5 export."); }; int EMWSClient::get_max_packet_size() const { diff --git a/modules/websocket/emws_client.h b/modules/websocket/emws_client.h index 1811d05eea..2d35f7f0f6 100644 --- a/modules/websocket/emws_client.h +++ b/modules/websocket/emws_client.h @@ -50,7 +50,7 @@ public: bool _is_connecting; Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const PoolVector<String> p_protocol = PoolVector<String>(), const Dictionary p_custom_headers = Dictionary()); Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); IP_Address get_connected_host() const; diff --git a/modules/websocket/websocket_client.cpp b/modules/websocket/websocket_client.cpp index 4ff5404c61..8bbd5aa37f 100644 --- a/modules/websocket/websocket_client.cpp +++ b/modules/websocket/websocket_client.cpp @@ -40,7 +40,7 @@ WebSocketClient::WebSocketClient() { WebSocketClient::~WebSocketClient() { } -Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protocols, bool gd_mp_api) { +Error WebSocketClient::connect_to_url(String p_url, const Vector<String> p_protocols, bool gd_mp_api, const Vector<String> p_custom_headers) { _is_multiplayer = gd_mp_api; String host = p_url; @@ -72,7 +72,7 @@ Error WebSocketClient::connect_to_url(String p_url, PoolVector<String> p_protoco host = host.substr(0, p_len); } - return connect_to_host(host, path, port, ssl, p_protocols); + return connect_to_host(host, path, port, ssl, p_protocols, p_custom_headers); } void WebSocketClient::set_verify_ssl_enabled(bool p_verify_ssl) { @@ -85,6 +85,17 @@ bool WebSocketClient::is_verify_ssl_enabled() const { return verify_ssl; } +Ref<X509Certificate> WebSocketClient::get_trusted_ssl_certificate() const { + + return ssl_cert; +} + +void WebSocketClient::set_trusted_ssl_certificate(Ref<X509Certificate> p_cert) { + + ERR_FAIL_COND(get_connection_status() != CONNECTION_DISCONNECTED); + ssl_cert = p_cert; +} + bool WebSocketClient::is_server() const { return false; @@ -132,13 +143,20 @@ void WebSocketClient::_on_error() { } void WebSocketClient::_bind_methods() { - ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api"), &WebSocketClient::connect_to_url, DEFVAL(PoolVector<String>()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("connect_to_url", "url", "protocols", "gd_mp_api", "custom_headers"), &WebSocketClient::connect_to_url, DEFVAL(Vector<String>()), DEFVAL(false), DEFVAL(Vector<String>())); ClassDB::bind_method(D_METHOD("disconnect_from_host", "code", "reason"), &WebSocketClient::disconnect_from_host, DEFVAL(1000), DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketClient::get_connected_host); + ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketClient::get_connected_port); ClassDB::bind_method(D_METHOD("set_verify_ssl_enabled", "enabled"), &WebSocketClient::set_verify_ssl_enabled); ClassDB::bind_method(D_METHOD("is_verify_ssl_enabled"), &WebSocketClient::is_verify_ssl_enabled); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "verify_ssl", PROPERTY_HINT_NONE, "", 0), "set_verify_ssl_enabled", "is_verify_ssl_enabled"); + ClassDB::bind_method(D_METHOD("get_trusted_ssl_certificate"), &WebSocketClient::get_trusted_ssl_certificate); + ClassDB::bind_method(D_METHOD("set_trusted_ssl_certificate"), &WebSocketClient::set_trusted_ssl_certificate); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "trusted_ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_trusted_ssl_certificate", "get_trusted_ssl_certificate"); + ADD_SIGNAL(MethodInfo("data_received")); ADD_SIGNAL(MethodInfo("connection_established", PropertyInfo(Variant::STRING, "protocol"))); ADD_SIGNAL(MethodInfo("server_close_request", PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); diff --git a/modules/websocket/websocket_client.h b/modules/websocket/websocket_client.h index 7ddb9468a5..08fd6798fe 100644 --- a/modules/websocket/websocket_client.h +++ b/modules/websocket/websocket_client.h @@ -31,6 +31,7 @@ #ifndef WEBSOCKET_CLIENT_H #define WEBSOCKET_CLIENT_H +#include "core/crypto/crypto.h" #include "core/error_list.h" #include "websocket_multiplayer_peer.h" #include "websocket_peer.h" @@ -43,17 +44,20 @@ class WebSocketClient : public WebSocketMultiplayerPeer { protected: Ref<WebSocketPeer> _peer; bool verify_ssl; + Ref<X509Certificate> ssl_cert; static void _bind_methods(); public: - Error connect_to_url(String p_url, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + Error connect_to_url(String p_url, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false, const Vector<String> p_custom_headers = Vector<String>()); void set_verify_ssl_enabled(bool p_verify_ssl); bool is_verify_ssl_enabled() const; + Ref<X509Certificate> get_trusted_ssl_certificate() const; + void set_trusted_ssl_certificate(Ref<X509Certificate> p_cert); virtual void poll() = 0; - virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()) = 0; + virtual Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()) = 0; virtual void disconnect_from_host(int p_code = 1000, String p_reason = "") = 0; virtual IP_Address get_connected_host() const = 0; virtual uint16_t get_connected_port() const = 0; diff --git a/modules/websocket/websocket_server.cpp b/modules/websocket/websocket_server.cpp index ef5f6f5c20..c7414075ed 100644 --- a/modules/websocket/websocket_server.cpp +++ b/modules/websocket/websocket_server.cpp @@ -42,19 +42,58 @@ WebSocketServer::~WebSocketServer() { void WebSocketServer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_listening"), &WebSocketServer::is_listening); - ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(PoolVector<String>()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("listen", "port", "protocols", "gd_mp_api"), &WebSocketServer::listen, DEFVAL(Vector<String>()), DEFVAL(false)); ClassDB::bind_method(D_METHOD("stop"), &WebSocketServer::stop); ClassDB::bind_method(D_METHOD("has_peer", "id"), &WebSocketServer::has_peer); ClassDB::bind_method(D_METHOD("get_peer_address", "id"), &WebSocketServer::get_peer_address); ClassDB::bind_method(D_METHOD("get_peer_port", "id"), &WebSocketServer::get_peer_port); ClassDB::bind_method(D_METHOD("disconnect_peer", "id", "code", "reason"), &WebSocketServer::disconnect_peer, DEFVAL(1000), DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_private_key"), &WebSocketServer::get_private_key); + ClassDB::bind_method(D_METHOD("set_private_key"), &WebSocketServer::set_private_key); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "private_key", PROPERTY_HINT_RESOURCE_TYPE, "CryptoKey", 0), "set_private_key", "get_private_key"); + + ClassDB::bind_method(D_METHOD("get_ssl_certificate"), &WebSocketServer::get_ssl_certificate); + ClassDB::bind_method(D_METHOD("set_ssl_certificate"), &WebSocketServer::set_ssl_certificate); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ssl_certificate", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ssl_certificate", "get_ssl_certificate"); + + ClassDB::bind_method(D_METHOD("get_ca_chain"), &WebSocketServer::get_ca_chain); + ClassDB::bind_method(D_METHOD("set_ca_chain"), &WebSocketServer::set_ca_chain); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "ca_chain", PROPERTY_HINT_RESOURCE_TYPE, "X509Certificate", 0), "set_ca_chain", "get_ca_chain"); + ADD_SIGNAL(MethodInfo("client_close_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "code"), PropertyInfo(Variant::STRING, "reason"))); ADD_SIGNAL(MethodInfo("client_disconnected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::BOOL, "was_clean_close"))); ADD_SIGNAL(MethodInfo("client_connected", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "protocol"))); ADD_SIGNAL(MethodInfo("data_received", PropertyInfo(Variant::INT, "id"))); } +Ref<CryptoKey> WebSocketServer::get_private_key() const { + return private_key; +} + +void WebSocketServer::set_private_key(Ref<CryptoKey> p_key) { + ERR_FAIL_COND(is_listening()); + private_key = p_key; +} + +Ref<X509Certificate> WebSocketServer::get_ssl_certificate() const { + return ssl_cert; +} + +void WebSocketServer::set_ssl_certificate(Ref<X509Certificate> p_cert) { + ERR_FAIL_COND(is_listening()); + ssl_cert = p_cert; +} + +Ref<X509Certificate> WebSocketServer::get_ca_chain() const { + return ca_chain; +} + +void WebSocketServer::set_ca_chain(Ref<X509Certificate> p_ca_chain) { + ERR_FAIL_COND(is_listening()); + ca_chain = p_ca_chain; +} + NetworkedMultiplayerPeer::ConnectionStatus WebSocketServer::get_connection_status() const { if (is_listening()) return CONNECTION_CONNECTED; diff --git a/modules/websocket/websocket_server.h b/modules/websocket/websocket_server.h index 83c0c10419..0b39f94473 100644 --- a/modules/websocket/websocket_server.h +++ b/modules/websocket/websocket_server.h @@ -31,6 +31,7 @@ #ifndef WEBSOCKET_H #define WEBSOCKET_H +#include "core/crypto/crypto.h" #include "core/reference.h" #include "websocket_multiplayer_peer.h" #include "websocket_peer.h" @@ -43,9 +44,13 @@ class WebSocketServer : public WebSocketMultiplayerPeer { protected: static void _bind_methods(); + Ref<CryptoKey> private_key; + Ref<X509Certificate> ssl_cert; + Ref<X509Certificate> ca_chain; + public: virtual void poll() = 0; - virtual Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false) = 0; + virtual Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false) = 0; virtual void stop() = 0; virtual bool is_listening() const = 0; virtual bool has_peer(int p_id) const = 0; @@ -62,6 +67,15 @@ public: void _on_disconnect(int32_t p_peer_id, bool p_was_clean); void _on_close_request(int32_t p_peer_id, int p_code, String p_reason); + Ref<CryptoKey> get_private_key() const; + void set_private_key(Ref<CryptoKey> p_key); + + Ref<X509Certificate> get_ssl_certificate() const; + void set_ssl_certificate(Ref<X509Certificate> p_cert); + + Ref<X509Certificate> get_ca_chain() const; + void set_ca_chain(Ref<X509Certificate> p_ca_chain); + virtual Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) = 0; WebSocketServer(); diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp index 0006a057e0..a422f65cfc 100644 --- a/modules/websocket/wsl_client.cpp +++ b/modules/websocket/wsl_client.cpp @@ -86,6 +86,7 @@ void WSLClient::_do_handshake() { WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); data->obj = this; data->conn = _connection; + data->tcp = _tcp; data->is_server = false; data->id = 1; _peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size); @@ -151,7 +152,7 @@ bool WSLClient::_verify_headers(String &r_protocol) { return true; } -Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) { +Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocols, const Vector<String> p_custom_headers) { ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE); @@ -180,7 +181,8 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, _connection = _tcp; _use_ssl = p_ssl; _host = p_host; - _protocols = p_protocols; + _protocols.clear(); + _protocols.append_array(p_protocols); _key = WSLPeer::generate_key(); // TODO custom extra headers (allow overriding this too?) @@ -199,6 +201,9 @@ Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, } request += "\r\n"; } + for (int i = 0; i < p_custom_headers.size(); i++) { + request += p_custom_headers[i] + "\r\n"; + } request += "\r\n"; _request = request.utf8(); @@ -236,7 +241,7 @@ void WSLClient::poll() { ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); ERR_FAIL_COND_MSG(ssl.is_null(), "SSL is not available in this build."); ssl->set_blocking_handshake_enabled(false); - if (ssl->connect_to_stream(_tcp, verify_ssl, _host) != OK) { + if (ssl->connect_to_stream(_tcp, verify_ssl, _host, ssl_cert) != OK) { disconnect_from_host(); _on_error(); return; @@ -293,7 +298,7 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) { _key = ""; _host = ""; - _protocols.resize(0); + _protocols.clear(); _use_ssl = false; _request = ""; @@ -305,12 +310,14 @@ void WSLClient::disconnect_from_host(int p_code, String p_reason) { IP_Address WSLClient::get_connected_host() const { - return IP_Address(); + ERR_FAIL_COND_V(!_peer->is_connected_to_host(), IP_Address()); + return _peer->get_connected_host(); } uint16_t WSLClient::get_connected_port() const { - return 1025; + ERR_FAIL_COND_V(!_peer->is_connected_to_host(), 0); + return _peer->get_connected_port(); } Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) { diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h index 57dfd635b7..870be94a87 100644 --- a/modules/websocket/wsl_client.h +++ b/modules/websocket/wsl_client.h @@ -64,7 +64,7 @@ private: String _key; String _host; - PoolVector<String> _protocols; + Vector<String> _protocols; bool _use_ssl; void _do_handshake(); @@ -72,7 +72,7 @@ private: public: Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>()); + Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, const Vector<String> p_protocol = Vector<String>(), const Vector<String> p_custom_headers = Vector<String>()); int get_max_packet_size() const; Ref<WebSocketPeer> get_peer(int p_peer_id) const; void disconnect_from_host(int p_code = 1000, String p_reason = ""); diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp index 74fb901232..32beccde5d 100644 --- a/modules/websocket/wsl_peer.cpp +++ b/modules/websocket/wsl_peer.cpp @@ -208,7 +208,6 @@ void WSLPeer::make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigne _data = p_data; _data->peer = this; _data->valid = true; - _connection = Ref<StreamPeer>(_data->conn); if (_data->is_server) wslay_event_context_server_init(&(_data->ctx), &wsl_callbacks, _data); @@ -302,18 +301,16 @@ void WSLPeer::close(int p_code, String p_reason) { IP_Address WSLPeer::get_connected_host() const { - ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address()); + ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), IP_Address()); - IP_Address ip; - return ip; + return _data->tcp->get_connected_host(); } uint16_t WSLPeer::get_connected_port() const { - ERR_FAIL_COND_V(!is_connected_to_host(), 0); + ERR_FAIL_COND_V(!is_connected_to_host() || _data->tcp.is_null(), 0); - uint16_t port = 0; - return port; + return _data->tcp->get_connected_port(); } void WSLPeer::invalidate() { diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h index d51b304fe1..01ad250468 100644 --- a/modules/websocket/wsl_peer.h +++ b/modules/websocket/wsl_peer.h @@ -35,6 +35,7 @@ #include "core/error_list.h" #include "core/io/packet_peer.h" +#include "core/io/stream_peer_tcp.h" #include "core/ring_buffer.h" #include "packet_buffer.h" #include "websocket_peer.h" @@ -55,6 +56,7 @@ public: void *obj; void *peer; Ref<StreamPeer> conn; + Ref<StreamPeerTCP> tcp; int id; wslay_event_context_ptr ctx; @@ -77,7 +79,6 @@ private: static bool _wsl_poll(struct PeerData *p_data); static void _wsl_destroy(struct PeerData **p_data); - Ref<StreamPeer> _connection; struct PeerData *_data; uint8_t _is_string; // Our packet info is just a boolean (is_string), using uint8_t for it. diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp index efb526eed1..993dceafb9 100644 --- a/modules/websocket/wsl_server.cpp +++ b/modules/websocket/wsl_server.cpp @@ -35,6 +35,7 @@ #include "core/project_settings.h" WSLServer::PendingPeer::PendingPeer() { + use_ssl = false; time = 0; has_request = false; response_sent = 0; @@ -42,7 +43,7 @@ WSLServer::PendingPeer::PendingPeer() { memset(req_buf, 0, sizeof(req_buf)); } -bool WSLServer::PendingPeer::_parse_request(const PoolStringArray p_protocols) { +bool WSLServer::PendingPeer::_parse_request(const Vector<String> p_protocols) { Vector<String> psa = String((char *)req_buf).split("\r\n"); int len = psa.size(); ERR_FAIL_COND_V_MSG(len < 4, false, "Not enough response headers, got: " + itos(len) + ", expected >= 4."); @@ -97,9 +98,19 @@ bool WSLServer::PendingPeer::_parse_request(const PoolStringArray p_protocols) { return true; } -Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) { +Error WSLServer::PendingPeer::do_handshake(const Vector<String> p_protocols) { if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT) return ERR_TIMEOUT; + if (use_ssl) { + Ref<StreamPeerSSL> ssl = static_cast<Ref<StreamPeerSSL> >(connection); + if (ssl.is_null()) + return FAILED; + ssl->poll(); + if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING) + return ERR_BUSY; + else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) + return FAILED; + } if (!has_request) { int read = 0; while (true) { @@ -143,11 +154,11 @@ Error WSLServer::PendingPeer::do_handshake(PoolStringArray p_protocols) { return OK; } -Error WSLServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) { +Error WSLServer::listen(int p_port, const Vector<String> p_protocols, bool gd_mp_api) { ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE); _is_multiplayer = gd_mp_api; - _protocols = p_protocols; + _protocols.append_array(p_protocols); _server->listen(p_port); return OK; @@ -185,6 +196,7 @@ void WSLServer::poll() { WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData); data->obj = this; data->conn = ppeer->connection; + data->tcp = ppeer->tcp; data->is_server = true; data->id = id; @@ -204,12 +216,21 @@ void WSLServer::poll() { return; while (_server->is_connection_available()) { - Ref<StreamPeer> conn = _server->take_connection(); + Ref<StreamPeerTCP> conn = _server->take_connection(); if (is_refusing_new_connections()) continue; // Conn will go out-of-scope and be closed. Ref<PendingPeer> peer = memnew(PendingPeer); - peer->connection = conn; + if (private_key.is_valid() && ssl_cert.is_valid()) { + Ref<StreamPeerSSL> ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create()); + ssl->set_blocking_handshake_enabled(false); + ssl->accept_stream(conn, private_key, ssl_cert, ca_chain); + peer->connection = ssl; + peer->use_ssl = true; + } else { + peer->connection = conn; + } + peer->tcp = conn; peer->time = OS::get_singleton()->get_ticks_msec(); _pending.push_back(peer); } @@ -231,6 +252,7 @@ void WSLServer::stop() { } _pending.clear(); _peer_map.clear(); + _protocols.clear(); } bool WSLServer::has_peer(int p_id) const { diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h index 2ceb941073..aae563355e 100644 --- a/modules/websocket/wsl_server.h +++ b/modules/websocket/wsl_server.h @@ -36,6 +36,7 @@ #include "websocket_server.h" #include "wsl_peer.h" +#include "core/io/stream_peer_ssl.h" #include "core/io/stream_peer_tcp.h" #include "core/io/tcp_server.h" @@ -49,10 +50,12 @@ private: class PendingPeer : public Reference { private: - bool _parse_request(const PoolStringArray p_protocols); + bool _parse_request(const Vector<String> p_protocols); public: + Ref<StreamPeerTCP> tcp; Ref<StreamPeer> connection; + bool use_ssl; int time; uint8_t req_buf[WSL_MAX_HEADER_SIZE]; @@ -65,7 +68,7 @@ private: PendingPeer(); - Error do_handshake(const PoolStringArray p_protocols); + Error do_handshake(const Vector<String> p_protocols); }; int _in_buf_size; @@ -75,11 +78,11 @@ private: List<Ref<PendingPeer> > _pending; Ref<TCP_Server> _server; - PoolStringArray _protocols; + Vector<String> _protocols; public: Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets); - Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false); + Error listen(int p_port, const Vector<String> p_protocols = Vector<String>(), bool gd_mp_api = false); void stop(); bool is_listening() const; int get_max_packet_size() const; |