diff --git a/MCGalaxy/Network/Sockets.cs b/MCGalaxy/Network/Sockets.cs index 7dcead7c8..e37a428c3 100644 --- a/MCGalaxy/Network/Sockets.cs +++ b/MCGalaxy/Network/Sockets.cs @@ -18,6 +18,10 @@ using System.Net; using System.Net.Sockets; using System.Security.Cryptography; using System.Text; +using System.IO; +using System.Net.Security; +using System.Threading; +using System.Security.Cryptography.X509Certificates; namespace MCGalaxy.Network { @@ -53,7 +57,14 @@ namespace MCGalaxy.Network { } else if (opcode == 'G' && Server.Config.WebClient) { pending.Remove(this); return new WebSocket(this); - } else { + } +#if SECURE_WEBSOCKETS + else if (opcode == 0x16 && Server.Config.WebClient) { + pending.Remove(this); + return new SecureSocket(this); + } +#endif + else { Logger.Log(LogType.UserActivity, "Disconnected {0} (unknown opcode {1})", IP, opcode); Close(); return null; @@ -251,7 +262,7 @@ namespace MCGalaxy.Network { } } - /// Abstracts a WebSocket on top of a TCP socket. + /// Abstracts a WebSocket on top of a socket. public sealed class WebSocket : INetSocket, INetProtocol { readonly INetSocket s; @@ -481,4 +492,100 @@ namespace MCGalaxy.Network { public void Disconnect() { Disconnect(1000); } public override void Close() { s.Close(); } } +#if SECURE_WEBSOCKETS + public sealed class SecureSocket : INetSocket, INetProtocol { + readonly INetSocket raw; + WrapperStream wrapper; + SslStream ssl; + + public SecureSocket(INetSocket socket) { + IP = socket.IP; + raw = socket; + + wrapper = new WrapperStream(); + wrapper.s = this; + + ssl = new SslStream(wrapper); + new Thread(IOThread).Start(); + } + + // Init taken care by underlying socket + public override void Init() { } + public override bool LowLatency { set { raw.LowLatency = value; } } + public override void Close() { raw.Close(); } + public void Disconnect() { Close(); } + + public override void Send(byte[] buffer, bool sync) { ssl.Write(buffer); } + public override void SendLowPriority(byte[] buffer) { ssl.Write(buffer); } + + public int ProcessReceived(byte[] data, int count) { + lock (wrapper.locker) { + for (int i = 0; i < count; i++) { + wrapper.input.Add(data[i]); + } + } + return count; + } + + void IOThread() { + try { + // UGLY HACK I don't know what this file should even contain??? seems you need public and private key + X509Certificate2 cert = new X509Certificate2(Server.Config.SslCertPath, Server.Config.SslCertPass); + ssl.AuthenticateAsServer(cert); + + Server.s.Log(".. reading player packets .."); + byte[] buffer = new byte[4096]; + for (;;) { + int read = ssl.Read(buffer, 0, 4096); + if (read == 0) break; + this.HandleReceived(buffer, read); + } + } catch (Exception ex) { + Logger.LogError("Error reading from secure stream", ex); + } + } + + // UGLY HACK because can't derive from two base classes + sealed class WrapperStream : Stream { + public SecureSocket s; + public readonly object locker = new object(); + public readonly List input = new List(); + + public override bool CanRead { get { return true; } } + public override bool CanSeek { get { return false; } } + public override bool CanWrite { get { return true; } } + + static Exception ex = new NotSupportedException(); + public override void Flush() { } + public override long Length { get { throw ex; } } + public override long Position { get { throw ex; } set { throw ex; } } + public override long Seek(long offset, SeekOrigin origin) { throw ex; } + public override void SetLength(long length) { throw ex; } + + public override int Read(byte[] buffer, int offset, int count) { + // UGLY HACK wait until got some data + for (;;) { + lock (locker) { if (input.Count > 0) break; } + Thread.Sleep(1); + } + + // now actually output the data + lock (locker) { + count = Math.Min(count, input.Count); + for (int i = 0; i < count; i++) { + buffer[offset++] = input[i]; + } + input.RemoveRange(0, count); + } + return count; + } + + public override void Write(byte[] buffer, int offset, int count) { + byte[] data = new byte[count]; + Buffer.BlockCopy(buffer, offset, data, 0, count); + s.raw.Send(data, false); + } + } + } +#endif } diff --git a/MCGalaxy/Server/ServerConfig.cs b/MCGalaxy/Server/ServerConfig.cs index d1c1250b8..d561e819a 100644 --- a/MCGalaxy/Server/ServerConfig.cs +++ b/MCGalaxy/Server/ServerConfig.cs @@ -41,8 +41,13 @@ namespace MCGalaxy { public bool Public = false; [ConfigBool("verify-names", "Server", true)] public bool VerifyNames = true; + [ConfigBool("support-web-client", "Server", true)] public bool WebClient = true; + [ConfigString("ssl-certificate-path", "Other", "", true)] + public string SslCertPath = ""; + [ConfigString("ssl-certificate-password", "Other", "", true)] + public string SslCertPass = ""; [ConfigBool("autoload", "Server", true)] public bool AutoLoadMaps = true;