public ForwardingConnection(TcpConnectionForwarder forwarder, TcpClient client, BigInteger conId, IPAddress addressForConnect, IPAddress addressForBind, Func <SemaphoreSlim, Task> waitForSending, Func <SemaphoreSlim, Task> waitForReceiving, SemaphoreSlim releaseConnectionSemaphore) { this.forwarder = forwarder; this.client = client; this.conId = conId; this.addressForConnect = addressForConnect; this.addressForBind = addressForBind; this.waitForSending = waitForSending; this.waitForReceiving = waitForReceiving; this.localRemoteIPEndpoint = ((IPEndPoint)client.Client.RemoteEndPoint); this.localLocalIPEndpoint = ((IPEndPoint)client.Client.LocalEndPoint); this.releaseConnectionSemaphore = releaseConnectionSemaphore; // Note: Setting client.SendTimeout has no effect in our case because it only applies to synchronous Write() calls. // To implement a send timeout, we implement something that will close the destination socket after a specific time // if the write call did not finish in that time, so that the Write() or Read() method of the stream will throw an exception. // Currently, this is implemented by the AsyncTimeoutHelper class which uses Semaphores in a separate Task to decide if the // socket should be closed. }
private void startButton_Click(object sender, RoutedEventArgs e) { try { string remoteHost = textHost.Text; int remotePort = int.Parse(textPortRemote.Text, CultureInfo.InvariantCulture); int localPort = int.Parse(textPortLocal.Text, CultureInfo.InvariantCulture); forwarder = new TcpConnectionForwarder( new Random().Next(), remoteHost, remotePort, localPort, sslHost, TcpConnectionForwarderUtils.sslProtocols, serverSslCertificate, TcpConnectionForwarderUtils.sslProtocols, 10000 /* TODO: let the user specify this number */); UpdownValueChanged(false); UpdownValueChanged(true); forwarder.ConnectionAccepted += con => { ConnectionData data = null; // used by the GUI thread // Add events to the connection con.LocalConnectionAuthenticated += (protocol, cipherAlgorithmType, cipherAlgorithmStrength, hashAlgorithmType, hashAlgorithmStrength, exchangeAlgorithmType, exchangeAlgorithmStrength) => Dispatcher.Invoke(new Action(() => con_LocalConnectionAuthenticated(data, protocol, cipherAlgorithmType, cipherAlgorithmStrength, hashAlgorithmType, hashAlgorithmStrength, exchangeAlgorithmType, exchangeAlgorithmStrength))); con.RemoteConnectionEstablished += (usedIpv6, remoteLocalEndpoint, remoteRemoteEndpoint) => Dispatcher.Invoke(new Action(() => con_RemoteConnectionEstablished(data, usedIpv6, remoteLocalEndpoint, remoteRemoteEndpoint))); con.RemoteConnectionAuthenticated += (protocol, remoteCertificate, cipherAlgorithmType, cipherAlgorithmStrength, hashAlgorithmType, hashAlgorithmStrength, exchangeAlgorithmType, exchangeAlgorithmStrength) => Dispatcher.Invoke(new Action(() => con_RemoteConnectionAuthenticated(data, protocol, remoteCertificate, cipherAlgorithmType, cipherAlgorithmStrength, hashAlgorithmType, hashAlgorithmStrength, exchangeAlgorithmType, exchangeAlgorithmStrength))); con.LocalConnectionClosed += () => Dispatcher.Invoke(new Action(() => con_LocalConnectionClosed(data))); con.RemoteConnectionClosed += () => Dispatcher.Invoke(new Action(() => con_RemoteConnectionClosed(data))); con.ConnectionClosedCompletely += () => Dispatcher.Invoke(new Action(() => con_ConnectionClosedCompletely(data))); con.ConnectionAborted += (fromClient, ex) => Dispatcher.Invoke(new Action(() => con_ConnectionAborted(data, fromClient, ex))); con.DataReceivedLocal += (buf, offset, count) => { byte[] copy = null; if (logDataContents || logDataToFile) { // Need to copy the byte[] array because when the following Action is processed // it may already be used for other data. // TODO: Don't copy it since we use Invoke() which waits until the method in the GUI thread // has completed. copy = new byte[count]; Array.Copy(buf, offset, copy, 0, count); } Dispatcher.Invoke(new Action(() => con_DataReceivedLocal(data, copy, 0, count))); }; con.DataReceivedRemote += (buf, offset, count) => { byte[] copy = null; if (logDataContents || logDataToFile) { // Need to copy the byte[] array because when the following Action is processed // it may already be used for other data. copy = new byte[count]; Array.Copy(buf, offset, copy, 0, count); } Dispatcher.Invoke(new Action(() => con_DataReceivedRemote(data, copy, 0, count))); }; con.DataForwardedLocal += () => Dispatcher.Invoke(new Action(() => con_DataForwardedLocal(data))); con.DataForwardedRemote += () => Dispatcher.Invoke(new Action(() => con_DataForwardedRemote(data))); Dispatcher.Invoke(new Action(() => { data = new ConnectionData() { Connection = con, LocalLocalEndpoint = con.LocalLocalIPEndpoint, LocalRemoteEndpoint = con.LocalRemoteIPEndpoint }; forwarder_ConnectionAccepted(data); })); }; forwarderTask = forwarder.RunAsync(); if (forwarderTask.IsCompleted) { // Task has already completed - this should mean there was some exception. // Wait for the task to get the exception. forwarderTask.Wait(); } else { // Wrap the task to handle unhandled exceptions. forwarderTask = new Func <Task>(async() => await ExceptionUtils.WrapTaskForHandlingUnhandledExceptions(async() => await forwarderTask))(); } // OK logDataToFile = checkLogToFile.IsChecked.Value; startButton.IsEnabled = textHost.IsEnabled = textPortLocal.IsEnabled = textPortRemote.IsEnabled = false; checkPauseClient.IsEnabled = checkPauseServer.IsEnabled = true; formsHost1.Child.Enabled = formsHost2.Child.Enabled = true; checkLimitSpeedClient.IsEnabled = checkLimitSpeedServer.IsEnabled = true; checkUseSsl.IsEnabled = false; checkUseServerSsl.IsEnabled = false; checkLogToFile.IsEnabled = false; } catch (Exception ex) { if (ExceptionUtils.ShouldExceptionBeRethrown(ex)) { throw; } forwarder = null; forwarderTask = null; MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); } }