├── screenshot1.png ├── TSTunnels.UI ├── App.ico ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── Program.cs ├── Win32Api.cs ├── TSTunnels.UI.csproj ├── UI.cs ├── UI.resx └── UI.Designer.cs ├── tools ├── ExportDll.exe ├── ExportDllAttribute.dll └── ExportDll.exe.config ├── README.md ├── TSTunnels ├── Common │ ├── Messages │ │ ├── MessageType.cs │ │ ├── ListenResponse.cs │ │ ├── HelloResponse.cs │ │ ├── HelloRequest.cs │ │ ├── AcceptRequest.cs │ │ ├── StreamData.cs │ │ ├── ConnectResponse.cs │ │ ├── ListenRequest.cs │ │ ├── ChannelMessage.cs │ │ ├── ConnectRequest.cs │ │ └── StreamError.cs │ ├── IStreamServer.cs │ ├── TcpListenerHelper.cs │ └── StreamPump.cs ├── Server │ ├── MessageLoggedEventArgs.cs.cs │ ├── ConnectedEventArgs.cs.cs │ ├── ForwardedPortEventArgs.cs │ ├── ForwardedPort.cs │ ├── WtsApi32.cs │ └── Server.cs ├── Client │ ├── Program.cs │ ├── WtsApi32.cs │ └── Client.cs ├── Properties │ └── AssemblyInfo.cs └── TSTunnels.csproj └── TSTunnels.sln /screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/normanr/tstunnels/HEAD/screenshot1.png -------------------------------------------------------------------------------- /TSTunnels.UI/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/normanr/tstunnels/HEAD/TSTunnels.UI/App.ico -------------------------------------------------------------------------------- /tools/ExportDll.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/normanr/tstunnels/HEAD/tools/ExportDll.exe -------------------------------------------------------------------------------- /tools/ExportDllAttribute.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/normanr/tstunnels/HEAD/tools/ExportDllAttribute.dll -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Remote Desktop Tunnels - Network Port Redirection 2 | Allows you to port forward with Remote Desktop, in the same way that ssh has been able to for years. 3 | 4 | ## Screen Shot 5 | ![Screenshot](screenshot1.png) 6 | -------------------------------------------------------------------------------- /TSTunnels.UI/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/MessageType.cs: -------------------------------------------------------------------------------- 1 | namespace TSTunnels.Common.Messages 2 | { 3 | public enum MessageType 4 | { 5 | Unknown = 0, 6 | HelloRequest, 7 | HelloResponse, 8 | ConnectRequest, 9 | ConnectResponse, 10 | ListenRequest, 11 | ListenResponse, 12 | AcceptRequest, 13 | StreamData, 14 | StreamError, 15 | } 16 | } -------------------------------------------------------------------------------- /TSTunnels/Server/MessageLoggedEventArgs.cs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TSTunnels.Server 6 | { 7 | public class MessageLoggedEventArgs : EventArgs 8 | { 9 | public string Message { get; private set; } 10 | 11 | public MessageLoggedEventArgs(string Message) 12 | { 13 | this.Message = Message; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TSTunnels/Server/ConnectedEventArgs.cs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TSTunnels.Server 6 | { 7 | public class ConnectedEventArgs : EventArgs 8 | { 9 | public string MachineName { get; private set; } 10 | 11 | public ConnectedEventArgs(string MachineName) 12 | { 13 | this.MachineName = MachineName; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TSTunnels/Server/ForwardedPortEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace TSTunnels.Server 6 | { 7 | public class ForwardedPortEventArgs : EventArgs 8 | { 9 | public ForwardedPort ForwardedPort { get; private set; } 10 | 11 | public ForwardedPortEventArgs(ForwardedPort forwardedPort) 12 | { 13 | ForwardedPort = forwardedPort; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /TSTunnels.UI/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | 5 | namespace TSTunnels.UI 6 | { 7 | static class Program 8 | { 9 | /// 10 | /// The main entry point for the application. 11 | /// 12 | [STAThread] 13 | static void Main() 14 | { 15 | Application.EnableVisualStyles(); 16 | Application.SetCompatibleTextRenderingDefault(false); 17 | Application.Run(new UI()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TSTunnels/Common/IStreamServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | using TSTunnels.Common.Messages; 7 | 8 | namespace TSTunnels.Common 9 | { 10 | public interface IStreamServer 11 | { 12 | void MessageReceived(ChannelMessage msg); 13 | int ConnectionCount { get; set; } 14 | IDictionary Streams { get; } 15 | IDictionary Listeners { get; } 16 | bool WriteMessage(ChannelMessage msg); 17 | void Log(object message); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /TSTunnels/Server/ForwardedPort.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Windows.Forms; 5 | 6 | namespace TSTunnels.Server 7 | { 8 | public enum ForwardDirection 9 | { 10 | Local, 11 | Remote 12 | } 13 | 14 | public class ForwardedPort 15 | { 16 | public ForwardDirection Direction; 17 | public string ListenEndPoint; 18 | public string ConnectEndPoint; 19 | public MethodInvoker Remove; 20 | 21 | public override string ToString() 22 | { 23 | return Direction + "\t" + ListenEndPoint + "\t" + ConnectEndPoint; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/ListenResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace TSTunnels.Common.Messages 5 | { 6 | [Serializable] 7 | public class ListenResponse : ChannelMessage 8 | { 9 | public readonly int ListenIndex; 10 | 11 | public ListenResponse(int ListenIndex) 12 | : base(MessageType.ListenResponse) 13 | { 14 | this.ListenIndex = ListenIndex; 15 | } 16 | 17 | protected ListenResponse(BinaryReader reader) 18 | : base(reader) 19 | { 20 | ListenIndex = reader.ReadInt32(); 21 | } 22 | 23 | protected override void Serialize(BinaryWriter writer) 24 | { 25 | base.Serialize(writer); 26 | writer.Write(ListenIndex); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/HelloResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace TSTunnels.Common.Messages 5 | { 6 | [Serializable] 7 | public class HelloResponse : ChannelMessage 8 | { 9 | public readonly string MachineName; 10 | 11 | public HelloResponse() 12 | : base(MessageType.HelloResponse) 13 | { 14 | MachineName = Environment.MachineName; 15 | } 16 | 17 | protected HelloResponse(BinaryReader reader) 18 | : base(reader) 19 | { 20 | MachineName = reader.ReadString(); 21 | } 22 | 23 | protected override void Serialize(BinaryWriter writer) 24 | { 25 | base.Serialize(writer); 26 | writer.Write(MachineName); 27 | } 28 | 29 | public override string ToString() 30 | { 31 | return base.ToString() + ": " + MachineName; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /TSTunnels.UI/Win32Api.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | 7 | namespace TSTunnels.UI 8 | { 9 | class Win32Api 10 | { 11 | [DllImport("Kernel32.dll", SetLastError = true)] 12 | private static extern IntPtr GetModuleHandle(string moduleName); 13 | 14 | [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)] 15 | private static extern IntPtr LoadIcon(IntPtr hInst, string iconId); 16 | 17 | private const string IDI_APPLICATION = "#32512"; 18 | 19 | public static Icon GetApplicationIcon() 20 | { 21 | var icon = LoadIcon(GetModuleHandle(null), IDI_APPLICATION); 22 | // vshost.exe doesn't have our application icon, so we could fail at this point 23 | return icon != IntPtr.Zero ? Icon.FromHandle(icon) : SystemIcons.Application; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/HelloRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace TSTunnels.Common.Messages 5 | { 6 | [Serializable] 7 | public class HelloRequest : ChannelMessage 8 | { 9 | public readonly string MachineName; 10 | 11 | public HelloRequest() 12 | : base(MessageType.HelloRequest) 13 | { 14 | MachineName = Environment.MachineName; 15 | } 16 | 17 | protected HelloRequest(BinaryReader reader) 18 | : base(reader) 19 | { 20 | MachineName = reader.ReadString(); 21 | } 22 | 23 | protected override void Serialize(BinaryWriter writer) 24 | { 25 | base.Serialize(writer); 26 | writer.Write(MachineName); 27 | } 28 | 29 | public override string ToString() 30 | { 31 | return base.ToString() + ": " + MachineName; 32 | } 33 | 34 | public void Process(IStreamServer server) 35 | { 36 | server.WriteMessage(new HelloResponse()); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/AcceptRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace TSTunnels.Common.Messages 5 | { 6 | [Serializable] 7 | public class AcceptRequest : ChannelMessage 8 | { 9 | public readonly int ListenIndex; 10 | public readonly int StreamIndex; 11 | public readonly string RemoteEndPoint; 12 | 13 | public AcceptRequest(int ListenIndex, int StreamIndex, string RemoteEndPoint) 14 | : base(MessageType.AcceptRequest) 15 | { 16 | this.ListenIndex = ListenIndex; 17 | this.StreamIndex = StreamIndex; 18 | this.RemoteEndPoint = RemoteEndPoint; 19 | } 20 | 21 | protected AcceptRequest(BinaryReader reader) 22 | : base(reader) 23 | { 24 | ListenIndex = reader.ReadInt32(); 25 | StreamIndex = reader.ReadInt32(); 26 | RemoteEndPoint = reader.ReadString(); 27 | } 28 | 29 | protected override void Serialize(BinaryWriter writer) 30 | { 31 | base.Serialize(writer); 32 | writer.Write(ListenIndex); 33 | writer.Write(StreamIndex); 34 | writer.Write(RemoteEndPoint); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /tools/ExportDll.exe.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin\ildasm.exe 12 | 13 | 14 | C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ilasm.exe 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /TSTunnels.UI/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:2.0.50727.3053 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TSTunnels.UI.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /TSTunnels/Common/TcpListenerHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | 8 | namespace TSTunnels.Common 9 | { 10 | public static class TcpListenerHelper 11 | { 12 | public static TcpListener Start(string listenAddress, int listenPort, Action acceptDelegate, Action exceptionDelegate) 13 | { 14 | var listener = new TcpListener(IPAddress.Parse(listenAddress), listenPort); 15 | listener.Start(); 16 | listener.BeginAcceptTcpClient(listener_AcceptTcpClient, (Converter)delegate(IAsyncResult ar) 17 | { 18 | try 19 | { 20 | acceptDelegate(listener.EndAcceptTcpClient(ar)); 21 | return listener; 22 | } 23 | catch (Exception ex) 24 | { 25 | Debug.Print(DateTime.Now + " " + Environment.MachineName + ": " + ex); 26 | exceptionDelegate(ex); 27 | return null; 28 | } 29 | }); 30 | return listener; 31 | } 32 | 33 | private static void listener_AcceptTcpClient(IAsyncResult ar) 34 | { 35 | var callback = (Converter)ar.AsyncState; 36 | var listener = callback(ar); 37 | if (listener != null) listener.BeginAcceptTcpClient(listener_AcceptTcpClient, callback); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/StreamData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace TSTunnels.Common.Messages 7 | { 8 | [Serializable] 9 | public class StreamData : ChannelMessage 10 | { 11 | public readonly int StreamIndex; 12 | public readonly byte[] Data; 13 | 14 | public StreamData(int StreamIndex, byte[] Data) 15 | : base(MessageType.StreamData) 16 | { 17 | this.StreamIndex = StreamIndex; 18 | this.Data = Data; 19 | } 20 | 21 | protected StreamData(BinaryReader reader) 22 | : base(reader) 23 | { 24 | StreamIndex = reader.ReadInt32(); 25 | var length = reader.ReadInt32(); 26 | Data = reader.ReadBytes(length); 27 | } 28 | 29 | protected override void Serialize(BinaryWriter writer) 30 | { 31 | base.Serialize(writer); 32 | writer.Write(StreamIndex); 33 | writer.Write(Data.Length); 34 | writer.Write(Data); 35 | } 36 | 37 | public void Process(IStreamServer server) 38 | { 39 | Stream stream; 40 | lock (server.Streams) 41 | { 42 | if (!server.Streams.ContainsKey(StreamIndex)) return; 43 | stream = server.Streams[StreamIndex]; 44 | } 45 | try 46 | { 47 | stream.Write(Data, 0, Data.Length); 48 | } 49 | catch (Exception ex) 50 | { 51 | server.WriteMessage(new StreamError(StreamIndex, ex)); 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /TSTunnels/Client/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using Microsoft.Win32; 7 | using TSTunnels.Common.Messages; 8 | using TSTunnels.Client.WtsApi32; 9 | 10 | namespace TSTunnels.Client 11 | { 12 | public static class Program 13 | { 14 | private const string registryAddins = @"Software\Microsoft\Terminal Server Client\Default\AddIns\"; 15 | 16 | [ExportDllAttribute.ExportDll("DllRegisterServer", CallingConvention.StdCall)] 17 | public static void DllRegisterServer() 18 | { 19 | using (var key = Registry.CurrentUser.CreateSubKey(registryAddins + ChannelMessage.ChannelName)) 20 | { 21 | Debug.Assert(key != null); 22 | var location = typeof(Client).Assembly.Location; 23 | Debug.Assert(location != null); 24 | key.SetValue("Name", location); 25 | } 26 | } 27 | 28 | [ExportDllAttribute.ExportDll("DllUnregisterServer", CallingConvention.StdCall)] 29 | public static void DllUnregisterServer() 30 | { 31 | Registry.CurrentUser.DeleteSubKey(registryAddins + ChannelMessage.ChannelName); 32 | } 33 | 34 | private static Client client; 35 | 36 | [ExportDllAttribute.ExportDll("VirtualChannelEntry", CallingConvention.StdCall)] 37 | public static bool VirtualChannelEntry(ref ChannelEntryPoints entry) 38 | { 39 | client = new Client(entry); 40 | return client.VirtualChannelInit(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /TSTunnels/Common/StreamPump.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace TSTunnels.Common 7 | { 8 | public class StreamPump 9 | { 10 | const int bufferSize = 10000; 11 | private readonly Stream stream; 12 | private readonly Action writeCallback; 13 | private readonly Action errorCallback; 14 | private readonly byte[] buf = new byte[bufferSize]; 15 | 16 | public StreamPump(Stream stream, Action writeCallback, Action errorCallback) 17 | { 18 | this.stream = stream; 19 | this.writeCallback = writeCallback; 20 | this.errorCallback = errorCallback; 21 | } 22 | 23 | public void Pump() 24 | { 25 | try 26 | { 27 | stream.BeginRead(buf, 0, bufferSize, ReadCallback, null); 28 | } 29 | catch (Exception ex) 30 | { 31 | errorCallback(ex); 32 | } 33 | } 34 | 35 | private void ReadCallback(IAsyncResult ar) 36 | { 37 | try 38 | { 39 | var bytesReceived = stream.EndRead(ar); 40 | if (bytesReceived > 0) 41 | { 42 | var data = new byte[bytesReceived]; 43 | Buffer.BlockCopy(buf, 0, data, 0, bytesReceived); 44 | writeCallback(data); 45 | Pump(); 46 | } 47 | else 48 | { 49 | errorCallback(new EndOfStreamException()); 50 | return; 51 | } 52 | } 53 | catch (Exception ex) 54 | { 55 | errorCallback(ex); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /TSTunnels/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TSTunnels")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TSTunnels")] 13 | [assembly: AssemblyCopyright("Copyright © Darkskies 2009")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0723206d-9a8a-4238-aa70-3ebc50831f64")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TSTunnels.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TSTunnels", "TSTunnels\TSTunnels.csproj", "{46202B33-3B41-4B2B-B349-9DE165C8883A}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TSTunnels.UI", "TSTunnels.UI\TSTunnels.UI.csproj", "{8D98224E-FDA0-422F-BDAA-8AF5BCE5F28A}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {46202B33-3B41-4B2B-B349-9DE165C8883A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {46202B33-3B41-4B2B-B349-9DE165C8883A}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {46202B33-3B41-4B2B-B349-9DE165C8883A}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {46202B33-3B41-4B2B-B349-9DE165C8883A}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {8D98224E-FDA0-422F-BDAA-8AF5BCE5F28A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {8D98224E-FDA0-422F-BDAA-8AF5BCE5F28A}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {8D98224E-FDA0-422F-BDAA-8AF5BCE5F28A}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {8D98224E-FDA0-422F-BDAA-8AF5BCE5F28A}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | GlobalSection(ExtensibilityGlobals) = postSolution 27 | VisualSVNWorkingCopyRoot = . 28 | EndGlobalSection 29 | EndGlobal 30 | -------------------------------------------------------------------------------- /TSTunnels.UI/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Remote Desktop Tunnels")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Remote Desktop Tunnels")] 13 | [assembly: AssemblyCopyright("Copyright © Darkskies 2009")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("01bd97f3-b6e2-46eb-8c77-f79088498b9d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/ConnectResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | 5 | namespace TSTunnels.Common.Messages 6 | { 7 | [Serializable] 8 | public class ConnectResponse : ChannelMessage 9 | { 10 | public readonly int StreamIndex; 11 | public readonly string LocalEndPoint; 12 | public readonly string RemoteEndPoint; 13 | 14 | public ConnectResponse(int StreamIndex, string RemoteEndPoint, string LocalEndPoint) 15 | : base(MessageType.ConnectResponse) 16 | { 17 | this.StreamIndex = StreamIndex; 18 | this.LocalEndPoint = LocalEndPoint; 19 | this.RemoteEndPoint = RemoteEndPoint; 20 | } 21 | 22 | protected ConnectResponse(BinaryReader reader) 23 | : base(reader) 24 | { 25 | StreamIndex = reader.ReadInt32(); 26 | LocalEndPoint = reader.ReadString(); 27 | RemoteEndPoint = reader.ReadString(); 28 | } 29 | 30 | protected override void Serialize(BinaryWriter writer) 31 | { 32 | base.Serialize(writer); 33 | writer.Write(StreamIndex); 34 | writer.Write(LocalEndPoint); 35 | writer.Write(RemoteEndPoint); 36 | } 37 | 38 | public override string ToString() 39 | { 40 | return base.ToString() + ": " + RemoteEndPoint + " to " + LocalEndPoint; 41 | } 42 | 43 | public void Process(IStreamServer server) 44 | { 45 | new StreamPump(server.Streams[StreamIndex], 46 | data => server.WriteMessage(new StreamData(StreamIndex, data)), 47 | ex => 48 | { 49 | server.Log("Forwarded port closed: " + RemoteEndPoint + " to " + LocalEndPoint); 50 | server.WriteMessage(new StreamError(StreamIndex, ex)); 51 | }).Pump(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/ListenRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace TSTunnels.Common.Messages 5 | { 6 | [Serializable] 7 | public class ListenRequest : ChannelMessage 8 | { 9 | public readonly int StreamIndex; 10 | public readonly string Address; 11 | public readonly int Port; 12 | 13 | public ListenRequest(int StreamIndex, string Address, int Port) 14 | : base(MessageType.ListenRequest) 15 | { 16 | this.StreamIndex = StreamIndex; 17 | this.Address = Address; 18 | this.Port = Port; 19 | } 20 | 21 | protected ListenRequest(BinaryReader reader) 22 | : base(reader) 23 | { 24 | StreamIndex = reader.ReadInt32(); 25 | Address = reader.ReadString(); 26 | Port = reader.ReadInt32(); 27 | } 28 | 29 | protected override void Serialize(BinaryWriter writer) 30 | { 31 | base.Serialize(writer); 32 | writer.Write(StreamIndex); 33 | writer.Write(Address); 34 | writer.Write(Port); 35 | } 36 | 37 | public void Process(IStreamServer server) 38 | { 39 | try 40 | { 41 | server.Listeners[StreamIndex] = TcpListenerHelper.Start(Address, Port, client => 42 | { 43 | var streamIndex = --server.ConnectionCount; 44 | server.Streams[streamIndex] = client.GetStream(); 45 | server.WriteMessage(new AcceptRequest(StreamIndex, streamIndex, client.Client.RemoteEndPoint.ToString())); 46 | }, exception => server.WriteMessage(new StreamError(StreamIndex, new EndOfStreamException()))); 47 | server.WriteMessage(new ListenResponse(StreamIndex)); 48 | } 49 | catch (Exception ex) 50 | { 51 | server.WriteMessage(new StreamError(StreamIndex, ex)); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /TSTunnels/Server/WtsApi32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using Microsoft.Win32.SafeHandles; 6 | 7 | namespace TSTunnels.Server 8 | { 9 | class WtsApi32 10 | { 11 | [DllImport("Wtsapi32.dll", SetLastError = true)] 12 | public static extern IntPtr WTSVirtualChannelOpen(IntPtr server, int sessionId, [MarshalAs(UnmanagedType.LPStr)] string virtualName); 13 | 14 | [DllImport("Wtsapi32.dll", SetLastError = true)] 15 | public static extern bool WTSVirtualChannelQuery(IntPtr channelHandle, WtsVirtualClass Class, out IntPtr data, out int bytesReturned); 16 | 17 | [DllImport("Wtsapi32.dll")] 18 | public static extern void WTSFreeMemory(IntPtr memory); 19 | 20 | [DllImport("Wtsapi32.dll", SetLastError = true)] 21 | public static extern bool WTSVirtualChannelWrite(IntPtr channelHandle, byte[] data, int length, out int bytesWritten); 22 | 23 | [DllImport("Wtsapi32.dll", SetLastError = true)] 24 | public static extern bool WTSVirtualChannelRead(IntPtr channelHandle, int TimeOut, IntPtr data, int length, out int bytesRead); 25 | 26 | [DllImport("Wtsapi32.dll")] 27 | public static extern bool WTSVirtualChannelClose(IntPtr channelHandle); 28 | 29 | public static Stream WTSVirtualChannelQuery_WTSVirtualFileHandle(IntPtr channelHandle) 30 | { 31 | int len; 32 | IntPtr buffer; 33 | var b = WTSVirtualChannelQuery(channelHandle, WtsVirtualClass.WTSVirtualFileHandle, out buffer, out len); 34 | if (!b) throw new Win32Exception(); 35 | var fileHandle = new SafeFileHandle(Marshal.ReadIntPtr(buffer), true); 36 | WTSFreeMemory(buffer); 37 | 38 | return new FileStream(fileHandle, FileAccess.ReadWrite, 0x1000, true); 39 | } 40 | } 41 | 42 | public enum WtsVirtualClass 43 | { 44 | WTSVirtualClient = 0, 45 | WTSVirtualFileHandle = 1 46 | } 47 | 48 | } -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/ChannelMessage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | using System.Runtime.Serialization.Formatters.Binary; 7 | using System.Text; 8 | using System.Runtime.Serialization; 9 | 10 | namespace TSTunnels.Common.Messages 11 | { 12 | public abstract class ChannelMessage 13 | { 14 | //ATTENTION: name should have 7 or less chars 15 | public const string ChannelName = "TSTnls"; 16 | 17 | public readonly MessageType Type; 18 | 19 | protected ChannelMessage(MessageType Type) 20 | { 21 | this.Type = Type; 22 | } 23 | 24 | protected ChannelMessage(BinaryReader reader) 25 | { 26 | Type = (MessageType)reader.ReadInt32(); 27 | } 28 | 29 | protected virtual void Serialize(BinaryWriter writer) 30 | { 31 | writer.Write((int)Type); 32 | } 33 | 34 | public byte[] ToByteArray() 35 | { 36 | if (Type == MessageType.Unknown) 37 | throw new ArgumentException(); 38 | using (var stream = new MemoryStream()) 39 | { 40 | using (var writer = new BinaryWriter(stream)) 41 | { 42 | writer.Write(GetType().FullName); 43 | Serialize(writer); 44 | } 45 | return stream.ToArray(); 46 | } 47 | } 48 | 49 | public static ChannelMessage FromByteArray(byte[] data) 50 | { 51 | using (var stream = new MemoryStream(data)) 52 | { 53 | return FromStream(stream); 54 | } 55 | } 56 | 57 | public static ChannelMessage FromStream(Stream stream) 58 | { 59 | using (var reader = new BinaryReader(stream)) 60 | { 61 | var type = System.Type.GetType(reader.ReadString()); 62 | return (ChannelMessage)type.InvokeMember(null, BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.NonPublic, null, null, new object[] { reader }); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/ConnectRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Net.Sockets; 6 | 7 | namespace TSTunnels.Common.Messages 8 | { 9 | [Serializable] 10 | public class ConnectRequest : ChannelMessage 11 | { 12 | public readonly int StreamIndex; 13 | public readonly string LocalEndPoint; 14 | public readonly string Address; 15 | public readonly int Port; 16 | 17 | public ConnectRequest(int StreamIndex, string LocalEndPoint, string Address, int Port) 18 | : base(MessageType.ConnectRequest) 19 | { 20 | this.StreamIndex = StreamIndex; 21 | this.LocalEndPoint = LocalEndPoint; 22 | this.Address = Address; 23 | this.Port = Port; 24 | } 25 | 26 | protected ConnectRequest(BinaryReader reader) 27 | : base(reader) 28 | { 29 | StreamIndex = reader.ReadInt32(); 30 | LocalEndPoint = reader.ReadString(); 31 | Address = reader.ReadString(); 32 | Port = reader.ReadInt32(); 33 | } 34 | 35 | protected override void Serialize(BinaryWriter writer) 36 | { 37 | base.Serialize(writer); 38 | writer.Write(StreamIndex); 39 | writer.Write(LocalEndPoint); 40 | writer.Write(Address); 41 | writer.Write(Port); 42 | } 43 | 44 | public void Process(IStreamServer server) 45 | { 46 | var client = new TcpClient(); 47 | client.BeginConnect(Address, Port, ar => 48 | { 49 | try 50 | { 51 | client.EndConnect(ar); 52 | server.Streams[StreamIndex] = client.GetStream(); 53 | var connectResult = new ConnectResponse(StreamIndex, LocalEndPoint, client.Client.RemoteEndPoint.ToString()); 54 | connectResult.Process(server); 55 | server.WriteMessage(connectResult); 56 | } 57 | catch (Exception ex) 58 | { 59 | Debug.Print(DateTime.Now + " " + Environment.MachineName + ": " + ex); 60 | server.WriteMessage(new StreamError(StreamIndex, ex)); 61 | } 62 | }, null); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /TSTunnels.UI/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:2.0.50727.3053 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TSTunnels.UI.Properties 12 | { 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources 26 | { 27 | 28 | private static global::System.Resources.ResourceManager resourceMan; 29 | 30 | private static global::System.Globalization.CultureInfo resourceCulture; 31 | 32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 33 | internal Resources() 34 | { 35 | } 36 | 37 | /// 38 | /// Returns the cached ResourceManager instance used by this class. 39 | /// 40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 41 | internal static global::System.Resources.ResourceManager ResourceManager 42 | { 43 | get 44 | { 45 | if ((resourceMan == null)) 46 | { 47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TSTunnels.UI.Properties.Resources", typeof(Resources).Assembly); 48 | resourceMan = temp; 49 | } 50 | return resourceMan; 51 | } 52 | } 53 | 54 | /// 55 | /// Overrides the current thread's CurrentUICulture property for all 56 | /// resource lookups using this strongly typed resource class. 57 | /// 58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 59 | internal static global::System.Globalization.CultureInfo Culture 60 | { 61 | get 62 | { 63 | return resourceCulture; 64 | } 65 | set 66 | { 67 | resourceCulture = value; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /TSTunnels/Common/Messages/StreamError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net.Sockets; 5 | using System.Text; 6 | 7 | namespace TSTunnels.Common.Messages 8 | { 9 | [Serializable] 10 | public class StreamError : ChannelMessage 11 | { 12 | public readonly int StreamIndex; 13 | public readonly string ExceptionType; 14 | public readonly string Message; 15 | public readonly string StackTrace; 16 | 17 | public StreamError(int StreamIndex, Exception Exception) 18 | : base(MessageType.StreamError) 19 | { 20 | this.StreamIndex = StreamIndex; 21 | ExceptionType = Exception.GetType().Name; 22 | Message = Exception.Message ?? ""; 23 | StackTrace = Exception.StackTrace ?? ""; 24 | } 25 | 26 | protected StreamError(BinaryReader reader) 27 | : base(reader) 28 | { 29 | StreamIndex = reader.ReadInt32(); 30 | ExceptionType = reader.ReadString(); 31 | Message = reader.ReadString(); 32 | StackTrace = reader.ReadString(); 33 | } 34 | 35 | protected override void Serialize(BinaryWriter writer) 36 | { 37 | base.Serialize(writer); 38 | writer.Write(StreamIndex); 39 | writer.Write(ExceptionType); 40 | writer.Write(Message); 41 | writer.Write(StackTrace); 42 | } 43 | 44 | public override string ToString() 45 | { 46 | return base.ToString() + ": " + ExceptionType + ": " + Message; 47 | } 48 | 49 | public void Process(IStreamServer server) 50 | { 51 | ProcessStreams(server.Streams); 52 | ProcessListeners(server.Listeners); 53 | } 54 | 55 | private void ProcessStreams(IDictionary streams) 56 | { 57 | Stream stream; 58 | lock (streams) 59 | { 60 | if (!streams.ContainsKey(StreamIndex)) return; 61 | stream = streams[StreamIndex]; 62 | } 63 | if (Message.Length != 0) 64 | { 65 | try 66 | { 67 | var data = Encoding.UTF8.GetBytes(Message); 68 | stream.Write(data, 0, data.Length); 69 | } 70 | catch (IOException) 71 | { 72 | } 73 | } 74 | try 75 | { 76 | stream.Close(); 77 | } 78 | catch (IOException) 79 | { 80 | } 81 | lock (streams) 82 | { 83 | if (streams.ContainsKey(StreamIndex)) streams.Remove(StreamIndex); 84 | } 85 | } 86 | 87 | private void ProcessListeners(IDictionary listeners) 88 | { 89 | TcpListener listener; 90 | lock (listeners) 91 | { 92 | if (!listeners.ContainsKey(StreamIndex)) return; 93 | listener = listeners[StreamIndex]; 94 | } 95 | try 96 | { 97 | listener.Stop(); 98 | } 99 | catch (IOException) 100 | { 101 | } 102 | lock (listeners) 103 | { 104 | if (listeners.ContainsKey(StreamIndex)) listeners.Remove(StreamIndex); 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /TSTunnels/Client/WtsApi32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using Microsoft.Win32; 4 | using System.Text; 5 | using System.Threading; 6 | 7 | namespace TSTunnels.Client 8 | { 9 | namespace WtsApi32 10 | { 11 | public delegate ChannelReturnCodes VirtualChannelInitDelegate(ref IntPtr initHandle, ChannelDef[] channels, int channelCount, int versionRequested, [MarshalAs(UnmanagedType.FunctionPtr)] ChannelInitEventDelegate channelInitEventProc); 12 | public delegate ChannelReturnCodes VirtualChannelOpenDelegate(IntPtr initHandle, ref int openHandle, [MarshalAs(UnmanagedType.LPStr)] string channelName, [MarshalAs(UnmanagedType.FunctionPtr)] ChannelOpenEventDelegate channelOpenEventProc); 13 | public delegate ChannelReturnCodes VirtualChannelCloseDelegate(int openHandle); 14 | public delegate ChannelReturnCodes VirtualChannelWriteDelegate(int openHandle, IntPtr data, uint dataLength, IntPtr userData); 15 | 16 | public delegate void ChannelInitEventDelegate(IntPtr initHandle, ChannelEvents Event, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] byte[] data, int dataLength); 17 | public delegate void ChannelOpenEventDelegate(int openHandle, ChannelEvents Event, IntPtr data, int dataLength, uint totalLength, ChannelFlags dataFlags); 18 | 19 | [StructLayout(LayoutKind.Sequential)] 20 | public struct ChannelEntryPoints 21 | { 22 | public int Size; 23 | public int ProtocolVersion; 24 | [MarshalAs(UnmanagedType.FunctionPtr)] 25 | public VirtualChannelInitDelegate VirtualChannelInit; 26 | [MarshalAs(UnmanagedType.FunctionPtr)] 27 | public VirtualChannelOpenDelegate VirtualChannelOpen; 28 | [MarshalAs(UnmanagedType.FunctionPtr)] 29 | public VirtualChannelCloseDelegate VirtualChannelClose; 30 | [MarshalAs(UnmanagedType.FunctionPtr)] 31 | public VirtualChannelWriteDelegate VirtualChannelWrite; 32 | } 33 | 34 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 35 | public struct ChannelDef 36 | { 37 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] 38 | public string name; 39 | public ChannelOptions options; 40 | } 41 | 42 | public enum ChannelEvents 43 | { 44 | Initialized = 0, 45 | Connected = 1, 46 | V1Connected = 2, 47 | Disconnected = 3, 48 | Terminated = 4, 49 | DataReceived = 10, 50 | WriteComplete = 11, 51 | WriteCanceled = 12 52 | } 53 | 54 | [Flags] 55 | public enum ChannelFlags 56 | { 57 | First = 0x01, 58 | Last = 0x02, 59 | Only = First | Last, 60 | Middle = 0, 61 | Fail = 0x100, 62 | ShowProtocol = 0x10, 63 | Suspend = 0x20, 64 | Resume = 0x40 65 | } 66 | 67 | [Flags] 68 | public enum ChannelOptions : uint 69 | { 70 | Initialized = 0x80000000, 71 | EncryptRDP = 0x40000000, 72 | EncryptSC = 0x20000000, 73 | EncryptCS = 0x10000000, 74 | PriorityHigh = 0x08000000, 75 | PriorityMedium = 0x04000000, 76 | PriorityLow = 0x02000000, 77 | CompressRDP = 0x00800000, 78 | Compress = 0x00400000, 79 | ShowProtocol = 0x00200000 80 | } 81 | 82 | public enum ChannelReturnCodes 83 | { 84 | Ok = 0, 85 | AlreadyInitialized = 1, 86 | NotInitialized = 2, 87 | AlreadyConnected = 3, 88 | NotConnected = 4, 89 | TooManyChanels = 5, 90 | BadChannel = 6, 91 | BadChannelHandle = 7, 92 | NoBuffer = 8, 93 | BadInitHandle = 9, 94 | NotOpen = 10, 95 | BadProc = 11, 96 | NoMemory = 12, 97 | UnknownChannelName = 13, 98 | AlreadyOpen = 14, 99 | NotInVirtualchannelEntry = 15, 100 | NullData = 16, 101 | ZeroLength = 17 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /TSTunnels/TSTunnels.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.30729 7 | 2.0 8 | {46202B33-3B41-4B2B-B349-9DE165C8883A} 9 | Library 10 | Properties 11 | TSTunnels 12 | TSTunnels 13 | v2.0 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | pdbonly 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | 33 | 34 | 35 | False 36 | ..\tools\ExportDllAttribute.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 78 | 79 | "$(SolutionDir)tools\ExportDll.exe" "$(TargetPath)" /$(ConfigurationName) 80 | regsvr32 /s "$(TargetPath)" 81 | 82 | -------------------------------------------------------------------------------- /TSTunnels.UI/TSTunnels.UI.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.30729 7 | 2.0 8 | {8D98224E-FDA0-422F-BDAA-8AF5BCE5F28A} 9 | WinExe 10 | Properties 11 | TSTunnels.UI 12 | TSTunnels.UI 13 | v2.0 14 | 512 15 | App.ico 16 | 17 | 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | Form 45 | 46 | 47 | UI.cs 48 | 49 | 50 | 51 | 52 | UI.cs 53 | 54 | 55 | ResXFileCodeGenerator 56 | Resources.Designer.cs 57 | Designer 58 | 59 | 60 | True 61 | Resources.resx 62 | 63 | 64 | SettingsSingleFileGenerator 65 | Settings.Designer.cs 66 | 67 | 68 | True 69 | Settings.settings 70 | True 71 | 72 | 73 | 74 | 75 | 76 | {46202B33-3B41-4B2B-B349-9DE165C8883A} 77 | TSTunnels 78 | 79 | 80 | 81 | 82 | 83 | 84 | 91 | 92 | xcopy /d/y $(TargetDir)*.* \\hunnypot\c$\usr\src\darkskies\winapps\$(SolutionName)\$(ProjectName)\$(OutDir) 93 | 94 | -------------------------------------------------------------------------------- /TSTunnels.UI/UI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Text; 7 | using System.Windows.Forms; 8 | using TSTunnels.Server; 9 | 10 | namespace TSTunnels.UI 11 | { 12 | public partial class UI : Form 13 | { 14 | private readonly Server.Server Server; 15 | 16 | public UI() 17 | { 18 | InitializeComponent(); 19 | Icon = Win32Api.GetApplicationIcon(); 20 | Server = new Server.Server(); 21 | Server.Connected += Server_Connected; 22 | Server.ForwardedPortAdded += Server_ForwardedPortAdded; 23 | Server.ForwardedPortRemoved += Server_ForwardedPortRemoved; 24 | Server.MessageLogged += Server_MessageLogged; 25 | } 26 | 27 | private void UI_Load(object sender, EventArgs e) 28 | { 29 | Server.Connect(); 30 | } 31 | 32 | private bool CloseInProgress; 33 | private void UI_FormClosing(object sender, FormClosingEventArgs e) 34 | { 35 | if (CloseInProgress || forwardedPortsListBox.Items.Count == 0) return; 36 | CloseInProgress = true; 37 | e.Cancel = true; 38 | AppendLogMessage("Cancelling all port forwardings"); 39 | foreach (ForwardedPort forwardedPort in forwardedPortsListBox.Items) 40 | { 41 | forwardedPort.Remove(); 42 | } 43 | } 44 | 45 | private void UI_FormClosed(object sender, FormClosedEventArgs e) 46 | { 47 | Server.Disconnect(); 48 | } 49 | 50 | private void Server_ForwardedPortAdded(object sender, ForwardedPortEventArgs e) 51 | { 52 | if (InvokeRequired) 53 | { 54 | Invoke(new EventHandler(Server_ForwardedPortAdded), new[] { sender, e }); 55 | return; 56 | } 57 | forwardedPortsListBox.Items.Add(e.ForwardedPort); 58 | sourceTextBox.Text = string.Empty; 59 | destinationTextBox.Text = string.Empty; 60 | } 61 | 62 | private void Server_ForwardedPortRemoved(object sender, ForwardedPortEventArgs e) 63 | { 64 | if (InvokeRequired) 65 | { 66 | Invoke(new EventHandler(Server_ForwardedPortRemoved), new[] { sender, e }); 67 | return; 68 | } 69 | forwardedPortsListBox.Items.Remove(e.ForwardedPort); 70 | sourceTextBox.Text = e.ForwardedPort.ListenEndPoint; 71 | destinationTextBox.Text = e.ForwardedPort.ConnectEndPoint; 72 | (e.ForwardedPort.Direction == ForwardDirection.Local ? localRadioButton : remoteRadioButton).Checked = true; 73 | if (CloseInProgress && forwardedPortsListBox.Items.Count == 0) Close(); 74 | } 75 | 76 | public void Server_Connected(object sender, ConnectedEventArgs e) 77 | { 78 | if (InvokeRequired) 79 | { 80 | Invoke(new EventHandler(Server_Connected), new[] { sender, e }); 81 | return; 82 | } 83 | portForwardingGroupBox.Enabled = true; 84 | toolTip1.SetToolTip(localRadioButton, "client -> " + e.MachineName + " => " + Environment.MachineName + " -> server"); 85 | toolTip1.SetToolTip(remoteRadioButton, "client -> " + Environment.MachineName + " => " + e.MachineName + " -> server"); 86 | } 87 | 88 | public void Server_MessageLogged(object sender, MessageLoggedEventArgs e) 89 | { 90 | if (InvokeRequired) 91 | { 92 | Invoke(new EventHandler(Server_MessageLogged), new[] { sender, e }); 93 | return; 94 | } 95 | AppendLogMessage(e.Message); 96 | } 97 | 98 | private void AppendLogMessage(object message) 99 | { 100 | eventLogListBox.SelectedIndex = eventLogListBox.Items.Add(DateTime.Now + "\t" + message); 101 | } 102 | 103 | private void addButton_Click(object sender, EventArgs e) 104 | { 105 | try 106 | { 107 | var listenEndPoint = sourceTextBox.Text.Split(':'); 108 | if (listenEndPoint[0].Length == 0 || listenEndPoint.Length > 2) 109 | { 110 | MessageBox.Show("You need to specify a source address in the form \"[host.name:]port\"", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 111 | return; 112 | } 113 | var listenAddress = listenEndPoint.Length > 1 ? listenEndPoint[0] : "127.0.0.1"; 114 | var listenPort = int.Parse(listenEndPoint.Length > 1 ? listenEndPoint[1] : listenEndPoint[0]); 115 | var connectEndPoint = destinationTextBox.Text.Split(':'); 116 | if (connectEndPoint[0].Length == 0 || connectEndPoint.Length > 2) 117 | { 118 | MessageBox.Show("You need to specify a destination address in the form \"[host.name:]port\"", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 119 | return; 120 | } 121 | var connectAddress = connectEndPoint.Length > 1 ? connectEndPoint[0] : "127.0.0.1"; 122 | var connectPort = int.Parse(connectEndPoint.Length > 1 ? connectEndPoint[1] : connectEndPoint[0]); 123 | (localRadioButton.Checked ? (Server.Server.CreatePort)Server.CreateClientPort : Server.CreateServerPort)(listenAddress, listenPort, connectAddress, connectPort); 124 | } 125 | catch (Exception ex) 126 | { 127 | AppendLogMessage(ex); 128 | } 129 | } 130 | 131 | private void removeButton_Click(object sender, EventArgs e) 132 | { 133 | var forwardedPort = forwardedPortsListBox.SelectedItem as ForwardedPort; 134 | if (forwardedPort != null) forwardedPort.Remove(); 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /TSTunnels.UI/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /TSTunnels.UI/UI.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /TSTunnels/Client/Client.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Net.Sockets; 6 | using System.Runtime.InteropServices; 7 | using System.Windows.Forms; 8 | using TSTunnels.Client.WtsApi32; 9 | using TSTunnels.Common; 10 | using TSTunnels.Common.Messages; 11 | 12 | namespace TSTunnels.Client 13 | { 14 | public class Client : IStreamServer 15 | { 16 | private readonly ChannelEntryPoints entryPoints; 17 | private readonly ChannelInitEventDelegate channelInitEventDelegate; 18 | private readonly ChannelOpenEventDelegate channelOpenEventDelegate; 19 | 20 | private IntPtr Channel; 21 | private int OpenChannel; 22 | 23 | public Client(ChannelEntryPoints entry) 24 | { 25 | entryPoints = entry; 26 | channelInitEventDelegate = VirtualChannelInitEventProc; 27 | channelOpenEventDelegate = VirtualChannelOpenEvent; 28 | Streams = new Dictionary(); 29 | Listeners = new Dictionary(); 30 | } 31 | 32 | public bool VirtualChannelInit() 33 | { 34 | var cd = new ChannelDef[1]; 35 | cd[0] = new ChannelDef { name = ChannelMessage.ChannelName }; 36 | var ret = entryPoints.VirtualChannelInit(ref Channel, cd, 1, 1, channelInitEventDelegate); 37 | if (ret != ChannelReturnCodes.Ok) 38 | { 39 | MessageBox.Show("TSTunnels: RDP Virtual channel Init Failed.\n" + ret, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 40 | return false; 41 | } 42 | return true; 43 | } 44 | 45 | private void VirtualChannelInitEventProc(IntPtr initHandle, ChannelEvents Event, byte[] data, int dataLength) 46 | { 47 | Debug.Print(DateTime.Now + " " + Environment.MachineName + ": VirtualChannelInitEventProc: " + Event); 48 | switch (Event) 49 | { 50 | case ChannelEvents.Initialized: 51 | break; 52 | case ChannelEvents.Connected: 53 | var ret = entryPoints.VirtualChannelOpen(initHandle, ref OpenChannel, ChannelMessage.ChannelName, channelOpenEventDelegate); 54 | if (ret != ChannelReturnCodes.Ok) 55 | { 56 | MessageBox.Show("TSTunnels: Open of RDP virtual channel failed.\n" + ret, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 57 | } 58 | else 59 | { 60 | /*main = new frmMain(entryPoints, OpenChannel); 61 | main.Show(); 62 | main.Hide(); 63 | string servername = System.Text.Encoding.Unicode.GetString(data); 64 | servername = servername.Substring(0, servername.IndexOf('\0')); 65 | main.Text = "TS addin in C#: " + servername;*/ 66 | } 67 | break; 68 | case ChannelEvents.V1Connected: 69 | MessageBox.Show("TSTunnels: Connecting to a non Windows 2000 Terminal Server.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); 70 | break; 71 | case ChannelEvents.Disconnected: 72 | //main.RealClosing = true; 73 | //main.Close(); 74 | break; 75 | case ChannelEvents.Terminated: 76 | break; 77 | } 78 | } 79 | 80 | private MemoryStream memoryStream; 81 | public void VirtualChannelOpenEvent(int openHandle, ChannelEvents Event, IntPtr pData, int dataLength, uint totalLength, ChannelFlags dataFlags) 82 | { 83 | Debug.Print(DateTime.Now + " " + Environment.MachineName + ": VirtualChannelOpenEvent: " + Event); 84 | switch (Event) 85 | { 86 | case ChannelEvents.DataReceived: 87 | var data = new byte[dataLength]; 88 | Marshal.Copy(pData, data, 0, dataLength); 89 | switch (dataFlags & ChannelFlags.Only) 90 | { 91 | case ChannelFlags.Only: 92 | MessageReceived(ChannelMessage.FromByteArray(data)); 93 | break; 94 | case ChannelFlags.First: 95 | memoryStream = new MemoryStream((int)totalLength); 96 | memoryStream.Write(data, 0, data.Length); 97 | break; 98 | case ChannelFlags.Middle: 99 | if (memoryStream != null) 100 | { 101 | memoryStream.Write(data, 0, data.Length); 102 | } 103 | break; 104 | case ChannelFlags.Last: 105 | if (memoryStream != null) 106 | { 107 | memoryStream.Write(data, 0, data.Length); 108 | memoryStream.Position = 0; 109 | MessageReceived(ChannelMessage.FromStream(memoryStream)); 110 | memoryStream = null; 111 | } 112 | break; 113 | } 114 | break; 115 | case ChannelEvents.WriteCanceled: 116 | case ChannelEvents.WriteComplete: 117 | /* 118 | * The VirtualChannelWrite function is asynchronous. When the write operation has been completed, 119 | * your VirtualChannelOpenEvent function receives a CHANNEL_EVENT_WRITE_COMPLETE notification. 120 | * Until that notification is received, the caller must not free or reuse the pData buffer passed to VirtualChannelWrite 121 | */ 122 | Marshal.FreeHGlobal(pData); 123 | break; 124 | } 125 | } 126 | 127 | #region Implementation of IStreamServer 128 | 129 | public int ConnectionCount { get; set; } 130 | public IDictionary Listeners { get; private set; } 131 | public IDictionary Streams { get; private set; } 132 | 133 | public void Log(object message) 134 | { 135 | Debug.Print(DateTime.Now + " " + Environment.MachineName + ": Log: " + message); 136 | } 137 | 138 | public void MessageReceived(ChannelMessage msg) 139 | { 140 | switch (msg.Type) 141 | { 142 | case MessageType.HelloRequest: 143 | ((HelloRequest)msg).Process(this); 144 | break; 145 | case MessageType.ConnectRequest: 146 | ((ConnectRequest)msg).Process(this); 147 | break; 148 | case MessageType.ListenRequest: 149 | ((ListenRequest)msg).Process(this); 150 | break; 151 | case MessageType.ConnectResponse: 152 | ((ConnectResponse)msg).Process(this); 153 | break; 154 | case MessageType.StreamData: 155 | ((StreamData)msg).Process(this); 156 | break; 157 | case MessageType.StreamError: 158 | ((StreamError)msg).Process(this); 159 | break; 160 | } 161 | } 162 | 163 | public bool WriteMessage(ChannelMessage msg) 164 | { 165 | var data = msg.ToByteArray(); 166 | var len = 4 + data.Length; 167 | var ptr = Marshal.AllocHGlobal(len); 168 | Marshal.WriteInt32(ptr, 0, data.Length); 169 | Marshal.Copy(data, 0, new IntPtr(ptr.ToInt32() + 4), data.Length); 170 | var ret = entryPoints.VirtualChannelWrite(OpenChannel, ptr, (uint)len, ptr); 171 | return ret == ChannelReturnCodes.Ok; 172 | } 173 | 174 | #endregion 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /TSTunnels/Server/Server.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.IO; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading; 8 | using TSTunnels.Common; 9 | using TSTunnels.Common.Messages; 10 | 11 | namespace TSTunnels.Server 12 | { 13 | public class Server : IStreamServer 14 | { 15 | private IntPtr mHandle; 16 | private BinaryReader reader; 17 | 18 | public bool IsConnected { get; private set; } 19 | private bool SeenHello; 20 | 21 | public delegate void CreatePort(string listenAddress, int listenPort, string connectAddress, int connectPort); 22 | 23 | class ClientListener 24 | { 25 | public Action ListenResponse; 26 | public Action StreamError; 27 | public Action AcceptRequest; 28 | } 29 | private readonly Dictionary clientListeners = new Dictionary(); 30 | 31 | #region Events 32 | 33 | public EventHandler Connected; 34 | 35 | protected void OnConnected(string machineName) 36 | { 37 | if (Connected != null) 38 | Connected(this, new ConnectedEventArgs(machineName)); 39 | } 40 | 41 | public EventHandler ForwardedPortAdded; 42 | 43 | protected void OnForwardedPortAdded(ForwardedPort forwardedPort) 44 | { 45 | if (ForwardedPortAdded != null) 46 | ForwardedPortAdded(this, new ForwardedPortEventArgs(forwardedPort)); 47 | } 48 | 49 | public EventHandler ForwardedPortRemoved; 50 | 51 | protected void OnForwardedPortRemoved(ForwardedPort forwardedPort) 52 | { 53 | if (ForwardedPortRemoved != null) 54 | ForwardedPortRemoved(this, new ForwardedPortEventArgs(forwardedPort)); 55 | } 56 | 57 | public EventHandler MessageLogged; 58 | 59 | protected void OnMessageLogged(string message) 60 | { 61 | if (MessageLogged != null) 62 | MessageLogged(this, new MessageLoggedEventArgs(message)); 63 | } 64 | 65 | #endregion 66 | 67 | public Server() 68 | { 69 | Streams = new Dictionary(); 70 | Listeners = new Dictionary(); 71 | } 72 | 73 | private delegate void Action(); 74 | public void Connect() 75 | { 76 | mHandle = WtsApi32.WTSVirtualChannelOpen(IntPtr.Zero, -1, ChannelMessage.ChannelName); 77 | if (mHandle == IntPtr.Zero) 78 | { 79 | Log("RDP Virtual channel Open Failed: " + new Win32Exception().Message); 80 | return; 81 | } 82 | 83 | try 84 | { 85 | var stream = WtsApi32.WTSVirtualChannelQuery_WTSVirtualFileHandle(mHandle); 86 | reader = new BinaryReader(new BufferedStream(stream)); 87 | } 88 | catch (Win32Exception ex) 89 | { 90 | Log("RDP Virtual channel Query Failed: " + ex.Message); 91 | return; 92 | } 93 | 94 | IsConnected = true; 95 | 96 | Action process = Process; 97 | process.BeginInvoke(process.EndInvoke, null); 98 | 99 | Action hello = () => 100 | { 101 | while (!SeenHello && IsConnected) 102 | { 103 | WriteMessage(new HelloRequest()); 104 | Thread.Sleep(200); 105 | } 106 | }; 107 | hello.BeginInvoke(hello.EndInvoke, null); 108 | } 109 | 110 | public void Disconnect() 111 | { 112 | IsConnected = false; 113 | if (reader != null) reader.Close(); 114 | var ret = WtsApi32.WTSVirtualChannelClose(mHandle); 115 | } 116 | 117 | private void Process() 118 | { 119 | while (IsConnected) 120 | { 121 | try 122 | { 123 | var len = reader.ReadInt32(); 124 | var buff = reader.ReadBytes(len); 125 | MessageReceived(ChannelMessage.FromByteArray(buff)); 126 | } 127 | catch (OperationCanceledException) 128 | { 129 | return; 130 | } 131 | catch (Exception ex) 132 | { 133 | Log(ex); 134 | } 135 | } 136 | } 137 | 138 | public void CreateClientPort(string listenAddress, int listenPort, string connectAddress, int connectPort) 139 | { 140 | var listenIndex = ++ConnectionCount; 141 | ForwardedPort forwardedPort = null; 142 | var listener = new ClientListener 143 | { 144 | ListenResponse = response => 145 | { 146 | forwardedPort = new ForwardedPort 147 | { 148 | Direction = ForwardDirection.Local, 149 | ListenEndPoint = listenAddress + ":" + listenPort, 150 | ConnectEndPoint = connectAddress + ":" + connectPort, 151 | Remove = () => 152 | { 153 | Log("Cancelling local port " + listenAddress + ":" + listenPort + " forwarding to " + connectAddress + ":" + connectPort + " requested"); 154 | WriteMessage(new StreamError(listenIndex, new EndOfStreamException())); 155 | }, 156 | }; 157 | Log("Local port forwarding from " + listenAddress + ":" + listenPort + " enabled"); 158 | OnForwardedPortAdded(forwardedPort); 159 | }, 160 | StreamError = error => 161 | { 162 | if (forwardedPort == null) 163 | { 164 | Log("Local port forwarding from " + listenAddress + ":" + listenPort + " failed: " + error.Message); 165 | lock (clientListeners) 166 | { 167 | if (clientListeners.ContainsKey(listenIndex)) clientListeners.Remove(listenIndex); 168 | } 169 | } 170 | else 171 | { 172 | Log("Cancelled local port " + listenAddress + ":" + listenPort + " forwarding to " + connectAddress + ":" + connectPort); 173 | OnForwardedPortRemoved(forwardedPort); 174 | } 175 | }, 176 | AcceptRequest = request => 177 | { 178 | Log("Opening forwarded connection " + request.RemoteEndPoint + " to " + connectAddress + ":" + connectPort); 179 | new ConnectRequest(request.StreamIndex, request.RemoteEndPoint, connectAddress, connectPort).Process(this); 180 | }, 181 | }; 182 | lock (clientListeners) 183 | { 184 | clientListeners[listenIndex] = listener; 185 | } 186 | Log("Requesting local port " + listenAddress + ":" + listenPort + " forward to " + connectAddress + ":" + connectPort); 187 | WriteMessage(new ListenRequest(listenIndex, listenAddress, listenPort)); 188 | } 189 | 190 | public void CreateServerPort(string listenAddress, int listenPort, string connectAddress, int connectPort) 191 | { 192 | try 193 | { 194 | var listenIndex = ++ConnectionCount; 195 | ForwardedPort forwardedPort = null; 196 | Listeners[listenIndex] = TcpListenerHelper.Start(listenAddress, listenPort, client => 197 | { 198 | var streamIndex = ++ConnectionCount; 199 | Log("Attempting to forward remote port " + client.Client.RemoteEndPoint + " to " + connectAddress + ":" + connectPort); 200 | Streams[streamIndex] = client.GetStream(); 201 | WriteMessage(new ConnectRequest(streamIndex, client.Client.RemoteEndPoint.ToString(), connectAddress, connectPort)); 202 | }, exception => 203 | { 204 | if (forwardedPort == null) 205 | { 206 | Log("Remote port " + listenAddress + ":" + listenPort + " forwarding to " + connectAddress + ":" + connectPort + " exception: " + exception); 207 | } 208 | else 209 | { 210 | Log("Cancelled remote port " + listenAddress + ":" + listenPort + " forwarding to " + connectAddress + ":" + connectPort); 211 | OnForwardedPortRemoved(forwardedPort); 212 | } 213 | }); 214 | Log("Remote port " + listenAddress + ":" + listenPort + " forwarding to " + connectAddress + ":" + connectPort); 215 | forwardedPort = new ForwardedPort 216 | { 217 | Direction = ForwardDirection.Remote, 218 | ListenEndPoint = listenAddress + ":" + listenPort, 219 | ConnectEndPoint = connectAddress + ":" + connectPort, 220 | Remove = () => 221 | { 222 | Log("Cancelling remote port " + listenAddress + ":" + listenPort + " forwarding to " + connectAddress + ":" + connectPort); 223 | new StreamError(listenIndex, new EndOfStreamException()).Process(this); 224 | }, 225 | }; 226 | OnForwardedPortAdded(forwardedPort); 227 | } 228 | catch (Exception ex) 229 | { 230 | Log("Remote port " + listenAddress + ":" + listenPort + " forwarding to " + connectAddress + ":" + connectPort + " failed: " + ex); 231 | } 232 | } 233 | 234 | #region Implementation of IStreamServer 235 | 236 | public int ConnectionCount { get; set; } 237 | public IDictionary Listeners { get; private set; } 238 | public IDictionary Streams { get; private set; } 239 | 240 | public void Log(object message) 241 | { 242 | OnMessageLogged(message.ToString()); 243 | } 244 | 245 | public void MessageReceived(ChannelMessage msg) 246 | { 247 | switch (msg.Type) 248 | { 249 | default: 250 | Log("Unknown message: " + msg); 251 | break; 252 | case MessageType.HelloResponse: 253 | { 254 | var response = (HelloResponse)msg; 255 | if (!SeenHello) 256 | { 257 | SeenHello = true; 258 | Log(response.MachineName + " connected to " + Environment.MachineName); 259 | OnConnected(response.MachineName); 260 | } 261 | } 262 | break; 263 | case MessageType.ListenResponse: 264 | { 265 | var response = (ListenResponse)msg; 266 | ClientListener listener = null; 267 | lock (clientListeners) 268 | { 269 | if (clientListeners.ContainsKey(response.ListenIndex)) listener = clientListeners[response.ListenIndex]; 270 | } 271 | if (listener != null) listener.ListenResponse(response); 272 | } 273 | break; 274 | case MessageType.AcceptRequest: 275 | { 276 | var request = (AcceptRequest)msg; 277 | ClientListener listener = null; 278 | lock (clientListeners) 279 | { 280 | if (clientListeners.ContainsKey(request.ListenIndex)) listener = clientListeners[request.ListenIndex]; 281 | } 282 | if (listener != null) listener.AcceptRequest(request); 283 | } 284 | break; 285 | case MessageType.ConnectResponse: 286 | ((ConnectResponse)msg).Process(this); 287 | break; 288 | case MessageType.StreamData: 289 | ((StreamData)msg).Process(this); 290 | break; 291 | case MessageType.StreamError: 292 | { 293 | var error = (StreamError)msg; 294 | ClientListener listener = null; 295 | lock (clientListeners) 296 | { 297 | if (clientListeners.ContainsKey(error.StreamIndex)) listener = clientListeners[error.StreamIndex]; 298 | } 299 | if (listener != null) 300 | { 301 | listener.StreamError(error); 302 | } 303 | else 304 | { 305 | ((StreamError)msg).Process(this); 306 | } 307 | } 308 | break; 309 | } 310 | } 311 | 312 | public bool WriteMessage(ChannelMessage msg) 313 | { 314 | var data = msg.ToByteArray(); 315 | int written; 316 | var ret = WtsApi32.WTSVirtualChannelWrite(mHandle, data, data.Length, out written); 317 | if (ret) return true; 318 | var ex = new Win32Exception(); 319 | if (!SeenHello && ex.NativeErrorCode == 1 /* Incorrect Function */) return false; 320 | Log("RDP Virtual channel Write Failed: " + ex.Message); 321 | return false; 322 | } 323 | 324 | #endregion 325 | } 326 | } 327 | -------------------------------------------------------------------------------- /TSTunnels.UI/UI.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace TSTunnels.UI 2 | { 3 | partial class UI 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.components = new System.ComponentModel.Container(); 32 | this.eventLogListBox = new System.Windows.Forms.ListBox(); 33 | this.eventLogGroupBox = new System.Windows.Forms.GroupBox(); 34 | this.portForwardingGroupBox = new System.Windows.Forms.GroupBox(); 35 | this.remoteRadioButton = new System.Windows.Forms.RadioButton(); 36 | this.localRadioButton = new System.Windows.Forms.RadioButton(); 37 | this.destinationTextBox = new System.Windows.Forms.TextBox(); 38 | this.addButton = new System.Windows.Forms.Button(); 39 | this.removeButton = new System.Windows.Forms.Button(); 40 | this.label4 = new System.Windows.Forms.Label(); 41 | this.label3 = new System.Windows.Forms.Label(); 42 | this.label2 = new System.Windows.Forms.Label(); 43 | this.label1 = new System.Windows.Forms.Label(); 44 | this.forwardedPortsListBox = new System.Windows.Forms.ListBox(); 45 | this.sourceTextBox = new System.Windows.Forms.TextBox(); 46 | this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); 47 | this.eventLogGroupBox.SuspendLayout(); 48 | this.portForwardingGroupBox.SuspendLayout(); 49 | this.SuspendLayout(); 50 | // 51 | // eventLogListBox 52 | // 53 | this.eventLogListBox.Dock = System.Windows.Forms.DockStyle.Fill; 54 | this.eventLogListBox.FormattingEnabled = true; 55 | this.eventLogListBox.Location = new System.Drawing.Point(3, 16); 56 | this.eventLogListBox.Name = "eventLogListBox"; 57 | this.eventLogListBox.Size = new System.Drawing.Size(650, 199); 58 | this.eventLogListBox.TabIndex = 0; 59 | // 60 | // eventLogGroupBox 61 | // 62 | this.eventLogGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 63 | | System.Windows.Forms.AnchorStyles.Left) 64 | | System.Windows.Forms.AnchorStyles.Right))); 65 | this.eventLogGroupBox.Controls.Add(this.eventLogListBox); 66 | this.eventLogGroupBox.Location = new System.Drawing.Point(12, 207); 67 | this.eventLogGroupBox.Name = "eventLogGroupBox"; 68 | this.eventLogGroupBox.Size = new System.Drawing.Size(656, 219); 69 | this.eventLogGroupBox.TabIndex = 1; 70 | this.eventLogGroupBox.TabStop = false; 71 | this.eventLogGroupBox.Text = "Event log"; 72 | // 73 | // portForwardingGroupBox 74 | // 75 | this.portForwardingGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 76 | | System.Windows.Forms.AnchorStyles.Right))); 77 | this.portForwardingGroupBox.Controls.Add(this.remoteRadioButton); 78 | this.portForwardingGroupBox.Controls.Add(this.localRadioButton); 79 | this.portForwardingGroupBox.Controls.Add(this.destinationTextBox); 80 | this.portForwardingGroupBox.Controls.Add(this.addButton); 81 | this.portForwardingGroupBox.Controls.Add(this.removeButton); 82 | this.portForwardingGroupBox.Controls.Add(this.label4); 83 | this.portForwardingGroupBox.Controls.Add(this.label3); 84 | this.portForwardingGroupBox.Controls.Add(this.label2); 85 | this.portForwardingGroupBox.Controls.Add(this.label1); 86 | this.portForwardingGroupBox.Controls.Add(this.forwardedPortsListBox); 87 | this.portForwardingGroupBox.Controls.Add(this.sourceTextBox); 88 | this.portForwardingGroupBox.Enabled = false; 89 | this.portForwardingGroupBox.Location = new System.Drawing.Point(12, 12); 90 | this.portForwardingGroupBox.Name = "portForwardingGroupBox"; 91 | this.portForwardingGroupBox.Size = new System.Drawing.Size(656, 189); 92 | this.portForwardingGroupBox.TabIndex = 0; 93 | this.portForwardingGroupBox.TabStop = false; 94 | this.portForwardingGroupBox.Text = "Port forwarding"; 95 | // 96 | // remoteRadioButton 97 | // 98 | this.remoteRadioButton.AutoSize = true; 99 | this.remoteRadioButton.Location = new System.Drawing.Point(494, 163); 100 | this.remoteRadioButton.Name = "remoteRadioButton"; 101 | this.remoteRadioButton.Size = new System.Drawing.Size(62, 17); 102 | this.remoteRadioButton.TabIndex = 6; 103 | this.remoteRadioButton.Text = "Re&mote"; 104 | this.remoteRadioButton.UseVisualStyleBackColor = true; 105 | // 106 | // localRadioButton 107 | // 108 | this.localRadioButton.AutoSize = true; 109 | this.localRadioButton.Checked = true; 110 | this.localRadioButton.Location = new System.Drawing.Point(437, 163); 111 | this.localRadioButton.Name = "localRadioButton"; 112 | this.localRadioButton.Size = new System.Drawing.Size(51, 17); 113 | this.localRadioButton.TabIndex = 5; 114 | this.localRadioButton.TabStop = true; 115 | this.localRadioButton.Text = "&Local"; 116 | this.localRadioButton.UseVisualStyleBackColor = true; 117 | // 118 | // destinationTextBox 119 | // 120 | this.destinationTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 121 | this.destinationTextBox.Location = new System.Drawing.Point(281, 162); 122 | this.destinationTextBox.Name = "destinationTextBox"; 123 | this.destinationTextBox.Size = new System.Drawing.Size(150, 20); 124 | this.destinationTextBox.TabIndex = 4; 125 | // 126 | // addButton 127 | // 128 | this.addButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 129 | this.addButton.Location = new System.Drawing.Point(562, 160); 130 | this.addButton.Name = "addButton"; 131 | this.addButton.Size = new System.Drawing.Size(88, 23); 132 | this.addButton.TabIndex = 7; 133 | this.addButton.Text = "A&dd"; 134 | this.addButton.UseVisualStyleBackColor = true; 135 | this.addButton.Click += new System.EventHandler(this.addButton_Click); 136 | // 137 | // removeButton 138 | // 139 | this.removeButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 140 | this.removeButton.Location = new System.Drawing.Point(562, 19); 141 | this.removeButton.Name = "removeButton"; 142 | this.removeButton.Size = new System.Drawing.Size(88, 23); 143 | this.removeButton.TabIndex = 10; 144 | this.removeButton.Text = "&Remove"; 145 | this.removeButton.UseVisualStyleBackColor = true; 146 | this.removeButton.Click += new System.EventHandler(this.removeButton_Click); 147 | // 148 | // label4 149 | // 150 | this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 151 | this.label4.AutoSize = true; 152 | this.label4.Location = new System.Drawing.Point(212, 165); 153 | this.label4.Name = "label4"; 154 | this.label4.Size = new System.Drawing.Size(63, 13); 155 | this.label4.TabIndex = 3; 156 | this.label4.Text = "Destination:"; 157 | // 158 | // label3 159 | // 160 | this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 161 | this.label3.AutoSize = true; 162 | this.label3.Location = new System.Drawing.Point(6, 165); 163 | this.label3.Name = "label3"; 164 | this.label3.Size = new System.Drawing.Size(44, 13); 165 | this.label3.TabIndex = 1; 166 | this.label3.Text = "Source:"; 167 | // 168 | // label2 169 | // 170 | this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 171 | this.label2.AutoSize = true; 172 | this.label2.Location = new System.Drawing.Point(6, 146); 173 | this.label2.Name = "label2"; 174 | this.label2.Size = new System.Drawing.Size(123, 13); 175 | this.label2.TabIndex = 0; 176 | this.label2.Text = "Add new forwarded port:"; 177 | // 178 | // label1 179 | // 180 | this.label1.AutoSize = true; 181 | this.label1.Location = new System.Drawing.Point(6, 24); 182 | this.label1.Name = "label1"; 183 | this.label1.Size = new System.Drawing.Size(86, 13); 184 | this.label1.TabIndex = 8; 185 | this.label1.Text = "Forwarded ports:"; 186 | // 187 | // forwardedPortsListBox 188 | // 189 | this.forwardedPortsListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 190 | | System.Windows.Forms.AnchorStyles.Left) 191 | | System.Windows.Forms.AnchorStyles.Right))); 192 | this.forwardedPortsListBox.FormattingEnabled = true; 193 | this.forwardedPortsListBox.Location = new System.Drawing.Point(9, 48); 194 | this.forwardedPortsListBox.Name = "forwardedPortsListBox"; 195 | this.forwardedPortsListBox.Size = new System.Drawing.Size(641, 95); 196 | this.forwardedPortsListBox.TabIndex = 9; 197 | // 198 | // sourceTextBox 199 | // 200 | this.sourceTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); 201 | this.sourceTextBox.Location = new System.Drawing.Point(56, 162); 202 | this.sourceTextBox.Name = "sourceTextBox"; 203 | this.sourceTextBox.Size = new System.Drawing.Size(150, 20); 204 | this.sourceTextBox.TabIndex = 2; 205 | // 206 | // UI 207 | // 208 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 209 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 210 | this.ClientSize = new System.Drawing.Size(680, 438); 211 | this.Controls.Add(this.portForwardingGroupBox); 212 | this.Controls.Add(this.eventLogGroupBox); 213 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 214 | this.MaximizeBox = false; 215 | this.Name = "UI"; 216 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 217 | this.Text = "Remote Desktop Tunnels"; 218 | this.Load += new System.EventHandler(this.UI_Load); 219 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.UI_FormClosed); 220 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.UI_FormClosing); 221 | this.eventLogGroupBox.ResumeLayout(false); 222 | this.portForwardingGroupBox.ResumeLayout(false); 223 | this.portForwardingGroupBox.PerformLayout(); 224 | this.ResumeLayout(false); 225 | 226 | } 227 | 228 | #endregion 229 | 230 | private System.Windows.Forms.ListBox eventLogListBox; 231 | private System.Windows.Forms.GroupBox eventLogGroupBox; 232 | private System.Windows.Forms.GroupBox portForwardingGroupBox; 233 | private System.Windows.Forms.ListBox forwardedPortsListBox; 234 | private System.Windows.Forms.TextBox sourceTextBox; 235 | private System.Windows.Forms.Button removeButton; 236 | private System.Windows.Forms.Label label2; 237 | private System.Windows.Forms.Label label1; 238 | private System.Windows.Forms.TextBox destinationTextBox; 239 | private System.Windows.Forms.Label label4; 240 | private System.Windows.Forms.Label label3; 241 | private System.Windows.Forms.Button addButton; 242 | private System.Windows.Forms.RadioButton remoteRadioButton; 243 | private System.Windows.Forms.RadioButton localRadioButton; 244 | private System.Windows.Forms.ToolTip toolTip1; 245 | } 246 | } 247 | 248 | --------------------------------------------------------------------------------