From a5aa411b6035635903e6d7f1cf8a134e519a2e6f Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Sun, 16 Aug 2020 18:58:39 -0700 Subject: [PATCH] Added support for websocket deflate for mapped connections. --- KVMControl.cs | 12 +- KVMViewer.cs | 6 +- MeshMapper.cs | 599 +++++++++------------------------------------ WebSocketClient.cs | 42 ++-- 4 files changed, 151 insertions(+), 508 deletions(-) diff --git a/KVMControl.cs b/KVMControl.cs index bc6206f..42b729f 100644 --- a/KVMControl.cs +++ b/KVMControl.cs @@ -502,15 +502,15 @@ namespace MeshCentralRouter public void Send(BinaryWriter bw) { //if (state == ConnectState.Disconnected) { RecycleBinaryWriter(bw); return; } - //try - //{ if ((parent != null) && (parent.wc != null)) { - parent.wc.SendBinary(((MemoryStream)bw.BaseStream).GetBuffer(), 0, (int)((MemoryStream)bw.BaseStream).Length); - bytesent += (int)((MemoryStream)bw.BaseStream).Length; + try + { + parent.wc.SendBinary(((MemoryStream)bw.BaseStream).GetBuffer(), 0, (int)((MemoryStream)bw.BaseStream).Length); + bytesent += (int)((MemoryStream)bw.BaseStream).Length; + } + catch (Exception) { } } - //} - //catch (Exception) { } RecycleBinaryWriter(bw); } diff --git a/KVMViewer.cs b/KVMViewer.cs index 4873b57..e48a464 100644 --- a/KVMViewer.cs +++ b/KVMViewer.cs @@ -101,7 +101,7 @@ namespace MeshCentralRouter wc.Start(u, server.wshash); } - private void Wc_onStateChanged(webSocketClient.ConnectionStates wsstate) + private void Wc_onStateChanged(webSocketClient sender, webSocketClient.ConnectionStates wsstate) { switch (wsstate) { @@ -131,7 +131,7 @@ namespace MeshCentralRouter UpdateStatus(); } - private void Wc_onStringData(string data) + private void Wc_onStringData(webSocketClient sender, string data, int orglen) { if ((state == 2) && ((data == "c") || (data == "cr"))) { @@ -181,7 +181,7 @@ namespace MeshCentralRouter } } - private void Wc_onBinaryData(byte[] data, int offset, int length) + private void Wc_onBinaryData(webSocketClient sender, byte[] data, int offset, int length, int orglen) { if (state != 3) return; kvmControl.ProcessData(data, offset, length); diff --git a/MeshMapper.cs b/MeshMapper.cs index 9e4065c..eda3b94 100644 --- a/MeshMapper.cs +++ b/MeshMapper.cs @@ -31,6 +31,12 @@ namespace MeshCentralRouter public bool inaddrany = false; TcpListener listener = null; + // Stats + public long bytesToServer = 0; + public long bytesToClient = 0; + public long bytesToServerCompressed = 0; + public long bytesToClientCompressed = 0; + public delegate void onStateMsgChangedHandler(string statemsg); public event onStateMsgChangedHandler onStateMsgChanged; @@ -145,7 +151,7 @@ namespace MeshCentralRouter UpdateInfo(); } - private void ShutdownClients(TcpClient c1, UdpClient c2, xwebclient c3, int counter) + private void ShutdownClients(TcpClient c1, UdpClient c2, webSocketClient c3, int counter) { Debug("#" + counter + ": ShutdownWebClients()"); @@ -193,535 +199,164 @@ namespace MeshCentralRouter private void ConnectWS(TcpClient client, int counter) { - xwebclient wc = new xwebclient(); + webSocketClient wc = new webSocketClient(); Debug("#" + counter + ": Connecting web socket to: " + wsurl.ToString()); - wc.Start(this, wsurl, certhash, client, counter); + wc.Start(wsurl, certhash); + wc.tag = client; + wc.id = counter; + wc.tunneling = false; + wc.onStateChanged += Wc_onStateChanged; + wc.onBinaryData += Wc_onBinaryData; + wc.onStringData += Wc_onStringData; } private void ConnectWS(UdpClient client, int counter) { - xwebclient wc = new xwebclient(); + webSocketClient wc = new webSocketClient(); Debug("#" + counter + ": Connecting web socket to: " + wsurl.ToString()); - wc.Start(this, wsurl, certhash, client, counter); + wc.Start(wsurl, certhash); + wc.tag = client; + wc.id = counter; + wc.tunneling = false; + wc.onStateChanged += Wc_onStateChanged; + wc.onBinaryData += Wc_onBinaryData; + wc.onStringData += Wc_onStringData; } - private class xwebclient : IDisposable + private void Wc_onStateChanged(webSocketClient sender, webSocketClient.ConnectionStates state) { - private TcpClient client = null; - private UdpClient uclient = null; - private NetworkStream stream = null; - private TcpClient wsclient = null; - private SslStream wsstream = null; - private NetworkStream wsrawstream = null; - private int state = 0; - private Uri url = null; - private byte[] readBuffer = new Byte[65000]; - private int readBufferLen = 0; - private int accopcodes = 0; - private bool accmask = false; - private int acclen = 0; - private bool tunneling = false; - private MeshMapper parent = null; - private bool proxyInUse = false; - public int counter = 0; - private string certhash; - private IPEndPoint uendpoint = null; + Debug("#" + sender.id + ": Websocket mapping, connected to server."); + } - public void Dispose() + private void Wc_onStringData(webSocketClient sender, string data, int orglen) + { + bytesToClient += data.Length; + bytesToClientCompressed += orglen; + if ((sender.tunneling == false) &&( (data == "c") || (data == "cr"))) { - if (state == 0) return; - state = 0; - try { wsstream.Close(); } catch (Exception) { } - try { wsstream.Dispose(); } catch (Exception) { } - try { client.Close(); } catch (Exception) { } - try { stream.Close(); } catch (Exception) { } - stream = null; - client = null; - wsstream = null; - wsclient = null; - --parent.totalConnectCounter; - parent.UpdateInfo(); - } + Debug("#" + sender.id + ": Websocket got server 'c' confirmation."); - public bool Start(MeshMapper parent, Uri url, string certhash, TcpClient client, int counter) - { - this.client = client; - return StartEx(parent, url, certhash, counter); - } + // Server confirmed connection, start reading the TCP stream + //Console.Write("WS-Relay Connect\r\n"); - public bool Start(MeshMapper parent, Uri url, string certhash, UdpClient client, int counter) - { - this.uclient = client; - return StartEx(parent, url, certhash, counter); - } - - public bool StartEx(MeshMapper parent, Uri url, string certhash, int counter) - { - if (state != 0) return false; - state = 1; - this.parent = parent; - this.url = url; - this.counter = counter; - this.certhash = certhash; - if (client != null) { this.stream = client.GetStream(); } - Uri proxyUri = null; - ++parent.totalConnectCounter; - parent.UpdateInfo(); - - // Check if we need to use a HTTP proxy (Auto-proxy way) - try + if (sender.tag == null) return; + if (sender.tag.GetType() == typeof(TcpClient)) { - RegistryKey registryKey = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true); - Object x = registryKey.GetValue("AutoConfigURL", null); - if ((x != null) && (x.GetType() == typeof(string))) - { - string proxyStr = GetProxyForUrlUsingPac("http" + ((url.Port == 80) ? "" : "s") + "://" + url.Host + ":" + url.Port, x.ToString()); - if (proxyStr != null) { proxyUri = new Uri("http://" + proxyStr); } - } - } - catch (Exception) { proxyUri = null; } - - // Check if we need to use a HTTP proxy (Normal way) - if (proxyUri == null) - { - var proxy = System.Net.HttpWebRequest.GetSystemWebProxy(); - proxyUri = proxy.GetProxy(url); - if ((url.Host.ToLower() == proxyUri.Host.ToLower()) && (url.Port == proxyUri.Port)) { proxyUri = null; } - } - - if (proxyUri != null) - { - // Proxy in use - proxyInUse = true; - wsclient = new TcpClient(); - wsclient.BeginConnect(proxyUri.Host, proxyUri.Port, new AsyncCallback(OnConnectSink), this); - } - else - { - // No proxy in use - proxyInUse = false; - wsclient = new TcpClient(); - wsclient.BeginConnect(url.Host, url.Port, new AsyncCallback(OnConnectSink), this); - } - return true; - } - - private void OnConnectSink(IAsyncResult ar) - { - if (wsclient == null) return; - - // Accept the connection - try - { - wsclient.EndConnect(ar); - } - catch (Exception ex) - { - parent.Debug("#" + counter + ": Websocket TCP failed to connect: " + ex.ToString()); - parent.ShutdownClients(client, uclient, this, this.counter); - return; - } - - if (proxyInUse == true) - { - // Send proxy connection request - wsrawstream = wsclient.GetStream(); - byte[] proxyRequestBuf = UTF8Encoding.UTF8.GetBytes("CONNECT " + url.Host + ":" + url.Port + " HTTP/1.1\r\nHost: " + url.Host + ":" + url.Port + "\r\n\r\n"); - wsrawstream.Write(proxyRequestBuf, 0, proxyRequestBuf.Length); - wsrawstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnProxyResponseSink), this); - } - else - { - // Start TLS connection - parent.Debug("#" + counter + ": Websocket TCP connected, doing TLS..."); - wsstream = new SslStream(wsclient.GetStream(), false, VerifyServerCertificate, null); - wsstream.BeginAuthenticateAsClient(url.Host, null, System.Security.Authentication.SslProtocols.Tls12, false, new AsyncCallback(OnTlsSetupSink), this); - } - } - - private void OnProxyResponseSink(IAsyncResult ar) - { - if (wsrawstream == null) return; - - int len = 0; - try { len = wsrawstream.EndRead(ar); } catch (Exception) { } - if (len == 0) - { - // Disconnect - parent.Debug("#" + counter + ": Websocket proxy disconnected, length = 0."); - parent.ShutdownClients(client, uclient, this, this.counter); - return; - } - - readBufferLen += len; - string proxyResponse = UTF8Encoding.UTF8.GetString(readBuffer, 0, readBufferLen); - if (proxyResponse.IndexOf("\r\n\r\n") >= 0) - { - // We get a full proxy response, we should get something like "HTTP/1.1 200 Connection established\r\n\r\n" - if (proxyResponse.StartsWith("HTTP/1.1 200 ")) - { - // All good, start TLS setup. - readBufferLen = 0; - parent.Debug("#" + counter + ": Websocket TCP connected, doing TLS..."); - wsstream = new SslStream(wsrawstream, false, VerifyServerCertificate, null); - wsstream.BeginAuthenticateAsClient(url.Host, null, System.Security.Authentication.SslProtocols.Tls12, false, new AsyncCallback(OnTlsSetupSink), this); - } - else - { - // Invalid response - parent.Debug("#" + counter + ": Proxy connection failed: " + proxyResponse); - parent.ShutdownClients(client, uclient, this, this.counter); - } - } - else - { - if (readBufferLen == readBuffer.Length) - { - // Buffer overflow - parent.Debug("#" + counter + ": Proxy connection failed"); - parent.ShutdownClients(client, uclient, this, this.counter); - } - else - { - // Read more proxy data - wsrawstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnProxyResponseSink), this); - } - } - } - - private void OnTlsSetupSink(IAsyncResult ar) - { - if (wsstream == null) return; - - // Accept the connection - try - { - wsstream.EndAuthenticateAsClient(ar); - } - catch (Exception ex) - { - // Disconnect - MessageBox.Show(ex.Message, Properties.Resources.MeshCentralRouter); - parent.Debug("#" + counter + ": Websocket TLS failed: " + ex.ToString()); - parent.ShutdownClients(client, uclient, this, this.counter); - return; - } - - // Send the HTTP header - parent.Debug("#" + counter + ": Websocket TLS setup, sending HTTP header..."); - string header = "GET " + url.PathAndQuery + " HTTP/1.1\r\nHost: " + url.Host + "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n\r\n"; - wsstream.Write(UTF8Encoding.UTF8.GetBytes(header)); - - // Start receiving data - wsstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnTlsDataSink), this); - } - - private void OnTlsDataSink(IAsyncResult ar) - { - if (wsstream == null) return; - - int len = 0; - try { len = wsstream.EndRead(ar); } catch (Exception) { } - if (len == 0) - { - // Disconnect - parent.Debug("#" + counter + ": Websocket disconnected, length = 0."); - parent.ShutdownClients(client, uclient, this, this.counter); - return; - } - //parent.Debug("#" + counter + ": Websocket got new data: " + len); - readBufferLen += len; - - // Consume all of the data - int consumed = 0; - int ptr = 0; - do - { - consumed = ProcessBuffer(readBuffer, ptr, readBufferLen - ptr); - if (consumed < 0) { parent.ShutdownClients(client, uclient, this, this.counter); return; } // Error, close the connection - ptr += consumed; - } while ((consumed > 0) && ((readBufferLen - consumed) > 0)); - - // Move the data forward - if ((ptr > 0) && (readBufferLen - ptr) > 0) - { - //Console.Write("MOVE FORWARD\r\n"); - Array.Copy(readBuffer, ptr, readBuffer, 0, (readBufferLen - ptr)); - } - readBufferLen = (readBufferLen - ptr); - - // Receive more data - try { wsstream.BeginRead(readBuffer, readBufferLen, readBuffer.Length - readBufferLen, new AsyncCallback(OnTlsDataSink), this); } catch (Exception) { } - } - - private int ProcessBuffer(byte[] buffer, int offset, int len) - { - string ss = UTF8Encoding.UTF8.GetString(buffer, offset, len); - - if (state == 1) - { - // Look for the end of the http header - string header = UTF8Encoding.UTF8.GetString(buffer, offset, len); - int i = header.IndexOf("\r\n\r\n"); - if (i == -1) return 0; - Dictionary parsedHeader = ParseHttpHeader(header.Substring(0, i)); - if ((parsedHeader == null) || (parsedHeader["_Path"] != "101")) { parent.Debug("#" + counter + ": Websocket bad header."); return -1; } // Bad header, close the connection - parent.Debug("#" + counter + ": Websocket got setup upgrade header."); - state = 2; - return len; // TODO: Technically we need to return the header length before UTF8 convert. - } - else if (state == 2) - { - // Parse a websocket fragment header - if (len < 2) return 0; - int headsize = 2; - accopcodes = buffer[offset]; - accmask = ((buffer[offset + 1] & 0x80) != 0); - acclen = (buffer[offset + 1] & 0x7F); - - if ((accopcodes & 0x0F) == 8) - { - // Close the websocket - parent.Debug("#" + counter + ": Websocket got closed fragment."); - return -1; - } - - if (acclen == 126) - { - if (len < 4) return 0; - headsize = 4; - acclen = (buffer[offset + 2] << 8) + (buffer[offset + 3]); - } - else if (acclen == 127) - { - if (len < 10) return 0; - headsize = 10; - acclen = (buffer[offset + 6] << 24) + (buffer[offset + 7] << 16) + (buffer[offset + 8] << 8) + (buffer[offset + 9]); - parent.Debug("#" + counter + ": Websocket receive large fragment: " + acclen); - } - if (accmask == true) - { - // TODO: Do unmasking here. - headsize += 4; - } - //parent.Debug("#" + counter + ": Websocket frag header - FIN: " + ((accopcodes & 0x80) != 0) + ", OP: " + (accopcodes & 0x0F) + ", LEN: " + acclen + ", MASK: " + accmask); - state = 3; - return headsize; - } - else if (state == 3) - { - // Parse a websocket fragment data - if (len < acclen) return 0; - //Console.Write("WSREAD: " + acclen + "\r\n"); - ProcessWsBuffer(buffer, offset, acclen, accopcodes); - state = 2; - return acclen; - } - return 0; - } - - private void ProcessWsBuffer(byte[] data, int offset, int len, int op) - { - int opcode = op & 0x0F; // 1 == String, 2 == Binary - - parent.Debug("#" + counter + ": Websocket frag data: " + acclen + ", opcode: " + opcode); - if ((tunneling == false) && (opcode == 1) && (data[offset] == 'c')) - { - parent.Debug("#" + counter + ": Websocket got server 'c' confirmation."); - - // Server confirmed connection, start reading the TCP stream - //Console.Write("WS-Relay Connect\r\n"); - + TcpClient client = (TcpClient)sender.tag; if (client != null) { byte[] buf1 = new byte[65000]; - try { client.GetStream().BeginRead(buf1, 4, buf1.Length - 4, new AsyncCallback(ClientEndReadWS), new object[] { this, client, buf1, counter }); } catch (Exception) { } - tunneling = true; - } - else if (uclient != null) - { - try { uclient.BeginReceive(new AsyncCallback(UClientEndReadWS), new object[] { this, uclient, counter }); } catch (Exception) { } - tunneling = true; + try { client.GetStream().BeginRead(buf1, 0, buf1.Length, new AsyncCallback(ClientEndReadWS), new object[] { this, sender, client, buf1 }); } catch (Exception) { } + sender.tunneling = true; } } - else if ((tunneling == true) && (opcode == 1)) + if (sender.tag.GetType() == typeof(UdpClient)) { - string text = UTF8Encoding.UTF8.GetString(data, offset, len); - parent.Debug("#" + counter + ": Websocket got text frame: " + text); - } - else if ((tunneling == true) && (opcode == 2)) - { - if (client != null) - { - // Write: WS --> TCP - // TODO: Async write? - if (stream != null) { try { stream.Write(data, offset, len); } catch (Exception) { } } - } - else if (uclient != null) - { - // Write: WS --> UDP - if (uendpoint != null) - { - if (offset == 0) - { - try { uclient.Send(data, len, uendpoint); } catch (Exception) { } - } - else - { - byte[] data2 = new byte[len]; - Array.Copy(data, offset, data2, 0, len); - try { uclient.Send(data2, len, uendpoint); } catch (Exception) { } - } - } - } + UdpClient uclient = (UdpClient)sender.tag; + try { uclient.BeginReceive(new AsyncCallback(UClientEndReadWS), new object[] { this, sender, uclient }); } catch (Exception) { } + sender.tunneling = true; } } - - private Dictionary ParseHttpHeader(string header) + else if (sender.tunneling == true) { - string[] lines = header.Replace("\r\n", "\r").Split('\r'); - if (lines.Length < 2) { return null; } - string[] directive = lines[0].Split(' '); - Dictionary values = new Dictionary(); - values["_Action"] = directive[0]; - values["_Path"] = directive[1]; - values["_Protocol"] = directive[2]; - for (int i = 1; i < lines.Length; i++) - { - var j = lines[i].IndexOf(":"); - values[lines[i].Substring(0, j).ToLower()] = lines[i].Substring(j + 1).Trim(); - } - return values; + Debug("#" + sender.id + ": Websocket got text frame: " + data); } + } - private bool VerifyServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) + private void Wc_onBinaryData(webSocketClient sender, byte[] data, int offset, int length, int orglen) + { + bytesToClient += length; + bytesToClientCompressed += orglen; + Debug("#" + sender.id + ": Websocket binary data: " + length); + if ((sender.tunneling == false) || (sender.tag == null)) return; + + if (sender.tag.GetType() == typeof(TcpClient)) { - // Check that the remote certificate is the expected one - return (certificate.GetCertHashString() == certhash); + // Write: WS --> TCP + TcpClient client = (TcpClient)sender.tag; + if (client != null) { try { client.GetStream().Write(data, offset, length); } catch (Exception) { } } } - - public void WriteWebSocketAsync(byte[] buf, int offset, int len, object[] args) + if ((sender.tag.GetType() == typeof(UdpClient)) && (sender.endpoint != null)) { - // Fetch the state - xwebclient wc = (xwebclient)args[0]; - - // Check that everything is ok - if ((state < 2) || (len < 1) || (len > 65535)) { parent.ShutdownClients(client, uclient, wc, counter); return; } - - //Console.Write("Length: " + len + "\r\n"); - //System.Threading.Thread.Sleep(0); - - if (len < 126) + // Write: WS --> UDP + UdpClient uclient = (UdpClient)sender.tag; + if (offset == 0) { - // Small fragment - buf[2] = 130; // Fragment op code (129 = text, 130 = binary) - buf[3] = (byte)(len & 0x7F); - try { wsstream.BeginWrite(buf, 2, len + 2, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { - parent.ShutdownClients(client, uclient, wc, counter); return; } + try { uclient.Send(data, length, sender.endpoint); } catch (Exception) { } } else { - // Large fragment - buf[0] = 130; // Fragment op code (129 = text, 130 = binary) - buf[1] = 126; - buf[2] = (byte)((len >> 8) & 0xFF); - buf[3] = (byte)(len & 0xFF); - try { wsstream.BeginWrite(buf, 0, len + 4, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { parent.ShutdownClients(client, uclient, wc, counter); return; } + byte[] data2 = new byte[length]; + Array.Copy(data, offset, data2, 0, length); + try { uclient.Send(data2, length, sender.endpoint); } catch (Exception) { } } } + } - private void WriteWebSocketAsyncDone(IAsyncResult ar) + + // Read from the local client + private void ClientEndReadWS(IAsyncResult ar) + { + // Fetch the state + object[] args = (object[])ar.AsyncState; + MeshMapper mm = (MeshMapper)args[0]; + webSocketClient wc = (webSocketClient)args[1]; + TcpClient client = (TcpClient)args[2]; + byte[] buf = (byte[])args[3]; + int counter = wc.id; + + int len = 0; + try { - // Fetch the state - object[] args = (object[])ar.AsyncState; - xwebclient wc = (xwebclient)args[0]; - object oclient; - TcpClient client = null; - UdpClient uclient = null; - byte[] buf = null; - int counter; - - if (args.Length == 4) { - oclient = client = (TcpClient)args[1]; - buf = (byte[])args[2]; - counter = (int)args[3]; - } else { - oclient = uclient = (UdpClient)args[1]; - counter = (int)args[2]; - } - - try { wsstream.EndWrite(ar); } catch (Exception) { parent.ShutdownClients(client, uclient, wc, counter); return; } - - if (client != null) { - // Receive more TCP data - try { client.GetStream().BeginRead(buf, 4, buf.Length - 4, new AsyncCallback(ClientEndReadWS), args); } catch (Exception) { parent.ShutdownClients(client, uclient, wc, counter); return; } - } else if (uclient != null) { - // Receive more UDP data - try { uclient.BeginReceive(new AsyncCallback(UClientEndReadWS), args); } catch (Exception) { parent.ShutdownClients(client, uclient, wc, counter); return; } - } + // Read the data + if (client != null && client.Connected == true) { len = client.GetStream().EndRead(ar); } } + catch (Exception) { ShutdownClients(client, null, wc, counter); return; } - // Read from the local client - private void ClientEndReadWS(IAsyncResult ar) + //Debug("#" + counter + ": ClientEndRead(" + len + ") - Local Read"); + if (len > 0) { - // Fetch the state - object[] args = (object[])ar.AsyncState; - xwebclient wc = (xwebclient)args[0]; - TcpClient client = (TcpClient)args[1]; - byte[] buf = (byte[])args[2]; - int counter = (int)args[3]; - - int len = 0; + // Forward the data & read again try { - // Read the data - if (client != null && client.Connected == true) { len = client.GetStream().EndRead(ar); } + mm.bytesToServer += buf.Length; + mm.bytesToServerCompressed += wc.SendBinary(buf, 0, len); // TODO: Do Async + try { client.GetStream().BeginRead(buf, 0, buf.Length, new AsyncCallback(ClientEndReadWS), new object[] { mm, wc, client, buf }); } catch (Exception) { } } - catch (Exception) { parent.ShutdownClients(client, uclient, wc, counter); return; } - - //Debug("#" + counter + ": ClientEndRead(" + len + ") - Local Read"); - if (len > 0) + catch (Exception) { - // Forward the data & read again - try - { - wc.WriteWebSocketAsync(buf, 4, len, args); - } - catch (Exception) - { - parent.ShutdownClients(client, uclient, wc, counter); - return; - } - } - else - { - parent.ShutdownClients(client, uclient, wc, counter); + ShutdownClients(client, null, wc, counter); return; } } - - private void UClientEndReadWS(IAsyncResult ar) + else { - // Fetch the state - object[] args = (object[])ar.AsyncState; - xwebclient wc = (xwebclient)args[0]; - UdpClient uclient = (UdpClient)args[1]; - int counter = (int)args[2]; - - byte[] buf = null; - try - { - // Read the data - if (uclient != null) { buf = uclient.EndReceive(ar, ref uendpoint); } - } - catch (Exception) { parent.ShutdownClients(client, uclient, wc, counter); return; } - - byte[] buf2 = new byte[4 + buf.Length]; - Array.Copy(buf, 0, buf2, 4, buf.Length); - - if (buf != null) { wc.WriteWebSocketAsync(buf2, 4, buf.Length, args); } + ShutdownClients(client, null, wc, counter); + return; } - } + private void UClientEndReadWS(IAsyncResult ar) + { + // Fetch the state + object[] args = (object[])ar.AsyncState; + MeshMapper mm = (MeshMapper)args[0]; + webSocketClient wc = (webSocketClient)args[1]; + UdpClient uclient = (UdpClient)args[2]; + int counter = wc.id; + + byte[] buf = null; + try + { + // Read the data + if (uclient != null) { buf = uclient.EndReceive(ar, ref wc.endpoint); } + } + catch (Exception) { ShutdownClients(null, uclient, wc, counter); return; } + + if (buf != null) { + mm.bytesToServer += buf.Length; + mm.bytesToServerCompressed += wc.SendBinary(buf, 0, buf.Length); // TODO: Do Async + try { uclient.BeginReceive(new AsyncCallback(UClientEndReadWS), new object[] { mm, wc, uclient }); } catch (Exception) { } + } + } } } diff --git a/WebSocketClient.cs b/WebSocketClient.cs index ef434cd..4aa64f7 100644 --- a/WebSocketClient.cs +++ b/WebSocketClient.cs @@ -16,6 +16,7 @@ limitations under the License. using System; using System.IO; +using System.Net; using System.Text; using System.Net.Sockets; using System.Net.Security; @@ -52,6 +53,12 @@ namespace MeshCentralRouter private static byte[] inflateEnd = { 0x00, 0x00, 0xff, 0xff }; private static byte[] inflateStart = { 0x00, 0x00, 0x00, 0x00 }; + // Outside variables + public object tag = null; + public int id = 0; + public bool tunneling = false; + public IPEndPoint endpoint; + public enum ConnectionStates { Disconnected = 0, @@ -64,13 +71,13 @@ namespace MeshCentralRouter NoError = 0 } - public delegate void onBinaryDataHandler(byte[] data, int offset, int length); + public delegate void onBinaryDataHandler(webSocketClient sender, byte[] data, int offset, int length, int orglen); public event onBinaryDataHandler onBinaryData; - public delegate void onStringDataHandler(string data); + public delegate void onStringDataHandler(webSocketClient sender, string data, int orglen); public event onStringDataHandler onStringData; - public delegate void onDebugMessageHandler(string msg); + public delegate void onDebugMessageHandler(webSocketClient sender, string msg); public event onDebugMessageHandler onDebugMessage; - public delegate void onStateChangedHandler(ConnectionStates state); + public delegate void onStateChangedHandler(webSocketClient sender, ConnectionStates state); public event onStateChangedHandler onStateChanged; public ConnectionStates State { get { return state; } } @@ -79,7 +86,7 @@ namespace MeshCentralRouter { if (state == newstate) return; state = newstate; - if (onStateChanged != null) { onStateChanged(state); } + if (onStateChanged != null) { onStateChanged(this, state); } } public void Dispose() @@ -89,7 +96,7 @@ namespace MeshCentralRouter SetState(ConnectionStates.Disconnected); } - public void Debug(string msg) { if (onDebugMessage != null) { onDebugMessage(msg); } if (xdebug) { try { File.AppendAllText("debug.log", "Debug-" + msg + "\r\n"); } catch (Exception) { } } } + public void Debug(string msg) { if (onDebugMessage != null) { onDebugMessage(this, msg); } if (xdebug) { try { File.AppendAllText("debug.log", "Debug-" + msg + "\r\n"); } catch (Exception) { } } } public bool Start(Uri url, string tlsCertFingerprint) { @@ -252,8 +259,6 @@ namespace MeshCentralRouter // Send the HTTP headers Debug("Websocket TLS setup, sending HTTP header..."); - //string header = "GET " + url.PathAndQuery + " HTTP/1.1\r\nHost: " + url.Host + "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\n" + extraHeaders + "\r\n"; - //string header = "GET " + url.PathAndQuery + " HTTP/1.1\r\nHost: " + url.Host + "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover; server_no_context_takeover\r\n" + extraHeaders + "\r\n"; string header = "GET " + url.PathAndQuery + " HTTP/1.1\r\nHost: " + url.Host + "\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Extensions: permessage-deflate; client_no_context_takeover\r\n" + extraHeaders + "\r\n"; wsstream.Write(UTF8Encoding.UTF8.GetBytes(header)); @@ -388,6 +393,7 @@ namespace MeshCentralRouter private void ProcessWsBuffer(byte[] data, int offset, int len, int op) { + int orglen = len; MemoryStream mem = null; if (((op & 0x40) != 0) && (inflateMemory != null)) { @@ -406,11 +412,11 @@ namespace MeshCentralRouter if ((op & 1) == 0) { // This is a birnay frame Debug("Websocket got binary data, len = " + len); - if (onBinaryData != null) { onBinaryData(data, offset, len); } + if (onBinaryData != null) { onBinaryData(this, data, offset, len, orglen); } } else { // This is a string frame Debug("Websocket got string data, len = " + len); - if (onStringData != null) { onStringData(UTF8Encoding.UTF8.GetString(data, offset, len)); } + if (onStringData != null) { onStringData(this, UTF8Encoding.UTF8.GetString(data, offset, len), orglen); } } if (mem != null) { mem.Dispose(); mem = null; } @@ -458,19 +464,19 @@ namespace MeshCentralRouter return ((tlsCertFingerprint == GetMeshKeyHash(certificate)) || (tlsCertFingerprint == certificate.GetCertHashString())); } - public void SendString(string data) + public int SendString(string data) { - if (state != ConnectionStates.Connected) return; + if (state != ConnectionStates.Connected) return 0; byte[] buf = UTF8Encoding.UTF8.GetBytes(data); - SendFragment(buf, 0, buf.Length, 129); + return SendFragment(buf, 0, buf.Length, 129); } - public void SendBinary(byte[] data, int offset, int len) { SendFragment(data, offset, len, 130); } + public int SendBinary(byte[] data, int offset, int len) { return SendFragment(data, offset, len, 130); } // Fragment op code (129 = text, 130 = binary) - public void SendFragment(byte[] data, int offset, int len, byte op) + public int SendFragment(byte[] data, int offset, int len, byte op) { - if (state != ConnectionStates.Connected) return; + if (state != ConnectionStates.Connected) return 0; byte[] buf; // If deflate is active, attempt to compress the data here. @@ -504,7 +510,7 @@ namespace MeshCentralRouter } // Check that everything is ok - if ((len < 1) || (len > 65535)) { Dispose(); return; } + if ((len < 1) || (len > 65535)) { Dispose(); return 0; } //Console.Write("Length: " + len + "\r\n"); //System.Threading.Thread.Sleep(0); @@ -527,6 +533,8 @@ namespace MeshCentralRouter //try { wsstream.BeginWrite(buf, 0, len + 4, new AsyncCallback(WriteWebSocketAsyncDone), args); } catch (Exception) { Dispose(); return; } wsstream.Write(buf, 0, len + 4); } + + return len; } public static string GetProxyForUrlUsingPac(string DestinationUrl, string PacUri)