private void DestinationWrite(Stream destination, byte[] buffer, int count, CommPacketDirection direction) { Application.Current.Dispatcher.Invoke(() => { ProxyUiWindow.WriteBytes(buffer, count, direction, m_connectionInstance); }); TryWriteStream(destination, buffer, 0, count, direction == CommPacketDirection.ServerToClient); }
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { if (Data.ShutdownStarted) { if (!shutdownCompleted) { e.Cancel = true; } return; } e.Cancel = true; Data.ShutdownStarted = true; ProxyUiWindow.CloseInstance(); SettingsWindow.CloseInstance(); ComponentEngine.Instance.Stop().ContinueWith(bla => { Dispatcher.BeginInvoke(new Action(() => { if (bla.Result.Count > 0) { MessageBox.Show($"The following errors occured while trying to restore your system:\r\n{string.Join("\r\n", bla.Result)}", "Shutdown Errors", MessageBoxButton.OK, MessageBoxImage.Warning); } shutdownCompleted = true; Close(); Task.Run(async() => { await Task.Delay(5000); Application.Current.Shutdown(0); }); })); }); }
/// <summary> /// Begins the UDP MITM. RemotePort must be set before calling this /// </summary> /// <returns></returns> public async Task StartAsync() { // Client to send data to the server UdpClient sender = new UdpClient(); // Endpoint representing the local port var endpoint = new IPEndPoint(IPAddress.Any, LocalPort); try { // Local function called when data is received void requestCallback(IAsyncResult ar) { try { // Complete the receive and get the bytes var bytes = m_listener.EndReceive(ar, ref endpoint); // Send the bytes to the server if (RemotePort != 0) { InstanceAdded = true; Application.Current.Dispatcher.Invoke(() => { ProxyUiWindow.SendBytesToUi(m_connectionInstance, new CommPacket { Data = bytes, Direction = CommPacketDirection.ClientToServer, Id = Guid.NewGuid(), Instance = m_connectionInstance, ParentPacket = null, Header = "UDP data", }); }); if (sender.Send(bytes, bytes.Length, RemoteHost, RemotePort) != bytes.Length) { throw new Exception("UDP send failed"); } } // Listen for more bytes m_listener.BeginReceive(requestCallback, null); } catch (Exception ex) { // Terminate the port on failure m_failureSource.TrySetException(ex); } } // Perform the first listen m_listener.BeginReceive(requestCallback, null); } catch (Exception ex) { // Terminate the port on failure m_failureSource.TrySetException(ex); } try { // Wait until failure or termination await m_failureSource.Task; } catch { } }
private void ForwardStreamNew(Stream source, Stream destination, byte[] buffer, CommPacketDirection direction) { var rawVerbs = new string[] { "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT" }; byte[][] verbs = rawVerbs.Select(cur => Encoding.UTF8.GetBytes(cur).Take(2).ToArray()).ToArray(); Regex requestLinePattern = new Regex($"^({string.Join("|", rawVerbs)}) [^ ]+ HTTP/1.1$"); Regex contentLengthPattern = new Regex($"^Content-Length: ([0-9]+)$"); Regex chunkedPattern = new Regex($"^Transfer-Encoding: chunked$"); Regex statusLinePattern = new Regex($"^HTTP/1.1 ([0-9]+) (.*)$"); Regex websocketPlanet = new Regex("GET /([0-9]+)/websocket/game HTTP/1.1"); Regex udpPortPattern = new Regex("\"udpPort\"\\:([0-9]+)\\,"); try { destination = new BufferedStream(destination); } catch (Exception ex) { Log.Error(ex, "Error creating buffered stream"); return; } new Thread(() => { MemoryStream ms = null; try { bool isWebSocket = false; while (true) { try { destination.Flush(); } catch (Exception ex) { Log.Error(ex, "Error Flushing stream"); Kill(direction == CommPacketDirection.ServerToClient); } if (!m_connectionInstance.IsConnectionOpen) { break; } //if (ms != null) //{ // ms.Position = 0; // messagecache[direction].Enqueue(ms); //} //while (messagecache[direction].Count > 10) //{ // messagecache[direction].Dequeue(); //} ms = new MemoryStream(); StreamWriter sw = null; bool ReadBytes(int count) { int offset = 0; while (offset < count) { int bytesRead = 0; try { bytesRead = source.Read(buffer, offset, count - offset); } catch (Exception ex) { Log.Error(ex, "Error Reading stream"); Kill(direction == CommPacketDirection.ClientToServer); } if (bytesRead == 0) { return(false); } offset += bytesRead; } return(true); } string ReadLine() { List <byte> result = new List <byte>(); byte?prevByte = null; while (true) { byte[] readBuffer = new byte[1]; if (source.Read(readBuffer, 0, 1) != 1) { return(null); } result.Add(readBuffer[0]); if (prevByte == '\r' && readBuffer[0] == '\n') { break; } prevByte = readBuffer[0]; } return(Encoding.UTF8.GetString(result.ToArray()).TrimEnd('\r', '\n')); } void DoHttpHeadersContentAndForward() { List <string> headers = new List <string>(); ulong contentLength = 0; bool chunked = false; string curLine = null; while ((curLine = ReadLine()) != null && curLine != string.Empty) { sw.WriteLine(curLine); curLine = curLine.Trim(); Match m = contentLengthPattern.Match(curLine); if (m.Success) { contentLength = Convert.ToUInt64(m.Groups[1].Value); } m = chunkedPattern.Match(curLine); if (m.Success) { chunked = true; } headers.Add(curLine.ToLower()); } sw.WriteLine(); sw.Flush(); if (curLine == null) { throw new Exception("HTTP unexpected end of stream while reading headers"); } if (chunked && contentLength > 0) { throw new Exception("Chunked content with length not supported"); } void DoReadLength(ulong curLength) { while (curLength > 0) { int bytesRead = 0; try { bytesRead = source.Read(buffer, 0, Math.Min(buffer.Length, (int)Math.Min(int.MaxValue, curLength))); } catch (Exception ex) { Log.Error(ex, "Error reading bytes"); Kill(direction == CommPacketDirection.ClientToServer); } if (bytesRead == 0) { throw new Exception("HTTP unexpected end of stream while reading content"); } ms.Write(buffer, 0, bytesRead); curLength -= (ulong)bytesRead; } } DoReadLength(contentLength); if (chunked) { bool lastChunk = false; while (!lastChunk && (curLine = ReadLine()) != null && curLine != string.Empty) { sw.WriteLine(curLine); sw.Flush(); var length = ulong.Parse(curLine.Trim()); if (length > 0) { DoReadLength(length); } else { lastChunk = true; } curLine = ReadLine(); if (curLine == null || curLine.Length != 0) { throw new Exception("HTTP protocol failure"); } sw.WriteLine(); sw.Flush(); } } ms.Position = 0; if (!headers.Contains("content-type: application/json".ToLower())) { DestinationWrite(destination, ms.ToArray(), (int)ms.Length, direction); } else { var orgLength = ms.Length; string entireMessage = new StreamReader(ms).ReadToEnd(); ForwardHttpData(destination, entireMessage, direction); } } void forwardWebsocketFrame() { WsFrame frame = null; try { frame = new WsFrame(buffer, 2, source); } catch (Exception ex) { Log.Error(ex, "Error creating WsFrame"); } var worldData = frame?.Messages.FirstOrDefault(cur => cur.ApiId.HasValue && cur.ApiId.Value == 0); if (worldData != null) { string theJson = Encoding.UTF8.GetString(worldData.Buffer, 0, worldData.Buffer.Length); Match m = udpPortPattern.Match(theJson); if (m.Success && planetStringName != null) { int serverPort = Convert.ToInt32(m.Groups[1].Value); if (serverPort.ToString().Length != 4) { throw new Exception("Length change of udpPort"); } if (!planetPorts.ContainsKey(planetStringName)) { //throw new Exception($"Planet dictionary does not contain {planetStringName}"); } else { planetPorts[planetStringName].RemotePort = serverPort; theJson = udpPortPattern.Replace(theJson, $"\"udpPort\":{planetPorts[planetStringName].LocalPort},"); byte[] sendData = Encoding.UTF8.GetBytes(theJson); if (sendData.Length != worldData.Buffer.Length) { throw new Exception("JSON length error"); } worldData.Buffer = sendData; } } } if (planetStringName != null) { frame?.Messages.AddRange(GetOutgoingMessages(planetStringName, direction)); } try { frame?.Send(destination); } catch (Exception ex) { Log.Error(ex, "Error sending frame"); Kill(direction == CommPacketDirection.ServerToClient); } //if (frame.readStream.Length != frame.writeStream.Length) //{ // throw new Exception("frame length mismatch."); //} //if (!frame.readStream.ToArray().SequenceEqual(frame.writeStream.ToArray())) //{ // throw new Exception("frame data mismatch."); //} if (frame == null) { frame = new WsFrame() { Messages = new List <WsMessage> { new WsMessage(24, null, Encoding.UTF8.GetBytes("Frame decoding failure!")), }, }; } try { websocketDataQueue[direction].Add(frame); } catch (Exception ex) { Log.Error(ex, "Error adding frame to queue"); } Application.Current.Dispatcher.Invoke(() => { ProxyUiWindow.AddFrame(frame, direction, m_connectionInstance); }); } if (!ReadBytes(2)) { // Connection terminated waiting for new message. This is fine. break; } if (direction == CommPacketDirection.ClientToServer) { if (!verbs.Any(cur => cur[0] == buffer[0] && cur[1] == buffer[1]) && !isWebSocket) { isWebSocket = true; ProxyManagerWindow.Instance.Dispatcher.BeginInvoke(new Action(() => { m_connectionInstance.Parent.Instances.Remove(m_connectionInstance); var wsg = ComponentEngine.Instance.GetComponent <TcpComponent>().websocketGroup; m_connectionInstance.Parent = wsg; wsg.Instances.Add(m_connectionInstance); })); } if (!isWebSocket) { // GET /index.html HTTP/1.1 sw = new StreamWriter(ms); string requestLine = (Encoding.UTF8.GetString(buffer, 0, 2) + ReadLine()).Trim(); sw.WriteLine(requestLine); if (!requestLinePattern.IsMatch(requestLine)) { throw new Exception("HTTP request line invalid"); } Match m = websocketPlanet.Match(requestLine); if (m.Success) { var newVal = Convert.ToInt32(m.Groups[1].Value); if (planetId != -1) { if (planetId != newVal) { throw new Exception("Multiple planets detected on a single stream"); } } else { planetId = newVal; planetStringName = planetLookup.Where(cur => cur.Value.Value == newVal).FirstOrDefault().Key; planetDisplayName = planetLookup[planetStringName].Key; } } DoHttpHeadersContentAndForward(); } else { forwardWebsocketFrame(); } } else { if ((buffer[0] != 'H' || buffer[1] != 'T') && !isWebSocket) { isWebSocket = true; ProxyManagerWindow.Instance.Dispatcher.BeginInvoke(new Action(() => { m_connectionInstance.Parent.Instances.Remove(m_connectionInstance); var wsg = ComponentEngine.Instance.GetComponent <TcpComponent>().websocketGroup; m_connectionInstance.Parent = wsg; wsg.Instances.Add(m_connectionInstance); })); } if (!isWebSocket) { // HTTP/1.1 200 OK sw = new StreamWriter(ms); string statusLine = (Encoding.UTF8.GetString(buffer, 0, 2) + ReadLine()).Trim(); sw.WriteLine(statusLine); if (!statusLinePattern.IsMatch(statusLine)) { throw new Exception("HTTP status line invalid"); } DoHttpHeadersContentAndForward(); } else { forwardWebsocketFrame(); } } } Log.Information("Killing connection"); Kill(direction == CommPacketDirection.ClientToServer); } catch (Exception ex) { Log.Error(ex, "Forward Stream error"); Kill(direction == CommPacketDirection.ClientToServer); } }).Start(); }