diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Common/Connection.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Common/Connection.cs index a890f9c40ff..68fe52f8f43 100644 --- a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Common/Connection.cs +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Common/Connection.cs @@ -8,12 +8,18 @@ namespace Mirror.SimpleWeb { - internal sealed class Connection : IDisposable + public interface IConnection + { + public TcpClient Client { get; } + public Stream Stream { get; set; } + } + internal sealed class Connection : IConnection, IDisposable { readonly object disposedLock = new object(); public const int IdNotSet = -1; public TcpClient client; + public TcpClient Client => client; public int connId = IdNotSet; /// @@ -28,6 +34,12 @@ internal sealed class Connection : IDisposable public string remoteAddress; public Stream stream; + public Stream Stream + { + get => stream; + set => stream = value; + } + public Thread receiveThread; public Thread sendThread; diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ICreateStream.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ICreateStream.cs new file mode 100644 index 00000000000..e88d1ea3ec2 --- /dev/null +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ICreateStream.cs @@ -0,0 +1,7 @@ +namespace Mirror.SimpleWeb +{ + public interface ICreateStream + { + bool TryCreateStream(IConnection conn); + } +} diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ICreateStream.cs.meta b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ICreateStream.cs.meta new file mode 100644 index 00000000000..251c4ec50f6 --- /dev/null +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ICreateStream.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 353fe98750934e5e93a514b2de7da5e0 +timeCreated: 1704723878 \ No newline at end of file diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ServerSslHelper.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ServerSslHelper.cs index fb45b135878..5652fd7083c 100644 --- a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ServerSslHelper.cs +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/ServerSslHelper.cs @@ -22,7 +22,7 @@ public SslConfig(bool enabled, string certPath, string certPassword, SslProtocol this.sslProtocols = sslProtocols; } } - internal class ServerSslHelper + internal class ServerSslHelper: ICreateStream { readonly SslConfig config; readonly X509Certificate2 certificate; @@ -37,14 +37,14 @@ public ServerSslHelper(SslConfig sslConfig) } } - internal bool TryCreateStream(Connection conn) + public bool TryCreateStream(IConnection conn) { - NetworkStream stream = conn.client.GetStream(); + NetworkStream stream = conn.Client.GetStream(); if (config.enabled) { try { - conn.stream = CreateStream(stream); + conn.Stream = CreateStream(stream); return true; } catch (Exception e) @@ -55,7 +55,7 @@ internal bool TryCreateStream(Connection conn) } else { - conn.stream = stream; + conn.Stream = stream; return true; } } diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/SimpleWebServer.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/SimpleWebServer.cs index 6ededebc480..008780ee679 100644 --- a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/SimpleWebServer.cs +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/SimpleWebServer.cs @@ -23,7 +23,7 @@ public SimpleWebServer(int maxMessagesPerTick, TcpConfig tcpConfig, int maxMessa // use max because bufferpool is used for both messages and handshake int max = Math.Max(maxMessageSize, handshakeMaxSize); bufferPool = new BufferPool(5, 20, max); - server = new WebSocketServer(tcpConfig, maxMessageSize, handshakeMaxSize, sslConfig, bufferPool); + server = new WebSocketServer(tcpConfig, maxMessageSize, handshakeMaxSize, bufferPool, new ServerSslHelper(sslConfig)); } public void Start(ushort port) diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/WebSocketServer.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/WebSocketServer.cs index c8895a2340f..1c2ecaa5fef 100644 --- a/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/WebSocketServer.cs +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWeb/Server/WebSocketServer.cs @@ -17,17 +17,17 @@ public class WebSocketServer Thread acceptThread; bool serverStopped; readonly ServerHandshake handShake; - readonly ServerSslHelper sslHelper; + readonly ICreateStream streamCreator; readonly BufferPool bufferPool; readonly ConcurrentDictionary connections = new ConcurrentDictionary(); int _idCounter = 0; - public WebSocketServer(TcpConfig tcpConfig, int maxMessageSize, int handshakeMaxSize, SslConfig sslConfig, BufferPool bufferPool) + public WebSocketServer(TcpConfig tcpConfig, int maxMessageSize, int handshakeMaxSize, BufferPool bufferPool, ICreateStream streamCreator) { this.tcpConfig = tcpConfig; this.maxMessageSize = maxMessageSize; - sslHelper = new ServerSslHelper(sslConfig); + this.streamCreator = streamCreator; this.bufferPool = bufferPool; handShake = new ServerHandshake(this.bufferPool, handshakeMaxSize); } @@ -105,7 +105,7 @@ void HandshakeAndReceiveLoop(Connection conn) { try { - bool success = sslHelper.TryCreateStream(conn); + bool success = streamCreator.TryCreateStream(conn); if (!success) { Log.Warn($"[SWT-WebSocketServer]: Failed to create SSL Stream {conn}"); diff --git a/Assets/Mirror/Transports/SimpleWeb/SimpleWebTransport.cs b/Assets/Mirror/Transports/SimpleWeb/SimpleWebTransport.cs index 5990bc3e807..63954875a6e 100644 --- a/Assets/Mirror/Transports/SimpleWeb/SimpleWebTransport.cs +++ b/Assets/Mirror/Transports/SimpleWeb/SimpleWebTransport.cs @@ -1,6 +1,5 @@ using System; using System.Net; -using System.Security.Authentication; using UnityEngine; using UnityEngine.Serialization; @@ -13,7 +12,8 @@ public class SimpleWebTransport : Transport, PortTransport public const string NormalScheme = "ws"; public const string SecureScheme = "wss"; - [Tooltip("Protect against allocation attacks by keeping the max message size small. Otherwise an attacker might send multiple fake packets with 2GB headers, causing the server to run out of memory after allocating multiple large packets.")] + [Tooltip( + "Protect against allocation attacks by keeping the max message size small. Otherwise an attacker might send multiple fake packets with 2GB headers, causing the server to run out of memory after allocating multiple large packets.")] public int maxMessageSize = 16 * 1024; [FormerlySerializedAs("handshakeMaxSize")] @@ -21,11 +21,13 @@ public class SimpleWebTransport : Transport, PortTransport public int maxHandshakeSize = 3000; [FormerlySerializedAs("serverMaxMessagesPerTick")] - [Tooltip("Caps the number of messages the server will process per tick. Allows LateUpdate to finish to let the reset of unity continue in case more messages arrive before they are processed")] + [Tooltip( + "Caps the number of messages the server will process per tick. Allows LateUpdate to finish to let the reset of unity continue in case more messages arrive before they are processed")] public int serverMaxMsgsPerTick = 10000; [FormerlySerializedAs("clientMaxMessagesPerTick")] - [Tooltip("Caps the number of messages the client will process per tick. Allows LateUpdate to finish to let the reset of unity continue in case more messages arrive before they are processed")] + [Tooltip( + "Caps the number of messages the client will process per tick. Allows LateUpdate to finish to let the reset of unity continue in case more messages arrive before they are processed")] public int clientMaxMsgsPerTick = 1000; [Tooltip("Send would stall forever if the network is cut off during a send, so we need a timeout (in milliseconds)")] @@ -37,69 +39,38 @@ public class SimpleWebTransport : Transport, PortTransport [Tooltip("disables nagle algorithm. lowers CPU% and latency but increases bandwidth")] public bool noDelay = true; - [Header("Obsolete SSL settings")] - - [Tooltip("Requires wss connections on server, only to be used with SSL cert.json, never with reverse proxy.\nNOTE: if sslEnabled is true clientUseWss is forced true, even if not checked.")] - public bool sslEnabled; - - [Tooltip("Protocols that SSL certificate is created to support.")] - public SslProtocols sslProtocols = SslProtocols.Tls12; - - [Tooltip("Path to json file that contains path to cert and its password\nUse Json file so that cert password is not included in client builds\nSee Assets/Mirror/Transports/.cert.example.Json")] - public string sslCertJson = "./cert.json"; - [Header("Server settings")] - [Tooltip("Port to use for server")] public ushort port = 7778; - public ushort Port - { - get - { -#if UNITY_WEBGL - if (clientWebsocketSettings.ClientPortOption == WebsocketPortOption.SpecifyPort) - return clientWebsocketSettings.CustomClientPort; - else - return port; -#else - return port; -#endif - } - set - { -#if UNITY_WEBGL - if (clientWebsocketSettings.ClientPortOption == WebsocketPortOption.SpecifyPort) - clientWebsocketSettings.CustomClientPort = value; - else - port = value; -#else - port = value; -#endif - } - } + + public TransportSecurity transportSecurity; [Tooltip("Groups messages in queue before calling Stream.Send")] public bool batchSend = true; [Tooltip("Waits for 1ms before grouping and sending messages.\n" + - "This gives time for mirror to finish adding message to queue so that less groups need to be made.\n" + - "If WaitBeforeSend is true then BatchSend Will also be set to true")] + "This gives time for mirror to finish adding message to queue so that less groups need to be made.\n" + + "If WaitBeforeSend is true then BatchSend Will also be set to true")] public bool waitBeforeSend = true; [Header("Client settings")] - - [Tooltip("Sets connect scheme to wss. Useful when client needs to connect using wss when TLS is outside of transport.\nNOTE: if sslEnabled is true clientUseWss is also true")] + [Tooltip( + "Sets connect scheme to wss. Useful when client needs to connect using wss when TLS is outside of transport.\nNOTE: if sslEnabled is true clientUseWss is also true")] public bool clientUseWss; + public ClientWebsocketSettings clientWebsocketSettings; [Header("Logging")] - [Tooltip("Choose minimum severity level for logging\nFlood level requires Debug build")] - [SerializeField] Log.Levels minimumLogLevel = Log.Levels.Warn; + [SerializeField] + private Log.Levels minimumLogLevel = Log.Levels.Warn; + + private SimpleWebClient client; + private SimpleWebServer server; /// - /// Gets _logLevels field - /// Sets _logLevels and Log.level fields + /// Gets _logLevels field + /// Sets _logLevels and Log.level fields /// public Log.Levels LogLevels { @@ -111,23 +82,40 @@ public Log.Levels LogLevels } } - SimpleWebClient client; - SimpleWebServer server; + private TcpConfig TcpConfig => new(noDelay, sendTimeout, receiveTimeout); - TcpConfig TcpConfig => new TcpConfig(noDelay, sendTimeout, receiveTimeout); + private void Awake() => Log.minLogLevel = minimumLogLevel; - void Awake() + private void OnValidate() => Log.minLogLevel = minimumLogLevel; + + public ushort Port { - Log.minLogLevel = minimumLogLevel; + get + { + #if UNITY_WEBGL + if (clientWebsocketSettings.ClientPortOption == WebsocketPortOption.SpecifyPort) + return clientWebsocketSettings.CustomClientPort; + else + return port; + #else + return port; + #endif + } + set + { + #if UNITY_WEBGL + if (clientWebsocketSettings.ClientPortOption == WebsocketPortOption.SpecifyPort) + clientWebsocketSettings.CustomClientPort = value; + else + port = value; + #else + port = value; + #endif + } } public override string ToString() => $"SWT [{port}]"; - void OnValidate() - { - Log.minLogLevel = minimumLogLevel; - } - public override bool Available() => true; public override int GetMaxPacketSize(int channelId = 0) => maxMessageSize; @@ -142,20 +130,18 @@ public override void Shutdown() #region Client - string GetClientScheme() => (sslEnabled || clientUseWss) ? SecureScheme : NormalScheme; + private string GetClientScheme() => transportSecurity && transportSecurity.enabled ? SecureScheme : clientUseWss ? SecureScheme : NormalScheme; - public override bool ClientConnected() - { + public override bool ClientConnected() => // not null and not NotConnected (we want to return true if connecting or disconnecting) - return client != null && client.ConnectionState != ClientState.NotConnected; - } + client != null && client.ConnectionState != ClientState.NotConnected; public override void ClientConnect(string hostname) { - UriBuilder builder = new UriBuilder + var builder = new UriBuilder { Scheme = GetClientScheme(), - Host = hostname, + Host = hostname }; switch (clientWebsocketSettings.ClientPortOption) @@ -198,7 +184,7 @@ public override void ClientConnect(Uri uri) client = null; }; - client.onData += (ArraySegment data) => OnClientDataReceived.Invoke(data, Channels.Reliable); + client.onData += data => OnClientDataReceived.Invoke(data, Channels.Reliable); // We will not invoke OnClientError if minLogLevel is set to None // We only send the full exception if minLogLevel is set to Verbose @@ -206,7 +192,7 @@ public override void ClientConnect(Uri uri) { case Log.Levels.Flood: case Log.Levels.Verbose: - client.onError += (Exception e) => + client.onError += e => { OnClientError.Invoke(TransportError.Unexpected, e.ToString()); ClientDisconnect(); @@ -215,7 +201,7 @@ public override void ClientConnect(Uri uri) case Log.Levels.Info: case Log.Levels.Warn: case Log.Levels.Error: - client.onError += (Exception e) => + client.onError += e => { OnClientError.Invoke(TransportError.Unexpected, e.Message); ClientDisconnect(); @@ -226,11 +212,9 @@ public override void ClientConnect(Uri uri) client.Connect(uri); } - public override void ClientDisconnect() - { + public override void ClientDisconnect() => // don't set client null here of messages wont be processed client?.Disconnect(); - } public override void ClientSend(ArraySegment segment, int channelId) { @@ -259,20 +243,17 @@ public override void ClientSend(ArraySegment segment, int channelId) } // messages should always be processed in early update - public override void ClientEarlyUpdate() - { - client?.ProcessMessageQueue(this); - } + public override void ClientEarlyUpdate() => client?.ProcessMessageQueue(this); #endregion #region Server - string GetServerScheme() => sslEnabled ? SecureScheme : NormalScheme; + private string GetServerScheme() => transportSecurity && transportSecurity.enabled ? SecureScheme : NormalScheme; public override Uri ServerUri() { - UriBuilder builder = new UriBuilder + var builder = new UriBuilder { Scheme = GetServerScheme(), Host = Dns.GetHostName(), @@ -281,22 +262,27 @@ public override Uri ServerUri() return builder.Uri; } - public override bool ServerActive() - { - return server != null && server.Active; - } + public override bool ServerActive() => server != null && server.Active; public override void ServerStart() { if (ServerActive()) Log.Warn("[SWT-ServerStart]: Server Already Started"); - SslConfig config = SslConfigLoader.Load(sslEnabled, sslCertJson, sslProtocols); + SslConfig config; + if (transportSecurity && transportSecurity.enabled) + { + config = transportSecurity.GetSslSettings(); + } + else + { + config = new SslConfig(false, "", "", System.Security.Authentication.SslProtocols.None); + } server = new SimpleWebServer(serverMaxMsgsPerTick, TcpConfig, maxMessageSize, maxHandshakeSize, config); server.onConnect += OnServerConnected.Invoke; server.onDisconnect += OnServerDisconnected.Invoke; - server.onData += (int connId, ArraySegment data) => OnServerDataReceived.Invoke(connId, data, Channels.Reliable); + server.onData += (connId, data) => OnServerDataReceived.Invoke(connId, data, Channels.Reliable); // We will not invoke OnServerError if minLogLevel is set to None // We only send the full exception if minLogLevel is set to Verbose @@ -373,11 +359,9 @@ public override void ServerSend(int connectionId, ArraySegment segment, in public Request ServerGetClientRequest(int connectionId) => server.GetClientRequest(connectionId); // messages should always be processed in early update - public override void ServerEarlyUpdate() - { - server?.ProcessMessageQueue(this); - } + public override void ServerEarlyUpdate() => server?.ProcessMessageQueue(this); #endregion + } }