├── .gitignore ├── AutoTunnel.Proxy.Node ├── AutoTunnel.Proxy.Node.csproj ├── proxy.js └── proxyStarter.js ├── AutoTunnel.sln ├── AutoTunnel ├── AutoTunnel.csproj ├── BaseSender.cs ├── ClientSender.cs ├── Config │ ├── ConfigHelper.cs │ ├── MainConfig.cs │ ├── RemoteClientConfig.cs │ └── RemoteServerConfig.cs ├── Encryption │ ├── ClientHandshake.cs │ ├── DecryptHelper.cs │ ├── EncryptHelper.cs │ ├── PasswordHelper.cs │ └── ServerHandshake.cs ├── EndpointHelper.cs ├── FirewallHelper.cs ├── InterfaceHelper.cs ├── Listener.cs ├── Logging │ ├── AggregateLog.cs │ ├── ConsoleLog.cs │ ├── FileLog.cs │ ├── ILog.cs │ └── LogHelper.cs ├── NativeHelper.cs ├── PacketWriter.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── ReplySender.cs ├── Service │ ├── ConsoleHelper.cs │ ├── MainService.cs │ ├── MainServiceInstallHelper.cs │ └── MainServiceInstaller.cs ├── Starter.cs ├── StateFlags.cs ├── TunnelSession.cs ├── TunnelStorage.cs ├── WinDivert.cs ├── app.manifest ├── config.json ├── credits.txt ├── packages.config ├── x64 │ ├── WinDivert.dll │ └── WinDivert64.sys └── x86 │ ├── WinDivert.dll │ └── WinDivert32.sys ├── LICENSE ├── README.md ├── tunnel.png ├── tunnel_active.png ├── tunnel_establishing.png └── tunnel_hUB_icon.ico /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | *.user 4 | *.suo 5 | packages 6 | .tools 7 | *.snk 8 | sign.cmd 9 | _releases 10 | .idea 11 | -------------------------------------------------------------------------------- /AutoTunnel.Proxy.Node/AutoTunnel.Proxy.Node.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {9C40B1D7-BB7E-4B64-9B85-2C106910FF2A} 8 | {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 9 | Library 10 | Properties 11 | AutoTunnel.Proxy.Node 12 | AutoTunnel.Proxy.Node 13 | v4.5 14 | 512 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 47 | -------------------------------------------------------------------------------- /AutoTunnel.Proxy.Node/proxy.js: -------------------------------------------------------------------------------- 1 | const dgram = require('dgram'); 2 | const dns = require('dns'); 3 | 4 | var logFunc = console.log; 5 | var checkFunc = function () { 6 | return true; 7 | }; 8 | 9 | var start = function (portNumber) { 10 | var server = dgram.createSocket('udp4'); 11 | 12 | var proxyPairs = {}; 13 | var failPairs = {}; 14 | 15 | server.bind(portNumber, function () { 16 | address = server.address(); 17 | logFunc('Server listening ' + address.address + ':' + address.port); 18 | }); 19 | server.on('error', function (err) { 20 | logFunc('Server error: ', err); 21 | }); 22 | 23 | server.on('message', function (msg, ep) { 24 | // try { 25 | // proxy init 26 | if (ep.size % 16 !== 0 && msg[0] === 0x7) { 27 | // invalid data 28 | if (ep.size <= 9) 29 | return; 30 | var len = msg.readInt32LE(4); 31 | var dstHostAndPort = msg.toString('utf8', 8, 8 + len); 32 | var spl = dstHostAndPort.split(':'); 33 | var dstHost = spl[0]; 34 | var dstPort = (spl[1] || 12017) * 1; 35 | 36 | dns.lookup(dstHost, 4, function (err, addr) { 37 | if (err !== null) { 38 | logFunc('Cannot resolve host ' + dstHost, err); 39 | return; 40 | } 41 | 42 | if (!checkFunc(addr, dstHost)) { 43 | logFunc('Host ' + dstHost + " was considered as invalid for proxyfying"); 44 | return 45 | } 46 | 47 | dstHost = addr; 48 | 49 | var socket = dgram.createSocket('udp4'); 50 | socket.on('message', function (bmsg, bep) { 51 | var pair = proxyPairs[ep.address + ':' + ep.port]; 52 | if (pair) { 53 | pair.lastActivity = new Date().getTime(); 54 | server.send(bmsg, 0, bep.size, ep.port, ep.address); 55 | } 56 | }); 57 | proxyPairs[ep.address + ':' + ep.port] = { 58 | sourceHost: ep.address, 59 | sourcePort: ep.port, 60 | host: dstHost, 61 | port: dstPort, 62 | targetSocket: socket, 63 | lastActivity: new Date().getTime() 64 | }; 65 | logFunc('Estabilished tunnel ', ep.address + ':' + ep.port + '->' + dstHost + ':' + dstPort); 66 | 67 | // TODO: think about answer 68 | }); 69 | } else { 70 | var pair = proxyPairs[ep.address + ':' + ep.port]; 71 | 72 | if (!pair) { 73 | var failPair = failPairs[ep.address + ':' + ep.port]; 74 | // one-time error sending, to add info to sender about 'we do not know who are you' 75 | // sender should reconnect to proxy 76 | if (!failPair) { 77 | logFunc('Missing binding for ', ep.address + ':' + ep.port); 78 | failPairs[ep.address + ':' + ep.port] = { lastActivity: new Date().getTime() }; 79 | var buffer = Buffer.alloc ? Buffer.alloc(4) : new Buffer(4); 80 | buffer.writeUInt32LE(8); 81 | server.send(buffer, 0, 4, ep.port, ep.address); 82 | } 83 | } else { 84 | pair.lastActivity = new Date().getTime(); 85 | pair.targetSocket.send(msg, 0, ep.size, pair.port, pair.host); 86 | } 87 | } 88 | // } catch (e) { 89 | // logFunc('Error in proxy message processing', e); 90 | // } 91 | }); 92 | 93 | setInterval(function () { 94 | var now = new Date().getTime(); 95 | Object.keys(proxyPairs).forEach(function (p) { 96 | var proxyPair = proxyPairs[p]; 97 | if (now - proxyPair.lastActivity > 10 * 60 * 1000) { 98 | proxyPair.targetSocket.close(); 99 | delete proxyPairs[p]; 100 | logFunc('Removing idle session ' + proxyPair.sourceHost + ':' + proxyPair.sourcePort); 101 | } 102 | }); 103 | 104 | Object.keys(failPairs).forEach(function (p) { 105 | var proxyPair = failPairs[p]; 106 | if (now - proxyPair.lastActivity > 10 * 60 * 1000) { 107 | delete failPairs[p]; 108 | } 109 | }); 110 | }, 10 * 60 * 1000); 111 | }; 112 | 113 | module.exports = { 114 | start: function (portNumber) { 115 | return start(portNumber); 116 | }, 117 | setLogger: function (loggerFunc) { 118 | logFunc = loggerFunc; 119 | }, 120 | setAllowedTargetIpCheckFunc: function(checkingFunc) { 121 | checkFunc = checkingFunc; 122 | } 123 | }; -------------------------------------------------------------------------------- /AutoTunnel.Proxy.Node/proxyStarter.js: -------------------------------------------------------------------------------- 1 | var p = require('./proxy.js'); 2 | 3 | p.setAllowedTargetIpCheckFunc(function (dstAddr, dstHost) { 4 | // just for example 5 | console.log(dstAddr) 6 | return /192\.168\.\d+\.\d+/.test(dstAddr); 7 | }); 8 | 9 | p.start(12018); -------------------------------------------------------------------------------- /AutoTunnel.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2012 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTunnel", "AutoTunnel\AutoTunnel.csproj", "{BC409F07-4C77-4930-8597-B9BD5837E672}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoTunnel.Proxy.Node", "AutoTunnel.Proxy.Node\AutoTunnel.Proxy.Node.csproj", "{9C40B1D7-BB7E-4B64-9B85-2C106910FF2A}" 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 | {BC409F07-4C77-4930-8597-B9BD5837E672}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {BC409F07-4C77-4930-8597-B9BD5837E672}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {BC409F07-4C77-4930-8597-B9BD5837E672}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {BC409F07-4C77-4930-8597-B9BD5837E672}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {9C40B1D7-BB7E-4B64-9B85-2C106910FF2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {9C40B1D7-BB7E-4B64-9B85-2C106910FF2A}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {9C40B1D7-BB7E-4B64-9B85-2C106910FF2A}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {9C40B1D7-BB7E-4B64-9B85-2C106910FF2A}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /AutoTunnel/AutoTunnel.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {BC409F07-4C77-4930-8597-B9BD5837E672} 8 | {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 9 | Exe 10 | Properties 11 | Force.AutoTunnel 12 | AutoTunnel 13 | v4.0 14 | 512 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | false 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | false 36 | 37 | 38 | true 39 | 40 | 41 | false 42 | 43 | 44 | ..\force.snk 45 | 46 | 47 | app.manifest 48 | 49 | 50 | ..\tunnel_hUB_icon.ico 51 | 52 | 53 | 54 | ..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll 55 | True 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | PreserveNewest 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 | Component 100 | 101 | 102 | Component 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | force.snk 114 | 115 | 116 | 117 | PreserveNewest 118 | 119 | 120 | PreserveNewest 121 | 122 | 123 | 124 | 125 | PreserveNewest 126 | 127 | 128 | PreserveNewest 129 | 130 | 131 | 132 | 133 | tunnel_hUB_icon.ico 134 | 135 | 136 | 137 | 138 | tunnel_active.png 139 | 140 | 141 | 142 | 143 | tunnel_establishing.png 144 | 145 | 146 | 147 | 154 | -------------------------------------------------------------------------------- /AutoTunnel/BaseSender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Runtime.InteropServices; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | using Force.AutoTunnel.Logging; 8 | 9 | namespace Force.AutoTunnel 10 | { 11 | public abstract class BaseSender : IDisposable 12 | { 13 | private IntPtr _handle; 14 | 15 | private bool _isExiting; 16 | 17 | protected IPAddress DstAddr { get; set; } 18 | 19 | public DateTime LastActivity { get; private set; } 20 | 21 | protected readonly TunnelStorage Storage; 22 | 23 | public TunnelSession Session { get; set; } 24 | 25 | protected readonly int? ClampMss; 26 | 27 | protected BaseSender(TunnelSession session, IPAddress watchAddr, TunnelStorage storage, int? clampMss) 28 | { 29 | Storage = storage; 30 | Session = session; 31 | ClampMss = clampMss; 32 | 33 | ReInitDivert(watchAddr); 34 | Task.Factory.StartNew(StartInternal); 35 | } 36 | 37 | protected void ReInitDivert(IPAddress newDstAddr) 38 | { 39 | DstAddr = newDstAddr; 40 | if (_handle != IntPtr.Zero && _handle != (IntPtr)(-1)) 41 | WinDivert.WinDivertClose(_handle); 42 | 43 | // or (udp and udp.DstPort != 12017) 44 | _handle = WinDivert.WinDivertOpen("outbound and ip and (ip.DstAddr == " + newDstAddr + ")", WinDivert.LAYER_NETWORK, 0, 0); 45 | if (_handle == new IntPtr(-1)) 46 | { 47 | LogHelper.Log.WriteLine("Cannot open divert driver: " + Marshal.GetLastWin32Error()); 48 | Environment.Exit(1); 49 | } 50 | } 51 | 52 | protected abstract void Send(byte[] packet, int packetLen); 53 | 54 | public void UpdateLastActivity() 55 | { 56 | LastActivity = DateTime.UtcNow; 57 | } 58 | 59 | private void StartInternal() 60 | { 61 | byte[] packet = new byte[65536]; 62 | WinDivert.WinDivertAddress addr = new WinDivert.WinDivertAddress(); 63 | int packetLen = 0; 64 | while (!_isExiting) 65 | { 66 | var oldHandle = _handle; 67 | if (!WinDivert.WinDivertRecv(_handle, packet, packet.Length, ref addr, ref packetLen)) 68 | { 69 | // showing error only if handle is not removed and not changed 70 | if (_handle != IntPtr.Zero && oldHandle == _handle) 71 | { 72 | LogHelper.Log.WriteLine("Cannot receive network data: " + Marshal.GetLastWin32Error()); 73 | Thread.Sleep(1000); 74 | } 75 | 76 | continue; 77 | } 78 | // we cannot handle such packets, 79 | // todo: think about writing to log 80 | if (packetLen >= ((65507 / 16) * 16) - 16) 81 | { 82 | continue; 83 | } 84 | // Console.WriteLine("Recv: " + packet[16] + "." + packet[17] + "." + packet[18] + "." + packet[19] + ":" + (packet[23] | ((uint)packet[22] << 8))); 85 | if (packet[9] == 17) 86 | { 87 | var key = ((ulong)(packet[16] | ((uint)packet[17] << 8) | ((uint)packet[18] << 16) | (((uint)packet[19]) << 24)) << 16) | (packet[23] | ((uint)packet[22] << 8)); 88 | // do not catch this packet, it is our tunnel to other computer 89 | if (Storage.HasSession(key)) 90 | { 91 | var writeLen = 0; 92 | WinDivert.WinDivertSend(_handle, packet, packetLen, ref addr, ref writeLen); 93 | continue; 94 | } 95 | } 96 | 97 | // tcp SYN 98 | /*if (packet[9] == 6) 99 | { 100 | Console.WriteLine(packet[20 + 13].ToString("X2")); 101 | }*/ 102 | 103 | // tcp + syn + MSS + valid length 104 | if (ClampMss.HasValue) 105 | { 106 | if (packet[9] == 6 && (packet[20 + 13] & 2) != 0 && packet[20 + 20] == 2 && packetLen > 20 + 24) 107 | { 108 | var len = packet[20 + 22] << 8 | packet[20 + 23]; 109 | Console.WriteLine(packet[20 + 22] << 8 | packet[20 + 23]); 110 | // UDP + encryption 111 | if (ClampMss.Value == 0) len -= 28 + 32; 112 | else len = ClampMss.Value - 28 + 32; 113 | // len = 1200; 114 | packet[20 + 22] = (byte)(len >> 8); 115 | packet[20 + 23] = (byte)(len & 0xFF); 116 | addr.DisablePseudoChecksums(); 117 | WinDivert.WinDivertHelperCalcChecksums(packet, packetLen, ref addr, 0); 118 | } 119 | } 120 | 121 | // if checksums are offloaded we need to recalculate it manually before send 122 | if (addr.HasPseudoChecksum) 123 | { 124 | addr.DisablePseudoChecksums(); 125 | WinDivert.WinDivertHelperCalcChecksums(packet, packetLen, ref addr, 0); 126 | } 127 | 128 | // Console.WriteLine("> " + packetLen + " " + addr.IfIdx + " " + addr.SubIfIdx + " " + addr.Direction); 129 | try 130 | { 131 | Send(packet, packetLen); 132 | } 133 | catch (Exception ex) 134 | { 135 | LogHelper.Log.WriteLine(ex); 136 | } 137 | } 138 | } 139 | 140 | public virtual void Dispose() 141 | { 142 | _isExiting = true; 143 | if (_handle != IntPtr.Zero && _handle != (IntPtr)(-1)) 144 | WinDivert.WinDivertClose(_handle); 145 | _handle = IntPtr.Zero; 146 | } 147 | 148 | ~BaseSender() 149 | { 150 | Dispose(); 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /AutoTunnel/ClientSender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net; 4 | using System.Net.NetworkInformation; 5 | using System.Net.Sockets; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | using Force.AutoTunnel.Config; 11 | using Force.AutoTunnel.Encryption; 12 | using Force.AutoTunnel.Logging; 13 | 14 | namespace Force.AutoTunnel 15 | { 16 | public class ClientSender : BaseSender 17 | { 18 | private Socket _socket; 19 | 20 | private bool _disposed; 21 | 22 | private EncryptHelper _encryptHelper; 23 | 24 | private readonly byte[] _serverKey; 25 | 26 | private readonly ManualResetEvent _initingEvent = new ManualResetEvent(false); 27 | 28 | private readonly PacketWriter _packetWriter; 29 | 30 | private bool _isInited; 31 | 32 | private readonly RemoteServerConfig _config; 33 | 34 | private IPEndPoint _connectEP; 35 | 36 | public ClientSender(RemoteServerConfig config, TunnelStorage storage) 37 | : base(null, EndpointHelper.ParseEndPoint(config.TunnelHost, 1).Address, storage, config.ClampMss) 38 | { 39 | storage.OutgoingConnectionAdresses.Add(DstAddr); 40 | _config = config; 41 | _serverKey = PasswordHelper.GenerateKey(config.Key); 42 | _packetWriter = new PacketWriter(); 43 | 44 | LogHelper.Log.WriteLine("Tunnel watcher was created for " + config.TunnelHost); 45 | 46 | Task.Factory.StartNew(ReceiveCycle); 47 | Task.Factory.StartNew(PingCycle); 48 | if (config.ConnectOnStart) 49 | Init(); 50 | IPAddress dummy; 51 | if (!IPAddress.TryParse(config.TunnelHost, out dummy)) 52 | Task.Factory.StartNew(CheckHostChange); 53 | } 54 | 55 | private void CheckHostChange() 56 | { 57 | // checking if target host has changed it ip address to other 58 | while (!_disposed) 59 | { 60 | var addresses = Dns.GetHostAddresses(_config.TunnelHost); 61 | if (addresses.Length > 0) 62 | { 63 | if (!addresses.Any(x => x.Equals(DstAddr))) 64 | { 65 | Storage.OutgoingConnectionAdresses.Remove(DstAddr); 66 | DstAddr = addresses.First(); 67 | ReInitDivert(DstAddr); 68 | Storage.OutgoingConnectionAdresses.Add(DstAddr); 69 | } 70 | } 71 | 72 | Thread.Sleep(60 * 1000); 73 | } 74 | } 75 | 76 | private int _isIniting; 77 | 78 | private DateTime _lastInitRequest; 79 | 80 | private void Init() 81 | { 82 | _lastInitRequest = DateTime.UtcNow; 83 | if (_isIniting == 1) 84 | return; 85 | Task.Factory.StartNew(InitInternal); 86 | } 87 | 88 | private void CloseSocket() 89 | { 90 | if (_socket != null) 91 | { 92 | _socket.Dispose(); 93 | } 94 | 95 | _socket = null; 96 | } 97 | 98 | private void InitInternal() 99 | { 100 | if (Interlocked.CompareExchange(ref _isIniting, 1, 0) == 1) 101 | return; 102 | DecryptHelper decryptHelper = null; 103 | EncryptHelper encryptHelper = null; 104 | try 105 | { 106 | _initingEvent.Reset(); 107 | 108 | var cs = new ClientHandshake(); 109 | var sendingPacketLen = cs.GetPacketForSending(); 110 | encryptHelper = new EncryptHelper(_serverKey); 111 | decryptHelper = new DecryptHelper(_serverKey); 112 | 113 | var proxyEP = !string.IsNullOrEmpty(_config.ProxyHost) ? EndpointHelper.ParseEndPoint(_config.ProxyHost, 12018) : null; 114 | var connectEP = proxyEP == null ? EndpointHelper.ParseEndPoint(_config.ConnectHost, 12017) : null; 115 | 116 | var destEP = proxyEP ?? connectEP; 117 | 118 | if (!destEP.Equals(_connectEP) && _connectEP != null) 119 | { 120 | Storage.RemoveSession(_connectEP); 121 | } 122 | 123 | _connectEP = destEP; 124 | 125 | Storage.IncrementEstablishing(); 126 | Storage.AddSession(new byte[16], destEP).IsClientSession = true; 127 | 128 | if (proxyEP != null) 129 | LogHelper.Log.WriteLine("Initializing connection to " + _config.ConnectHost + " via proxy " + proxyEP); 130 | else 131 | LogHelper.Log.WriteLine("Initializing connection to " + connectEP); 132 | 133 | var packetToSend = encryptHelper.Encrypt(cs.SendingPacket, sendingPacketLen); 134 | var initBuf = new byte[packetToSend.Count + 4]; 135 | Buffer.BlockCopy(packetToSend.Array, 0, initBuf, 4, packetToSend.Count); 136 | initBuf[0] = (byte)StateFlags.Connecting; 137 | initBuf[1] = 1; // version 138 | var recLength = 0; 139 | 140 | // killing old socket 141 | CloseSocket(); 142 | _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 143 | _socket.Connect(destEP); 144 | 145 | Task task = null; 146 | var period = 2000; 147 | while (true) 148 | { 149 | if (proxyEP != null) 150 | { 151 | // do not encrypt data to proxy, bcs it should be simple and do not work with encryption 152 | // can be changed in future version 153 | var bytesHost = Encoding.UTF8.GetBytes(_config.ConnectHost); 154 | var totalLen = 4 + 4 + bytesHost.Length; 155 | // should not be divided to 16 (we need to separate message from usual packets 156 | if (totalLen % 16 == 0) totalLen++; 157 | var proxyBuf = new byte[totalLen]; 158 | proxyBuf[0] = (byte)StateFlags.ProxyConnecting; 159 | proxyBuf[1] = 1; // version 160 | proxyBuf[4] = (byte)bytesHost.Length; 161 | proxyBuf[5] = (byte)(bytesHost.Length >> 8); 162 | proxyBuf[6] = (byte)(bytesHost.Length >> 16); 163 | proxyBuf[7] = (byte)(bytesHost.Length >> 24); 164 | Buffer.BlockCopy(bytesHost, 0, proxyBuf, 8, bytesHost.Length); 165 | _socket.Send(proxyBuf, proxyBuf.Length, SocketFlags.None); 166 | // just to give more chances to process this message by other parts of network 167 | Thread.Sleep(10); 168 | } 169 | 170 | _socket.Send(initBuf, initBuf.Length, SocketFlags.None); 171 | task = task ?? Task.Factory.StartNew(() => recLength = _socket.Receive(_receiveBuffer)); 172 | /*var sw = Stopwatch.StartNew(); 173 | recLength = _socket.Receive(_receiveBuffer); 174 | Console.WriteLine(sw.ElapsedMilliseconds);*/ 175 | 176 | if (task.Wait(period)) 177 | break; 178 | 179 | period = Math.Min(period + 2000, 1000 * 60); 180 | 181 | LogHelper.Log.WriteLine("No response from server " + destEP); 182 | if (!_config.ConnectOnStart && DateTime.UtcNow.Subtract(_lastInitRequest).TotalSeconds > 60) 183 | { 184 | LogHelper.Log.WriteLine("Stopping connect atteptions to " + destEP + " until another request will occur"); 185 | } 186 | } 187 | 188 | if (recLength < 4 || _receiveBuffer[0] != (byte)StateFlags.ConnectAnswer) 189 | { 190 | LogHelper.Log.WriteLine("Invalid server response"); 191 | Storage.RemoveSession(destEP); 192 | return; 193 | } 194 | 195 | var decLen = decryptHelper.Decrypt(_receiveBuffer, 4); 196 | if (decLen < 9) 197 | { 198 | LogHelper.Log.WriteLine("Invalid server response"); 199 | Storage.RemoveSession(destEP); 200 | return; 201 | } 202 | 203 | var sessionKey = cs.GetPacketFromServer(decryptHelper.InnerBuf, decLen); 204 | // previous encrypt helper 205 | if (_encryptHelper != null) 206 | _encryptHelper.Dispose(); 207 | _encryptHelper = new EncryptHelper(sessionKey); 208 | var session = Storage.GetSession(destEP); 209 | session.Decryptor = new DecryptHelper(sessionKey); 210 | Session = session; 211 | LogHelper.Log.WriteLine("Initialized connection to " + destEP); 212 | _isInited = true; 213 | _initingEvent.Set(); 214 | 215 | // after connect - sending packet to estabilish backward connection 216 | using (var p = new Ping()) p.Send(DstAddr, 1); 217 | } 218 | finally 219 | { 220 | Interlocked.Exchange(ref _isIniting, 0); 221 | Storage.DecrementEstablishing(); 222 | if (decryptHelper != null) decryptHelper.Dispose(); 223 | if (encryptHelper != null) encryptHelper.Dispose(); 224 | } 225 | } 226 | 227 | private void DropInit() 228 | { 229 | _isInited = false; 230 | _initingEvent.Reset(); 231 | if (_config.KeepAlive) 232 | Init(); 233 | } 234 | 235 | private void PingCycle() 236 | { 237 | DateTime lastPingDate = DateTime.MinValue; 238 | var pingSpan = TimeSpan.FromSeconds(_config.PingInterval); 239 | 240 | while (!_disposed) 241 | { 242 | if (_isInited) 243 | { 244 | // problem with server? no answers, dropping connection 245 | if (Session.SendReceiveDifference > TimeSpan.FromSeconds(_config.PingInterval * 2)) 246 | { 247 | DropInit(); 248 | } 249 | 250 | if ((_config.KeepAlive && DateTime.UtcNow.Subtract(lastPingDate) > pingSpan) || Session.SendReceiveDifference > pingSpan) 251 | { 252 | _socket.Send(new byte[] { (byte)StateFlags.Ping, 0, 0, 0 }, 4, SocketFlags.None); 253 | lastPingDate = DateTime.UtcNow; 254 | } 255 | } 256 | else 257 | { 258 | // force renew connection attempt 259 | if (_config.KeepAlive) 260 | _lastInitRequest = DateTime.UtcNow; 261 | } 262 | 263 | Thread.Sleep(1000); 264 | } 265 | } 266 | 267 | protected override void Send(byte[] packet, int packetLen) 268 | { 269 | if (!_isInited) 270 | { 271 | Init(); 272 | // if (!_initingEvent.WaitOne(4000)) 273 | // return; 274 | } 275 | 276 | if (_isInited) 277 | { 278 | var packetToSend = _encryptHelper.Encrypt(packet, packetLen); 279 | Session.UpdateReceiveActivity(); 280 | _socket.Send(packetToSend.Array, packetToSend.Count, SocketFlags.None); 281 | } 282 | } 283 | 284 | private readonly byte[] _receiveBuffer = new byte[65536]; 285 | 286 | private void ReceiveCycle() 287 | { 288 | byte[] buf = _receiveBuffer; 289 | while (!_disposed) 290 | { 291 | if (!_isInited) 292 | _initingEvent.WaitOne(); 293 | if (_disposed) 294 | return; 295 | 296 | int len; 297 | 298 | try 299 | { 300 | len = _socket.Receive(buf); 301 | } 302 | catch (Exception/* ex*/) 303 | { 304 | if (_isIniting == 1 && _isInited) 305 | { 306 | LogHelper.Log.WriteLine("Receive data error"); 307 | _isInited = false; 308 | // LogHelper.Log.WriteLine(ex); 309 | CloseSocket(); 310 | 311 | if (_config.KeepAlive) Init(); 312 | } 313 | 314 | Thread.Sleep(1000); 315 | continue; 316 | } 317 | 318 | // just drop data, assume that it is invalid 319 | if (len % 16 != 0) 320 | { 321 | // in any case, this is error 322 | if (buf[0] != (byte)StateFlags.Pong) 323 | { 324 | LogHelper.Log.WriteLine("Received an error flag from " + _socket.RemoteEndPoint); 325 | DropInit(); 326 | continue; 327 | } 328 | } 329 | 330 | Session.UpdateReceiveActivity(); 331 | 332 | var decryptHelper = Session.Decryptor; 333 | var decLen = decryptHelper.Decrypt(buf, 0); 334 | _packetWriter.Write(decryptHelper.InnerBuf, decLen); 335 | } 336 | } 337 | 338 | public override void Dispose() 339 | { 340 | base.Dispose(); 341 | _disposed = true; 342 | _initingEvent.Set(); 343 | CloseSocket(); 344 | } 345 | } 346 | } 347 | -------------------------------------------------------------------------------- /AutoTunnel/Config/ConfigHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | using Force.AutoTunnel.Logging; 7 | 8 | using Newtonsoft.Json; 9 | 10 | namespace Force.AutoTunnel.Config 11 | { 12 | public static class ConfigHelper 13 | { 14 | public static MainConfig Config { get; private set; } 15 | 16 | private static FileSystemWatcher _fsw; 17 | 18 | public static bool LoadConfig(bool isFirstTime) 19 | { 20 | try 21 | { 22 | if (_fsw != null) 23 | { 24 | _fsw.Dispose(); 25 | _fsw = null; 26 | } 27 | 28 | if (!isFirstTime) 29 | LogHelper.Log.WriteLine("Reloading config"); 30 | 31 | var configPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "config.json"); 32 | if (!File.Exists(configPath)) 33 | { 34 | if (isFirstTime) 35 | Console.Error.WriteLine("Missing config file"); 36 | else 37 | LogHelper.Log.WriteLine("Missing config file"); 38 | 39 | return false; 40 | } 41 | 42 | MainConfig config; 43 | using (var f = File.OpenRead(configPath)) 44 | config = new JsonSerializer().Deserialize(new JsonTextReader(new StreamReader(f))); 45 | 46 | if (config.RemoteClients == null) 47 | config.RemoteClients = new RemoteClientConfig[0]; 48 | if (config.RemoteClients.Length == 0) 49 | config.EnableListening = false; 50 | if (config.RemoteServers == null) 51 | config.RemoteServers = new RemoteServerConfig[0]; 52 | foreach (var remoteServerConfig in config.RemoteServers) 53 | { 54 | if (string.IsNullOrEmpty(remoteServerConfig.ConnectHost) && string.IsNullOrEmpty(remoteServerConfig.TunnelHost)) 55 | throw new InvalidOperationException("Missing host info in config"); 56 | 57 | if (string.IsNullOrEmpty(remoteServerConfig.TunnelHost)) 58 | remoteServerConfig.TunnelHost = remoteServerConfig.ConnectHost; 59 | if (string.IsNullOrEmpty(remoteServerConfig.ConnectHost)) 60 | remoteServerConfig.ConnectHost = remoteServerConfig.TunnelHost; 61 | } 62 | 63 | if (!isFirstTime) 64 | Starter.Stop(); 65 | 66 | var log = new AggregateLog(); 67 | if (Environment.UserInteractive) log.AddLog(new ConsoleLog()); 68 | if (!string.IsNullOrEmpty(config.LogFileName)) log.AddLog(new FileLog(config.LogFileName)); 69 | LogHelper.SetLog(log); 70 | 71 | Config = config; 72 | 73 | if (!isFirstTime) 74 | Starter.Start(); 75 | 76 | if (config.AutoReloadOnChange) 77 | { 78 | _fsw = new FileSystemWatcher(Path.GetDirectoryName(configPath) ?? string.Empty, Path.GetFileName(configPath) ?? string.Empty); 79 | _fsw.Changed += FswOnChanged; 80 | _fsw.Created += FswOnChanged; 81 | _fsw.Deleted += FswOnChanged; 82 | _fsw.EnableRaisingEvents = true; 83 | } 84 | } 85 | catch (Exception ex) 86 | { 87 | if (isFirstTime) Console.Error.WriteLine("Error in parsing config: " + ex.Message); 88 | else 89 | { 90 | LogHelper.Log.WriteLine("Error in parsing config. Leaving old config " + ex.Message); 91 | } 92 | 93 | return false; 94 | } 95 | 96 | return true; 97 | } 98 | 99 | private static DateTime _reloadTime; 100 | 101 | private static Task _activatedTask; 102 | 103 | private static void FswOnChanged(object sender, FileSystemEventArgs fileSystemEventArgs) 104 | { 105 | _reloadTime = DateTime.UtcNow.AddSeconds(4); 106 | if (_activatedTask != null) 107 | return; 108 | _activatedTask = Task.Factory.StartNew( 109 | () => 110 | { 111 | while (true) 112 | { 113 | Thread.Sleep(TimeSpan.FromSeconds(1)); 114 | if (DateTime.UtcNow > _reloadTime) 115 | { 116 | _activatedTask = null; 117 | LoadConfig(false); 118 | break; 119 | } 120 | } 121 | }); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /AutoTunnel/Config/MainConfig.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace Force.AutoTunnel.Config 6 | { 7 | public class MainConfig 8 | { 9 | [DefaultValue(true)] 10 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 11 | public bool EnableListening { get; set; } 12 | 13 | [DefaultValue(true)] 14 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 15 | public bool AddFirewallRule { get; set; } 16 | 17 | public RemoteClientConfig[] RemoteClients { get; set; } 18 | 19 | public string ListenAddress { get; set; } 20 | 21 | [DefaultValue(12017)] 22 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 23 | public int Port { get; set; } 24 | 25 | public RemoteServerConfig[] RemoteServers { get; set; } 26 | 27 | [DefaultValue(10 * 60)] 28 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 29 | public int IdleSessionTime { get; set; } 30 | 31 | public string LogFileName { get; set; } 32 | 33 | [DefaultValue(15)] 34 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 35 | public int PingBackTime { get; set; } 36 | 37 | [DefaultValue(true)] 38 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 39 | public bool AutoReloadOnChange { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /AutoTunnel/Config/RemoteClientConfig.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Force.AutoTunnel.Config 4 | { 5 | public class RemoteClientConfig 6 | { 7 | public string Key { get; set; } 8 | 9 | [JsonIgnore] 10 | public byte[] BinaryKey { get; set; } 11 | 12 | public string Description { get; set; } 13 | 14 | public int? ClampMss { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /AutoTunnel/Config/RemoteServerConfig.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | 3 | using Newtonsoft.Json; 4 | 5 | namespace Force.AutoTunnel.Config 6 | { 7 | public class RemoteServerConfig 8 | { 9 | public string TunnelHost { get; set; } 10 | 11 | public string ProxyHost { get; set; } 12 | 13 | public string ConnectHost { get; set; } 14 | 15 | public string Key { get; set; } 16 | 17 | public bool KeepAlive { get; set; } 18 | 19 | public bool ConnectOnStart { get; set; } 20 | 21 | public int? ClampMss { get; set; } 22 | 23 | [DefaultValue(15)] 24 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] 25 | public int PingInterval { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AutoTunnel/Encryption/ClientHandshake.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace Force.AutoTunnel.Encryption 5 | { 6 | public class ClientHandshake 7 | { 8 | private readonly string _publicKey; 9 | 10 | private readonly string _privateKey; 11 | 12 | public ClientHandshake() 13 | { 14 | var tuple = PasswordHelper.CreateRsa(); 15 | _publicKey = tuple.Item1; 16 | _privateKey = tuple.Item2; 17 | } 18 | 19 | public byte[] SendingPacket { get; private set; } 20 | 21 | public int GetPacketForSending() 22 | { 23 | var toSend = Encoding.UTF8.GetBytes(_publicKey); 24 | SendingPacket = new byte[4096]; 25 | var outBuf = SendingPacket; 26 | outBuf[0] = 0x1; 27 | outBuf[1] = 0x0; 28 | outBuf[2] = (byte)'A'; 29 | outBuf[3] = (byte)'T'; 30 | Buffer.BlockCopy(toSend, 0, outBuf, 4, toSend.Length); 31 | 32 | return toSend.Length + 4; 33 | } 34 | 35 | public byte[] GetPacketFromServer(byte[] data, int dataLen) 36 | { 37 | var tb = new byte[dataLen]; 38 | Buffer.BlockCopy(data, 0, tb, 0, dataLen); 39 | return PasswordHelper.Decrypt(_privateKey, tb); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /AutoTunnel/Encryption/DecryptHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace Force.AutoTunnel.Encryption 5 | { 6 | public class DecryptHelper : IDisposable 7 | { 8 | private readonly byte[] _innerBuf = new byte[65536]; 9 | 10 | private readonly Aes _aes; 11 | 12 | private readonly byte[] _headerBuf = new byte[16]; 13 | 14 | public byte[] InnerBuf 15 | { 16 | get 17 | { 18 | return _innerBuf; 19 | } 20 | } 21 | 22 | public DecryptHelper(byte[] key) 23 | { 24 | Aes aes; 25 | aes = Aes.Create(); 26 | aes.Key = key; 27 | aes.IV = new byte[16]; 28 | aes.Mode = CipherMode.CBC; 29 | aes.Padding = PaddingMode.None; 30 | _aes = aes; 31 | // _decryptor = aes.CreateDecryptor(); 32 | } 33 | 34 | public int Decrypt(byte[] data, int offset) 35 | { 36 | var hb = _headerBuf; 37 | // strange situation, decryptor cannot be used multiple times, problem with resetting cbc data, or my fault... 38 | // but encrypting is work with extracted encryptor 39 | using (var decryptor = _aes.CreateDecryptor()) 40 | { 41 | decryptor.TransformBlock(data, offset, 16, hb, 0); 42 | var len = hb[0] | (hb[1] << 8) | (hb[2] << 16) | (hb[3] << 24); 43 | if (len > data.Length) return -1; 44 | if (hb[4] != 1 || hb[5] != 0 || hb[6] != 'A' || hb[7] != 'T') return -1; 45 | var len16 = (len + 15) & ~15; 46 | if (len < 0 || len > _innerBuf.Length) return -1; 47 | decryptor.TransformBlock(data, offset + 16, len16, _innerBuf, 0); 48 | // decryptor.TransformFinalBlock(new byte[0], 0, 0); 49 | return len; 50 | } 51 | } 52 | 53 | public void Dispose() 54 | { 55 | _aes.Dispose(); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /AutoTunnel/Encryption/EncryptHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Cryptography; 3 | 4 | namespace Force.AutoTunnel.Encryption 5 | { 6 | public class EncryptHelper : IDisposable 7 | { 8 | private readonly RandomNumberGenerator _random = RandomNumberGenerator.Create(); 9 | 10 | private readonly byte[] _innerBuf = new byte[65536]; 11 | 12 | private readonly byte[] _headerBuf = new byte[16]; 13 | 14 | private readonly ICryptoTransform _encryptor; 15 | 16 | private readonly Aes _aes; 17 | 18 | public EncryptHelper(byte[] key) 19 | { 20 | Aes aes; 21 | aes = Aes.Create(); 22 | aes.Key = key; 23 | aes.IV = new byte[16]; 24 | aes.Mode = CipherMode.CBC; 25 | aes.Padding = PaddingMode.None; 26 | _aes = aes; 27 | _encryptor = aes.CreateEncryptor(); 28 | } 29 | 30 | public ArraySegment Encrypt(byte[] data, int len) 31 | { 32 | var hb = _headerBuf; 33 | _random.GetBytes(hb); 34 | hb[0] = (byte)(len & 0xff); 35 | hb[1] = (byte)((len >> 8) & 0xff); 36 | hb[2] = (byte)((len >> 16) & 0xff); 37 | hb[3] = (byte)((len >> 24) & 0xff); 38 | hb[4] = 0x1; 39 | hb[5] = 0x0; 40 | hb[6] = (byte)'A'; 41 | hb[7] = (byte)'T'; 42 | 43 | _encryptor.TransformBlock(hb, 0, 16, _innerBuf, 0); 44 | var tl = 0; 45 | if (len > 0) 46 | tl = _encryptor.TransformBlock(data, 0, (len + 15) & ~15, _innerBuf, 16); 47 | 48 | _encryptor.TransformFinalBlock(new byte[0], 0, 0); 49 | return new ArraySegment(_innerBuf, 0, tl + 16); 50 | } 51 | 52 | public void Dispose() 53 | { 54 | // _encryptor.Dispose(); 55 | _aes.Dispose(); 56 | _random.Dispose(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /AutoTunnel/Encryption/PasswordHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Security.Cryptography; 4 | 5 | namespace Force.AutoTunnel 6 | { 7 | public class PasswordHelper 8 | { 9 | public static byte[] GenerateKey(string password) 10 | { 11 | // we encrypt all data with random value, so, we can use static password here 12 | return new Rfc2898DeriveBytes(string.IsNullOrEmpty(password) ? "no_password" : password, "AutoTunnel".Select(x => (byte)x).ToArray(), 4096).GetBytes(16); 13 | } 14 | 15 | public static Tuple CreateRsa() 16 | { 17 | using (var rsa = RSA.Create()) 18 | { 19 | rsa.KeySize = 2048; 20 | return new Tuple(rsa.ToXmlString(false), rsa.ToXmlString(true)); 21 | } 22 | } 23 | 24 | public static byte[] Encrypt(string publicRsaKey, byte[] data) 25 | { 26 | using (var rsa = new RSACryptoServiceProvider()) 27 | { 28 | rsa.FromXmlString(publicRsaKey); 29 | return rsa.Encrypt(data, false); 30 | } 31 | } 32 | 33 | public static byte[] Decrypt(string privateRsaKey, byte[] data) 34 | { 35 | using (var rsa = new RSACryptoServiceProvider()) 36 | { 37 | rsa.FromXmlString(privateRsaKey); 38 | return rsa.Decrypt(data, false); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /AutoTunnel/Encryption/ServerHandshake.cs: -------------------------------------------------------------------------------- 1 | using System.Security.Cryptography; 2 | using System.Text; 3 | 4 | namespace Force.AutoTunnel.Encryption 5 | { 6 | public class ServerHandshake 7 | { 8 | public ServerHandshake() 9 | { 10 | } 11 | 12 | public byte[] SessionKey { get; private set; } 13 | 14 | public byte[] GetOutPacket(byte[] inPacket, int len) 15 | { 16 | if (inPacket[0] != 1 || inPacket[1] != 0 || inPacket[2] != 'A' || inPacket[3] != 'T') return null; 17 | var publicRsa = Encoding.UTF8.GetString(inPacket, 4, inPacket.Length - 4); 18 | SessionKey = new byte[16]; 19 | using (var random = RandomNumberGenerator.Create()) 20 | random.GetBytes(SessionKey); 21 | return PasswordHelper.Encrypt(publicRsa, SessionKey); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /AutoTunnel/EndpointHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net; 4 | 5 | namespace Force.AutoTunnel 6 | { 7 | public static class EndpointHelper 8 | { 9 | public static IPEndPoint ParseEndPoint(string address, int defaultPort) 10 | { 11 | var sepIdx = address.IndexOf(':'); 12 | string host = address; 13 | int port = defaultPort; 14 | if (sepIdx >= 0) 15 | { 16 | host = address.Substring(0, sepIdx); 17 | port = Convert.ToInt32(address.Remove(0, sepIdx + 1)); 18 | } 19 | 20 | IPAddress ipAddress; 21 | if (!IPAddress.TryParse(host, out ipAddress)) 22 | ipAddress = Dns.GetHostAddresses(host).First(); 23 | 24 | return new IPEndPoint(ipAddress, port); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AutoTunnel/FirewallHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | 5 | namespace Force.AutoTunnel 6 | { 7 | public static class FirewallHelper 8 | { 9 | public static void AddOpenFirewallRule(string port) 10 | { 11 | if (Environment.OSVersion.Version.Major < 6) 12 | ProcessRunner.RunProcess("netsh", "firewall add portopening TCP " + port + " AutoTunnel ENABLE all"); 13 | else 14 | { 15 | ProcessRunner.RunProcess("netsh", "advfirewall firewall delete rule name=\"AutoTunnel\" protocol=UDP dir=in localport=" + port); 16 | ProcessRunner.RunProcess("netsh", "advfirewall firewall add rule name=\"AutoTunnel\" protocol=UDP dir=in localport=" + port + " action=allow"); 17 | } 18 | } 19 | 20 | public static void DeleteFirewallRule(string port) 21 | { 22 | if (Environment.OSVersion.Version.Major < 6) 23 | { 24 | ProcessRunner.RunProcess("netsh", "firewall delete portopening TCP " + port); 25 | } 26 | else 27 | { 28 | ProcessRunner.RunProcess("netsh", "advfirewall firewall delete rule name=\"AutoTunnel\" protocol=UDP dir=in"); 29 | } 30 | } 31 | 32 | public static class ProcessRunner 33 | { 34 | public static string RunProcess(string fileName, string args) 35 | { 36 | using (var process = 37 | Process.Start( 38 | new ProcessStartInfo 39 | { 40 | FileName = fileName, 41 | Arguments = args, 42 | WorkingDirectory = Path.GetDirectoryName(fileName) ?? Environment.CurrentDirectory, 43 | UseShellExecute = false, 44 | RedirectStandardError = true, 45 | RedirectStandardInput = true, // don't remove or change these 2 lines, 46 | RedirectStandardOutput = true, 47 | // "The handle is invalid" message will flood error output otherwise 48 | CreateNoWindow = true 49 | })) 50 | { 51 | string errors = process.StandardError.ReadToEnd(); 52 | process.WaitForExit(); 53 | return string.IsNullOrEmpty(errors) ? null : errors; 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /AutoTunnel/InterfaceHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Net.NetworkInformation; 2 | using System.Reflection; 3 | 4 | namespace Force.AutoTunnel 5 | { 6 | public static class InterfaceHelper 7 | { 8 | public static uint GetInterfaceId() 9 | { 10 | NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces(); 11 | foreach (NetworkInterface interface2 in allNetworkInterfaces) 12 | { 13 | if ((((interface2.OperationalStatus == OperationalStatus.Up) && (interface2.Speed > 0L)) && (interface2.NetworkInterfaceType != NetworkInterfaceType.Loopback)) && (interface2.NetworkInterfaceType != NetworkInterfaceType.Tunnel)) 14 | { 15 | var prop = interface2.GetType().GetField("index", BindingFlags.Instance | BindingFlags.NonPublic); 16 | return (uint)prop.GetValue(interface2); 17 | // Console.WriteLine(interface2.Id + " " + interface2.Name + " " + prop.GetValue(interface2)); 18 | } 19 | } 20 | 21 | return 0; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /AutoTunnel/Listener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | using Force.AutoTunnel.Config; 9 | using Force.AutoTunnel.Encryption; 10 | using Force.AutoTunnel.Logging; 11 | 12 | namespace Force.AutoTunnel 13 | { 14 | public class Listener : IDisposable 15 | { 16 | private readonly TunnelStorage _storage; 17 | 18 | private readonly MainConfig _config; 19 | 20 | private readonly PacketWriter _packetWriter; 21 | 22 | private bool _disposed; 23 | 24 | private readonly ManualResetEvent _stopEvent = new ManualResetEvent(false); 25 | 26 | public Listener(TunnelStorage storage, MainConfig config) 27 | { 28 | _storage = storage; 29 | _config = config; 30 | 31 | _packetWriter = new PacketWriter(); 32 | } 33 | 34 | public void Start() 35 | { 36 | LogHelper.Log.WriteLine("Started listening for incoming connections on " 37 | + (string.IsNullOrEmpty(_config.ListenAddress) ? string.Empty : (_config.ListenAddress + ":")) 38 | + _config.Port); 39 | Task.Factory.StartNew(StartInternal); 40 | Task.Factory.StartNew(CleanupThread); 41 | } 42 | 43 | private void CleanupThread() 44 | { 45 | while (!_disposed) 46 | { 47 | var oldSessions = _storage.GetOldSessions(TimeSpan.FromSeconds(_config.IdleSessionTime)); 48 | foreach (var os in oldSessions) 49 | { 50 | LogHelper.Log.WriteLine("Removing idle session: " + os); 51 | _storage.RemoveSession(os); 52 | } 53 | 54 | _stopEvent.WaitOne(_config.IdleSessionTime * 1000); 55 | } 56 | } 57 | 58 | private Socket _socket; 59 | 60 | private void StartInternal() 61 | { 62 | Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); 63 | _socket = s; 64 | try 65 | { 66 | var addr = IPAddress.Any; 67 | if (!string.IsNullOrEmpty(_config.ListenAddress)) 68 | { 69 | if (!IPAddress.TryParse(_config.ListenAddress, out addr)) 70 | { 71 | Console.Error.WriteLine("Invalid IP Address in config: " + _config.ListenAddress); 72 | Environment.Exit(1); 73 | } 74 | } 75 | 76 | s.Bind(new IPEndPoint(addr, _config.Port)); 77 | byte[] inBuf = new byte[65536]; 78 | EndPoint ep1 = new IPEndPoint(IPAddress.Any, 0); 79 | while (!_disposed) 80 | { 81 | try 82 | { 83 | int cnt = s.ReceiveFrom(inBuf, ref ep1); 84 | IPEndPoint ep = (IPEndPoint)ep1; 85 | TunnelSession session; 86 | 87 | if (cnt == 0) continue; 88 | byte[] decBuf = null; 89 | if (cnt % 16 != 0) 90 | { 91 | if (inBuf[0] == (byte)StateFlags.Connecting) 92 | { 93 | LogHelper.Log.WriteLine("Establishing connection from " + ep); 94 | int dataLen = -1; 95 | DecryptHelper decryptHelper = null; 96 | EncryptHelper encryptHelper = null; 97 | RemoteClientConfig selectedRemoteClient = null; 98 | foreach (var remoteClient in _config.RemoteClients) 99 | { 100 | if (remoteClient.BinaryKey == null) 101 | remoteClient.BinaryKey = PasswordHelper.GenerateKey(remoteClient.Key); 102 | decryptHelper = new DecryptHelper(remoteClient.BinaryKey); 103 | dataLen = decryptHelper.Decrypt(inBuf, 4); 104 | if (dataLen > 0) 105 | { 106 | encryptHelper = new EncryptHelper(remoteClient.BinaryKey); 107 | selectedRemoteClient = remoteClient; 108 | break; 109 | } 110 | } 111 | 112 | // data is invalid, do not reply 113 | if (dataLen < 0) 114 | { 115 | LogHelper.Log.WriteLine("Invalid data from " + ep); 116 | continue; 117 | } 118 | 119 | var serverHandshake = new ServerHandshake(); 120 | var outPacket = serverHandshake.GetOutPacket(decryptHelper.InnerBuf, dataLen); 121 | var newSession = _storage.AddSession(serverHandshake.SessionKey, ep); 122 | if (selectedRemoteClient != null) 123 | newSession.ClampMss = selectedRemoteClient.ClampMss; 124 | 125 | var outEncryptesPacket = encryptHelper.Encrypt(outPacket, outPacket.Length); 126 | var initBuf = new byte[outEncryptesPacket.Count + 4]; 127 | Buffer.BlockCopy(outEncryptesPacket.Array, 0, initBuf, 4, outEncryptesPacket.Count); 128 | initBuf[0] = (byte)StateFlags.ConnectAnswer; 129 | initBuf[1] = 1; // version 130 | 131 | s.SendTo(initBuf, initBuf.Length, SocketFlags.None, ep); 132 | var descr = selectedRemoteClient != null ? (!string.IsNullOrEmpty(selectedRemoteClient.Description) ? " as " + selectedRemoteClient.BinaryKey : string.Empty) : string.Empty; 133 | LogHelper.Log.WriteLine("Established connection from " + ep + descr); 134 | encryptHelper.Dispose(); 135 | decryptHelper.Dispose(); 136 | continue; 137 | } 138 | 139 | if (inBuf[0] == (byte)StateFlags.Ping) // ping 140 | { 141 | session = _storage.GetSession(ep); 142 | if (session != null) session.UpdateReceiveActivity(); 143 | s.SendTo(new byte[] { (byte)StateFlags.Pong, 0, 0, 0 }, 4, SocketFlags.None, ep); 144 | continue; 145 | } 146 | // it is good idea, but attacker can cause close of our connection 147 | // so, think in future about this 148 | /*else if (inBuf[0] == (byte)StateFlags.ConnectionClosing) // ping 149 | { 150 | _storage.RemoveSession(ep); 151 | continue; 152 | }*/ 153 | else 154 | { 155 | // error 156 | LogHelper.Log.WriteLine("Unsupported data from " + ep); 157 | s.SendTo(new byte[] { (byte)StateFlags.ErrorFromServer, 0, 0, 0 }, 4, SocketFlags.None, ep); 158 | continue; 159 | } 160 | } 161 | else 162 | { 163 | session = _storage.GetSession(ep); 164 | if (session == null) 165 | { 166 | s.SendTo(new byte[] { (byte)StateFlags.ErrorFromServer, 0, 0, 0 }, 4, SocketFlags.None, ep); 167 | LogHelper.Log.WriteLine("Missing decryptor for " + ep); 168 | continue; 169 | } 170 | 171 | var len = session.Decryptor.Decrypt(inBuf, 0); 172 | if (len < 0) 173 | { 174 | s.SendTo(new byte[] { (byte)StateFlags.ErrorFromServer, 0, 0, 0 }, 4, SocketFlags.None, ep); 175 | LogHelper.Log.WriteLine("Unable to decrypt data from " + ep); 176 | continue; 177 | } 178 | 179 | decBuf = session.Decryptor.InnerBuf; 180 | cnt = len; 181 | session.UpdateReceiveActivity(); 182 | } 183 | 184 | // var sourceIp = decBuf[12] + "." + decBuf[13] + "." + decBuf[14] + "." + decBuf[15]; 185 | var sourceIp = new IPAddress(decBuf[12] | (decBuf[13] << 8) | (decBuf[14] << 16) | (decBuf[15] << 24)); 186 | // if we already has option to establish connection to this ip, do not add additional sender 187 | if (!_storage.OutgoingConnectionAdresses.Contains(sourceIp)) 188 | { 189 | var sender = _storage.GetOrAddSender(sourceIp, () => new ReplySender(session, sourceIp, s, _storage)); 190 | sender.UpdateLastActivity(); 191 | 192 | // session was changed for client, killing it and update data 193 | if (!sender.Session.RemoteEP.Equals(ep)) 194 | { 195 | LogHelper.Log.WriteLine("Client for " + sourceIp + " has changed endpoint from " + sender.Session.RemoteEP + " to " + ep); 196 | s.SendTo(new byte[] { 0x3, 0, 0, 0 }, 4, SocketFlags.None, sender.Session.RemoteEP); 197 | _storage.RemoveSession(sender.Session.RemoteEP); 198 | sender.Session = session; 199 | } 200 | } 201 | 202 | _packetWriter.Write(decBuf, cnt); 203 | } 204 | catch (Exception ex) 205 | { 206 | if (!_disposed) 207 | LogHelper.Log.WriteLine(ex); 208 | break; 209 | } 210 | } 211 | } 212 | catch (SocketException) 213 | { 214 | return; 215 | } 216 | } 217 | 218 | public void Dispose() 219 | { 220 | _disposed = true; 221 | _stopEvent.Set(); 222 | _packetWriter.Dispose(); 223 | if (_socket != null) 224 | { 225 | _socket.Dispose(); 226 | _socket = null; 227 | } 228 | } 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /AutoTunnel/Logging/AggregateLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Force.AutoTunnel.Logging 7 | { 8 | public class AggregateLog : ILog 9 | { 10 | private List _logs = new List(); 11 | 12 | public AggregateLog AddLog(ILog log) 13 | { 14 | _logs = _logs.Concat(new[] { log }).ToList(); 15 | return this; 16 | } 17 | 18 | public void ClearLogs() 19 | { 20 | _logs = new List(); 21 | } 22 | 23 | public void WriteLine(string line) 24 | { 25 | _logs.ForEach(x => x.WriteLine(line)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /AutoTunnel/Logging/ConsoleLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | 4 | namespace Force.AutoTunnel.Logging 5 | { 6 | public class ConsoleLog : ILog 7 | { 8 | public void WriteLine(string line) 9 | { 10 | Console.WriteLine(DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ", CultureInfo.InvariantCulture) + line); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /AutoTunnel/Logging/FileLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace Force.AutoTunnel.Logging 9 | { 10 | public class FileLog : ILog 11 | { 12 | private readonly string _fileName; 13 | 14 | public FileLog(string fileName) 15 | { 16 | if (!Path.IsPathRooted(fileName)) 17 | fileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); 18 | _fileName = fileName; 19 | } 20 | 21 | public void WriteLine(string line) 22 | { 23 | File.AppendAllText(_fileName, DateTime.Now.ToString("[yyyy-MM-dd HH:mm:ss] ", CultureInfo.InvariantCulture) + line + Environment.NewLine); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AutoTunnel/Logging/ILog.cs: -------------------------------------------------------------------------------- 1 | namespace Force.AutoTunnel.Logging 2 | { 3 | public interface ILog 4 | { 5 | void WriteLine(string line); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /AutoTunnel/Logging/LogHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.AutoTunnel.Logging 4 | { 5 | public static class LogHelper 6 | { 7 | private static ILog _log = new ConsoleLog(); 8 | 9 | public static void SetLog(ILog log) 10 | { 11 | _log = log; 12 | } 13 | 14 | public static ILog Log 15 | { 16 | get 17 | { 18 | return _log; 19 | } 20 | } 21 | 22 | public static void WriteLine(this ILog log, Exception ex) 23 | { 24 | log.WriteLine(ex.ToString()); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AutoTunnel/NativeHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace Force.AutoTunnel 6 | { 7 | public static class NativeHelper 8 | { 9 | private static readonly bool _isNativePossible; 10 | 11 | [DllImport("Kernel32.dll")] 12 | private static extern IntPtr LoadLibrary(string path); 13 | 14 | public static bool IsNativeAvailable { get; private set; } 15 | 16 | static NativeHelper() 17 | { 18 | _isNativePossible = Init(); 19 | IsNativeAvailable = _isNativePossible; 20 | } 21 | 22 | private static bool Init() 23 | { 24 | try 25 | { 26 | InitInternal(); 27 | return true; 28 | } 29 | catch (Exception) // will use software realization 30 | { 31 | return false; 32 | } 33 | } 34 | 35 | private static void InitInternal() 36 | { 37 | var architectureSuffix = IntPtr.Size == 8 ? "x64" : "x86"; 38 | var libraryName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, architectureSuffix); 39 | libraryName = Path.Combine(libraryName, "WinDivert.dll"); 40 | 41 | if (LoadLibrary(libraryName) == IntPtr.Zero) 42 | throw new InvalidOperationException("Unexpected error in dll loading"); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /AutoTunnel/PacketWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Force.AutoTunnel 4 | { 5 | public class PacketWriter : IDisposable 6 | { 7 | private IntPtr _handle; 8 | 9 | private WinDivert.WinDivertAddress _addr; 10 | 11 | public PacketWriter() 12 | { 13 | _handle = WinDivert.WinDivertOpen("false", WinDivert.LAYER_NETWORK, 0, 0); 14 | _addr = new WinDivert.WinDivertAddress(); 15 | _addr.IfIdx = InterfaceHelper.GetInterfaceId(); 16 | } 17 | 18 | public void Dispose() 19 | { 20 | if (_handle != IntPtr.Zero && _handle != (IntPtr)(-1)) 21 | WinDivert.WinDivertClose(_handle); 22 | _handle = IntPtr.Zero; 23 | } 24 | 25 | public void Write(byte[] packet, int packetLen) 26 | { 27 | int writeLen = 0; 28 | /*if (packet[9] == 6 && (packet[20 + 13] & 2) != 0 && packet[20 + 20] == 2 && packetLen > 20 + 24) 29 | { 30 | var len = packet[20 + 22] << 8 | packet[20 + 23]; 31 | Console.WriteLine("X: " + (packet[20 + 22] << 8 | packet[20 + 23])); 32 | // UDP + encryption 33 | len -= 28 + 32; 34 | packet[20 + 22] = (byte)(len >> 8); 35 | packet[20 + 23] = (byte)(len & 0xFF); 36 | WinDivert.WinDivertHelperCalcChecksums(packet, packetLen, ref _addr, 0); 37 | }*/ 38 | 39 | WinDivert.WinDivertSend(_handle, packet, packetLen, ref _addr, ref writeLen); 40 | } 41 | 42 | ~PacketWriter() 43 | { 44 | Dispose(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /AutoTunnel/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.ServiceProcess; 5 | using System.Threading; 6 | 7 | using Force.AutoTunnel.Config; 8 | using Force.AutoTunnel.Logging; 9 | using Force.AutoTunnel.Service; 10 | 11 | namespace Force.AutoTunnel 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | if (ProcessArgs(args)) return; 18 | 19 | if (!NativeHelper.IsNativeAvailable) 20 | { 21 | Console.Error.WriteLine("Cannot load WinDivert library"); 22 | return; 23 | } 24 | 25 | if (!ConfigHelper.LoadConfig(true)) 26 | return; 27 | 28 | if (Environment.UserInteractive) 29 | { 30 | RunInConsole(); 31 | } 32 | else 33 | { 34 | LogHelper.Log.WriteLine("Starting service..."); 35 | ServiceBase.Run(new MainService()); 36 | } 37 | } 38 | 39 | private static bool ProcessArgs(string[] args) 40 | { 41 | if (args.Any(x => x == "service")) 42 | { 43 | bool isUninstall = args.Any(x => x == "uninstall" || x == "remove"); 44 | MainServiceInstallerHelper.Process(!isUninstall, new string[0]); 45 | return true; 46 | } 47 | 48 | return false; 49 | } 50 | 51 | private static void RunInConsole() 52 | { 53 | Console.WriteLine("Press Ctrl+C for exit"); 54 | 55 | var attr = typeof(Program).Assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), false).Cast().First(); 56 | LogHelper.Log.WriteLine("AutoTunnel by Force. Version: " + attr.Version); 57 | AppDomain.CurrentDomain.DomainUnload += (sender, args) => ConsoleHelper.SetActiveIcon(ConsoleHelper.IconStatus.Default); 58 | Console.CancelKeyPress += (sender, args) => ConsoleHelper.SetActiveIcon(ConsoleHelper.IconStatus.Default); 59 | LogHelper.Log.WriteLine("Starting interactive..."); 60 | Starter.Start(); 61 | Thread.Sleep(-1); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /AutoTunnel/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("AutoTunnel")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("Force.AutoTunnel")] 12 | [assembly: AssemblyCopyright("Copyright © Force 2017")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("BC409F07-4C77-4930-8597-B9BD5837E672")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.2.0.3")] -------------------------------------------------------------------------------- /AutoTunnel/ReplySender.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Sockets; 4 | 5 | using Force.AutoTunnel.Encryption; 6 | using Force.AutoTunnel.Logging; 7 | 8 | namespace Force.AutoTunnel 9 | { 10 | public class ReplySender : BaseSender 11 | { 12 | private readonly Socket _socket; 13 | 14 | private readonly EncryptHelper _encryptHelper; 15 | 16 | public ReplySender(TunnelSession session, IPAddress watchAddr, Socket socket, TunnelStorage storage) 17 | : base(session, watchAddr, storage, session.ClampMss) 18 | { 19 | LogHelper.Log.WriteLine("Tunnel watcher was created for " + watchAddr); 20 | _socket = socket; 21 | _encryptHelper = new EncryptHelper(session.Key); 22 | } 23 | 24 | protected override void Send(byte[] packet, int packetLen) 25 | { 26 | Session.UpdateSendActivity(); 27 | var p = _encryptHelper.Encrypt(packet, packetLen); 28 | _socket.SendTo(p.Array, p.Count, SocketFlags.None, Session.RemoteEP); 29 | } 30 | 31 | public override void Dispose() 32 | { 33 | base.Dispose(); 34 | if (_encryptHelper != null) 35 | _encryptHelper.Dispose(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /AutoTunnel/Service/ConsoleHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace Force.AutoTunnel.Service 6 | { 7 | public static class ConsoleHelper 8 | { 9 | [DllImport("kernel32.dll")] 10 | private static extern IntPtr GetConsoleWindow(); 11 | [DllImport("user32.dll")] 12 | private static extern IntPtr SendMessage(IntPtr hWnd, uint message, int lParam, IntPtr wParam); 13 | 14 | private static IntPtr _iconActiveHandle = IntPtr.Zero; 15 | private static IntPtr _iconEstablishingHandle = IntPtr.Zero; 16 | private static IntPtr _iconParentHandle = IntPtr.Zero; 17 | 18 | private static void RestoreOriginalIcon() 19 | { 20 | if (!Environment.UserInteractive) 21 | return; 22 | SendMessage(GetConsoleWindow(), 0x80, 1, _iconParentHandle); 23 | SendMessage(GetConsoleWindow(), 0x80, 0, _iconParentHandle); 24 | } 25 | 26 | public enum IconStatus 27 | { 28 | Active, 29 | Establishing, 30 | Default 31 | } 32 | 33 | public static void SetActiveIcon(IconStatus status) 34 | { 35 | if (!Environment.UserInteractive) 36 | return; 37 | if (_iconActiveHandle == IntPtr.Zero) 38 | { 39 | // ReSharper disable AssignNullToNotNullAttribute 40 | _iconActiveHandle = new Bitmap(typeof(ConsoleHelper).Assembly.GetManifestResourceStream("Force.AutoTunnel.tunnel_active.png")).GetHicon(); 41 | _iconEstablishingHandle = new Bitmap(typeof(ConsoleHelper).Assembly.GetManifestResourceStream("Force.AutoTunnel.tunnel_establishing.png")).GetHicon(); 42 | // ReSharper restore AssignNullToNotNullAttribute 43 | 44 | _iconParentHandle = SendMessage(GetConsoleWindow(), 0x80, 0, _iconActiveHandle); 45 | } 46 | 47 | var icon = _iconParentHandle; 48 | if (status == IconStatus.Active) icon = _iconActiveHandle; 49 | else if (status == IconStatus.Establishing) icon = _iconEstablishingHandle; 50 | SendMessage(GetConsoleWindow(), 0x80, 0, icon); 51 | SendMessage(GetConsoleWindow(), 0x80, 1, icon); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /AutoTunnel/Service/MainService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.ServiceProcess; 5 | using System.Text; 6 | 7 | namespace Force.AutoTunnel.Service 8 | { 9 | public class MainService : ServiceBase 10 | { 11 | protected override void OnStart(string[] args) 12 | { 13 | base.OnStart(args); 14 | Starter.Start(); 15 | } 16 | 17 | protected override void OnStop() 18 | { 19 | Starter.Stop(); 20 | base.OnStop(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /AutoTunnel/Service/MainServiceInstallHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Configuration.Install; 4 | 5 | using Force.AutoTunnel.Logging; 6 | 7 | namespace Force.AutoTunnel.Service 8 | { 9 | public static class MainServiceInstallerHelper 10 | { 11 | public static void Process(bool install, string[] args) 12 | { 13 | var log = Logging.LogHelper.Log; 14 | 15 | try 16 | { 17 | log.WriteLine("Started service installation process"); 18 | 19 | using (var installer = new AssemblyInstaller(typeof(Program).Assembly, args)) 20 | { 21 | var state = new Hashtable(); 22 | 23 | try 24 | { 25 | if (install) 26 | { 27 | log.WriteLine("Installing service"); 28 | installer.Install(state); 29 | 30 | log.WriteLine("Commiting installation"); 31 | installer.Commit(state); 32 | 33 | log.WriteLine("Disabling server header"); 34 | } 35 | else 36 | { 37 | log.WriteLine("Uninstalling service"); 38 | installer.Uninstall(state); 39 | } 40 | 41 | log.WriteLine("Installation process completed successfully"); 42 | } 43 | catch (Exception ex) 44 | { 45 | log.WriteLine("Exception during installation process"); 46 | log.WriteLine(ex); 47 | log.WriteLine("Rolling back installation process"); 48 | 49 | try 50 | { 51 | installer.Rollback(state); 52 | } 53 | catch (Exception rex) 54 | { 55 | log.WriteLine("Exception in rollback"); 56 | log.WriteLine(rex); 57 | } 58 | } 59 | } 60 | } 61 | catch (Exception ex) 62 | { 63 | log.WriteLine("Exception in installation process: "); 64 | log.WriteLine(ex); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /AutoTunnel/Service/MainServiceInstaller.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Configuration.Install; 3 | using System.ServiceProcess; 4 | 5 | namespace Force.AutoTunnel.Service 6 | { 7 | [RunInstaller(true)] 8 | public class MainServiceInstaller : Installer 9 | { 10 | public MainServiceInstaller() 11 | { 12 | Installers.Add(new ServiceProcessInstaller 13 | { 14 | Account = ServiceAccount.LocalSystem 15 | }); 16 | 17 | Installers.Add(new ServiceInstaller 18 | { 19 | StartType = ServiceStartMode.Automatic, 20 | ServiceName = "AutoTunnel", 21 | DisplayName = "AutoTunnel", 22 | }); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /AutoTunnel/Starter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Globalization; 3 | 4 | using Force.AutoTunnel.Config; 5 | 6 | namespace Force.AutoTunnel 7 | { 8 | public class Starter 9 | { 10 | private static TunnelStorage _storage; 11 | 12 | private static List _clientSenders; 13 | 14 | private static Listener _listener; 15 | 16 | public static void Start() 17 | { 18 | _storage = new TunnelStorage(); 19 | var config = ConfigHelper.Config; 20 | 21 | if (config.EnableListening) 22 | { 23 | if (config.AddFirewallRule) 24 | FirewallHelper.AddOpenFirewallRule(config.Port.ToString(CultureInfo.InvariantCulture)); 25 | 26 | _listener = new Listener(_storage, config); 27 | _listener.Start(); 28 | } 29 | 30 | _clientSenders = new List(); 31 | foreach (var rs in config.RemoteServers) 32 | { 33 | _clientSenders.Add(new ClientSender(rs, _storage)); 34 | } 35 | } 36 | 37 | public static void Stop() 38 | { 39 | _clientSenders.ForEach(x => x.Dispose()); 40 | _storage.RemoveAllSessions(); 41 | if (_listener != null) 42 | _listener.Dispose(); 43 | if (ConfigHelper.Config.AddFirewallRule) 44 | FirewallHelper.DeleteFirewallRule(ConfigHelper.Config.Port.ToString(CultureInfo.InvariantCulture)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /AutoTunnel/StateFlags.cs: -------------------------------------------------------------------------------- 1 | namespace Force.AutoTunnel 2 | { 3 | public enum StateFlags : byte 4 | { 5 | Connecting = 1, 6 | 7 | ConnectAnswer = 2, 8 | 9 | ErrorFromServer = 3, 10 | 11 | Ping = 5, 12 | 13 | ProxyConnecting = 7, 14 | 15 | ErrorFromProxy = 8, 16 | 17 | Pong = 9, 18 | 19 | // todo : think about it 20 | // ConnectionClosing = 10 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /AutoTunnel/TunnelSession.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | 4 | using Force.AutoTunnel.Encryption; 5 | 6 | namespace Force.AutoTunnel 7 | { 8 | public class TunnelSession 9 | { 10 | public TunnelSession(IPEndPoint remoteEP) 11 | { 12 | RemoteEP = remoteEP; 13 | UpdateReceiveActivity(); 14 | UpdateSendActivity(); 15 | } 16 | 17 | public IPEndPoint RemoteEP { get; private set; } 18 | 19 | public byte[] Key { get; set; } 20 | 21 | public DecryptHelper Decryptor { get; set; } 22 | 23 | public DateTime LastReceiveActivity { get; private set; } 24 | 25 | public DateTime LastSendActivity { get; private set; } 26 | 27 | public bool IsClientSession { get; set; } 28 | 29 | public void UpdateReceiveActivity() 30 | { 31 | LastReceiveActivity = DateTime.UtcNow; 32 | } 33 | 34 | public void UpdateSendActivity() 35 | { 36 | LastSendActivity = DateTime.UtcNow; 37 | } 38 | 39 | public TimeSpan SendReceiveDifference 40 | { 41 | get 42 | { 43 | return LastSendActivity.Subtract(LastReceiveActivity); 44 | } 45 | } 46 | 47 | public int? ClampMss { get; set; } 48 | 49 | } 50 | } -------------------------------------------------------------------------------- /AutoTunnel/TunnelStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading; 7 | 8 | using Force.AutoTunnel.Encryption; 9 | using Force.AutoTunnel.Service; 10 | 11 | namespace Force.AutoTunnel 12 | { 13 | public class TunnelStorage 14 | { 15 | private readonly ConcurrentDictionary _clients = new ConcurrentDictionary(); 16 | 17 | public readonly HashSet OutgoingConnectionAdresses = new HashSet(); 18 | 19 | public BaseSender GetOrAddSender(IPAddress dstAddr, Func creatorFunc) 20 | { 21 | #pragma warning disable 612,618 22 | return _clients.GetOrAdd(dstAddr.Address, s => creatorFunc()); 23 | #pragma warning restore 612,618 24 | } 25 | 26 | public IPEndPoint[] GetOldSessions(TimeSpan killTime) 27 | { 28 | var dt = DateTime.UtcNow; 29 | return _sessions.Where(x => !x.Value.IsClientSession && dt.Subtract(x.Value.LastReceiveActivity) >= killTime) 30 | .Select(x => x.Key) 31 | .Select(x => new IPEndPoint((long)(x >> 16), (int)(x & 0xffff))) 32 | .ToArray(); 33 | } 34 | 35 | public void RemoveAllSessions() 36 | { 37 | _sessions.Keys 38 | .Select(x => new IPEndPoint((long)(x >> 16), (int)(x & 0xffff))) 39 | .ToList() 40 | .ForEach(RemoveSession); 41 | } 42 | 43 | public void RemoveSession(IPEndPoint endPoint) 44 | { 45 | var hostKey = GetHostKey(endPoint); 46 | TunnelSession session; 47 | if (_sessions.TryRemove(hostKey, out session)) 48 | { 49 | if (session.IsClientSession) 50 | return; 51 | _clients.Where(x => x.Value.Session == session).Select(x => x.Key).ToList().ForEach( 52 | x => 53 | { 54 | BaseSender value; 55 | if (_clients.TryRemove(x, out value)) value.Dispose(); 56 | }); 57 | } 58 | 59 | SetIcon(); 60 | } 61 | 62 | private readonly ConcurrentDictionary _sessions = new ConcurrentDictionary(); 63 | 64 | public TunnelSession AddSession(byte[] sessionKey, IPEndPoint remoteEP) 65 | { 66 | var hostKey = GetHostKey(remoteEP); 67 | var s = new TunnelSession(remoteEP) 68 | { 69 | Key = sessionKey, 70 | Decryptor = new DecryptHelper(sessionKey) 71 | }; 72 | _sessions[hostKey] = s; 73 | SetIcon(); 74 | return s; 75 | } 76 | 77 | public TunnelSession GetSession(IPEndPoint remoteHost) 78 | { 79 | TunnelSession value; 80 | if (_sessions.TryGetValue(GetHostKey(remoteHost), out value)) return value; 81 | return null; 82 | } 83 | 84 | private static ulong GetHostKey(IPEndPoint endpoint) 85 | { 86 | #pragma warning disable 612,618 87 | return ((ulong)endpoint.Address.Address << 16) | (uint)endpoint.Port; 88 | #pragma warning restore 612,618 89 | } 90 | 91 | public bool HasSession(ulong key) 92 | { 93 | return _sessions.ContainsKey(key); 94 | } 95 | 96 | private int _establishingCount; 97 | 98 | public void IncrementEstablishing() 99 | { 100 | Interlocked.Increment(ref _establishingCount); 101 | SetIcon(); 102 | } 103 | 104 | public void DecrementEstablishing() 105 | { 106 | Interlocked.Decrement(ref _establishingCount); 107 | SetIcon(); 108 | } 109 | 110 | private void SetIcon() 111 | { 112 | if (_establishingCount > 0) 113 | { 114 | ConsoleHelper.SetActiveIcon(ConsoleHelper.IconStatus.Establishing); 115 | } 116 | else 117 | { 118 | ConsoleHelper.SetActiveIcon(_sessions.Count > 0 ? ConsoleHelper.IconStatus.Active : ConsoleHelper.IconStatus.Default); 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /AutoTunnel/WinDivert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Force.AutoTunnel 5 | { 6 | public static class WinDivert 7 | { 8 | public const int LAYER_NETWORK = 0; 9 | 10 | public const int LAYER_NETWORK_FORWARD = 1; 11 | 12 | public const int FLAG_SNIFF = 1; 13 | 14 | public const int FLAG_DROP = 2; 15 | 16 | [StructLayout(LayoutKind.Sequential)] 17 | public struct WinDivertAddress 18 | { 19 | public ulong Timestamp; 20 | public uint IfIdx; /* Packet's interface index. */ 21 | public uint SubIfIdx; /* Packet's sub-interface index. */ 22 | public uint Flags; 23 | /*public byte Direction; /* Packet's direction. #1# 24 | public byte Loopback; 25 | public byte Impostor; 26 | public byte PseudoIPChecksum; 27 | public byte PseudoTCPChecksum; 28 | public byte PseudoUDPChecksum; 29 | public byte Reserved;*/ 30 | 31 | public uint Direction 32 | { 33 | get 34 | { 35 | return Flags & 1; 36 | } 37 | } 38 | 39 | public uint Loopback 40 | { 41 | get 42 | { 43 | return Flags & 2; 44 | } 45 | } 46 | 47 | public uint Impostor 48 | { 49 | get 50 | { 51 | return Flags & 4; 52 | } 53 | } 54 | 55 | public bool HasPseudoChecksum 56 | { 57 | get 58 | { 59 | return (Flags & (8 | 16 | 32)) != 0; 60 | } 61 | } 62 | 63 | public void DisablePseudoChecksums() 64 | { 65 | Flags &= 255 - (8 + 16 + 32); 66 | } 67 | } 68 | 69 | [StructLayout(LayoutKind.Explicit)] 70 | public struct WinDivertIpHeader 71 | { 72 | [FieldOffset(0)] 73 | public byte HdrLengthAndVersion; 74 | 75 | [FieldOffset(1)] 76 | public byte Tos; 77 | 78 | [FieldOffset(2)] 79 | public ushort Length; 80 | 81 | [FieldOffset(4)] 82 | public ushort Id; 83 | 84 | [FieldOffset(6)] 85 | public ushort FragOff0; 86 | 87 | [FieldOffset(8)] 88 | public byte Ttl; 89 | 90 | [FieldOffset(9)] 91 | public byte Protocol; 92 | 93 | [FieldOffset(10)] 94 | public short Checksum; 95 | 96 | [FieldOffset(12)] 97 | public uint SrcAddr; 98 | 99 | [FieldOffset(16)] 100 | public uint DstAddr; 101 | } 102 | 103 | [DllImport("WinDivert.dll")] 104 | public static extern bool WinDivertHelperParsePacket( 105 | byte[] pPacket, 106 | int packetLen, 107 | ref WinDivertIpHeader ipHdr, 108 | IntPtr ipv6Hdr, 109 | IntPtr icmpHdr, 110 | IntPtr icmpv6Hdr, 111 | IntPtr tcpHdr, 112 | IntPtr updHdr, 113 | IntPtr ppdataHdr, 114 | ref int dataLen); 115 | 116 | [DllImport("WinDivert.dll")] 117 | public static extern IntPtr WinDivertOpen(string filter, int layer, short priority, ulong flags); 118 | 119 | [DllImport("WinDivert.dll")] 120 | public static extern bool WinDivertClose(IntPtr handle); 121 | 122 | [DllImport("WinDivert.dll")] 123 | public static extern bool WinDivertRecv(IntPtr handle, byte[] packet, int packetLen, ref WinDivertAddress addr, ref int readLen); 124 | 125 | [DllImport("WinDivert.dll")] 126 | public static extern bool WinDivertSend(IntPtr handle, byte[] packet, int packetLen, ref WinDivertAddress addr, ref int writeLen); 127 | 128 | [DllImport("WinDivert.dll")] 129 | public static extern bool WinDivertHelperCalcChecksums(byte[] packet, int packetLen, ref WinDivertAddress addr, ulong flags); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /AutoTunnel/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | - 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /AutoTunnel/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "remoteClients": [ 3 | { "key": "key1" } 4 | ], 5 | "remoteServers": [ 6 | { 7 | "tunnelHost": "192.168.16.8", 8 | "connectHost": "192.168.18.1:12017", 9 | "proxyHost": "127.0.0.1:12018", 10 | "key": "key1", 11 | "keepAlive": true, 12 | "connectOnStart": true 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /AutoTunnel/credits.txt: -------------------------------------------------------------------------------- 1 |
Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
-------------------------------------------------------------------------------- /AutoTunnel/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /AutoTunnel/x64/WinDivert.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/AutoTunnel/95ca05cb512eee076b6905b8daff6df098133766/AutoTunnel/x64/WinDivert.dll -------------------------------------------------------------------------------- /AutoTunnel/x64/WinDivert64.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/AutoTunnel/95ca05cb512eee076b6905b8daff6df098133766/AutoTunnel/x64/WinDivert64.sys -------------------------------------------------------------------------------- /AutoTunnel/x86/WinDivert.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/AutoTunnel/95ca05cb512eee076b6905b8daff6df098133766/AutoTunnel/x86/WinDivert.dll -------------------------------------------------------------------------------- /AutoTunnel/x86/WinDivert32.sys: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/AutoTunnel/95ca05cb512eee076b6905b8daff6df098133766/AutoTunnel/x86/WinDivert32.sys -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 force 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AutoTunnel 2 | Secured network tunnel for connecting two computers. Main usage: connecting travelling computer to one server. 3 | 4 | ## Features 5 | 6 | * Works for Windows 7 or greater 7 | * Client-Server model 8 | * Works on IP level and tunnels ICMP, TCP and UDP data 9 | * Uses only one UDP port (12017 by default) 10 | * Can connect by hostnames with periodic refreshing data 11 | * Client can be NAT'ed 12 | * Server can be NAT'ed, but you need to use a special proxy 13 | * Creates transparent secured tunnel between two computers without allocation of additional IP-addresses, so you do not need for separate DNS-names for computers 14 | 15 | ## Differences from VPN 16 | 17 | * No server needed 18 | * No separate IP address or network interface 19 | * Always active (if service running) 20 | 21 | ## Differences from IPSec 22 | 23 | * No need to specify both endpoints to static addresses 24 | * Only one UDP port for usage (instead of ESP, AH protocols and 500 and 4500 UDP ports) 25 | * Just some settings to make tunnel works, you no need to specify lot of parameters in different configs and phases 26 | * Currently, no IPv6 support 27 | 28 | ## Limitations 29 | 30 | * Due usage existing IPs for computers there are can be collision with current IPs 31 | * If network has problems with UDP fragmentation (or badly configured Jumbo Frames), there are can be strange errors and packet loss 32 | 33 | # Description 34 | 35 | AutoTunnel uses [WinDivert](https://reqrypt.org/windivert.html) library to catch outgoing network packets, transfers it to another computer and puts them back, as normal packets. 36 | So, this packets looks like usual network packets, but you do not need to specify routing tables between computers. These computers are in virtual local network. 37 | After establishing connection with another computer, it begins to catch packets to source computer and transfers it back to it through established tunnel. 38 | 39 | ## Encryption 40 | Tunnel is encrypted with AES256 with PFS support. Currently, for auth you can use only preshared keys (I think, it simple and good variant for authentication, because one key can be changed to another in some seconds, you do not need to regenerate certificates or private keys). 41 | This key is used only for initial handshake and has not transferred in open form. After handshake stage, another, temporary session key is used for data encryption. 42 | 43 | ## Tunnel Handling 44 | 45 | There are two types of computers: 46 | 47 | * Server - listens incoming connections 48 | * Client - connects to server 49 | 50 | Any computer can be server for others computers and client for another. There is no need to use separate program. 51 | So, despite only two computers can use one tunnel, you can create a lot of tunnels between pairs of different computers. 52 | 53 | Also, you can establish connection between two computers in client mode. Two tunnels will be created, but packets will be passed correctly. 54 | 55 | 56 | # Installation and running 57 | 58 | You need: 59 | 60 | * Windows 7/2008R2 or greater 61 | * Enabled .NET Framework 4.0 62 | * [VC Redist 2012](https://www.microsoft.com/en-us/download/details.aspx?id=30679) 63 | 64 | Program can be started in console or work as a service. Service can be installed in next way: 65 | ``` 66 | AutoTunnel.exe service install 67 | sc start AutoTunnel 68 | ``` 69 | ## Configuration 70 | 71 | Before starting, you need to configure config.json file. Example config: 72 | ``` 73 | { 74 | "enableListening": true, 75 | "addFirewallRule": true, 76 | "port": 12017, 77 | "logFileName": "somelog.log", 78 | "autoReloadOnChange": true, 79 | "remoteClients": [ 80 | { 81 | "key": "key1", 82 | "description": "my key" 83 | } 84 | ], 85 | "remoteServers": [ 86 | { 87 | "tunnelHost": "192.168.16.8", 88 | "connectHost": "192.168.18.1:12017", 89 | "proxyHost": "192.168.24.1:12018", 90 | "key": "key1", 91 | "keepAlive": true, 92 | "connectOnStart": true 93 | } 94 | ] 95 | } 96 | ``` 97 | 98 | Key | Default Value | Description 99 | ----|---------------|------------ 100 | enableListening | true | Is Application listen incoming connections 101 | addFirewallRule | true | Add opening rule to Windows Firewall 102 | port | 12017 | Listening port 103 | logFileName | null | Name of file to log program messages 104 | autoReloadOnChange | true | Reload config automatically if it changed 105 | remoteClients | null | Data of remote clients for server (if multiple clients are specified - any key can be used for connection) 106 | remoteClients.key | null | Pre-shared Key for remote client 107 | remoteClients.description | null | Description of remote client for logging and your notes 108 | remoteServers | null | Servers for connecting as client 109 | remoteServers.tunnelHost | null | IP address or host name of remote computer. If any packets will be send to this computer, it will be passed through tunnel 110 | remoteServers.connectHost | null | IP address or host with port of remote computer to connect. If skipped - tunnel host data can be used. You can specify it, it target computer has different IP addresses and you want to connect to one of them, but pass data for another 111 | remoteServers.proxyHost | null | IP address or host with port of proxy. Proxy can be used to connect to server which is not available from outer network 112 | remoteServers.key | null | Pre-shared key to establish connection with remote server 113 | remoteServers.keepAlive | false | Send special keep alive packets to keep connection alive. If you need permament connection, you can set it to true 114 | remoteServers.connectOnStart | false | Connect to another computer on application start or only when first packet will be sent to it 115 | 116 | 117 | # Proxy 118 | If target computer is unavailable from outer network but you have separate computer which can transfer packets from local network to outer network, you can use special proxy program. 119 | 120 | Full scheme of connection can be similar: 121 | 122 | ``` 123 | Client computer (gray ip, NAT) <-> Router <-> (Internet) <-> Router <-> Proxy <-> Server (NAT) 124 | 192.168.1.42 <-> 192.168.1.1|91.10.20.42 <-> (Internet) <-> 82.40.10.1 <-> 82.40.10.54 <-> 10.0.1.15 125 | ``` 126 | 127 | 128 | Current implementation does not encrypt data, it just transfer packets from one computer to another. For better compatibility, current version of proxy is written on node.js, so it can be run at lot of hardware. 129 | 130 | Also, when you use a proxy, just proxy resolves host name to ip. So, it can be useful for internal host names. 131 | 132 | By default, proxy is listening on 12018 port and can filter destination ip addresses or host names. 133 | 134 | # Licensing 135 | 136 | AutoTunnel has [MIT](https://github.com/force-net/AutTunnel/blob/develop/LICENSE) license, but it uses [WinDivert](https://reqrypt.org/windivert.html) library with [LGPL3](https://reqrypt.org/windivert-doc-v1.2.html#license) license. 137 | 138 |
Application Icons made by Freepik from www.flaticon.com is licensed by CC 3.0 BY
139 | 140 | 141 | -------------------------------------------------------------------------------- /tunnel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/AutoTunnel/95ca05cb512eee076b6905b8daff6df098133766/tunnel.png -------------------------------------------------------------------------------- /tunnel_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/AutoTunnel/95ca05cb512eee076b6905b8daff6df098133766/tunnel_active.png -------------------------------------------------------------------------------- /tunnel_establishing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/AutoTunnel/95ca05cb512eee076b6905b8daff6df098133766/tunnel_establishing.png -------------------------------------------------------------------------------- /tunnel_hUB_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/force-net/AutoTunnel/95ca05cb512eee076b6905b8daff6df098133766/tunnel_hUB_icon.ico --------------------------------------------------------------------------------