This commit is contained in:
SpringHgui 2021-08-01 23:23:58 +08:00
parent 3e5020b890
commit 3e6a3bbc2a
17 changed files with 110 additions and 406 deletions

View File

@ -1,29 +0,0 @@
using FastTunnel.Core.Client;
using FastTunnel.Core.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace FastTunel.Core.WebApi.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class TunnelController : ControllerBase
{
FastTunnelServer _fastTunnelServer;
public TunnelController(FastTunnelServer fastTunnelServer)
{
_fastTunnelServer = fastTunnelServer;
}
[HttpGet]
public int GetWebCount()
{
return _fastTunnelServer.WebList.Count;
}
}
}

View File

@ -1,11 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\FastTunnel.Core\FastTunnel.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Controller_SelectedScaffolderID>ApiControllerEmptyScaffolder</Controller_SelectedScaffolderID>
<Controller_SelectedScaffolderCategoryPath>root/Common/Api</Controller_SelectedScaffolderCategoryPath>
<ActiveDebugProfile>IIS Express</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@ -1,9 +0,0 @@
namespace FastTunel.Core.WebApi
{
public class Program
{
public static void Main(string[] args)
{
}
}
}

View File

@ -1,27 +0,0 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:63078/",
"sslPort": 44352
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"FastTunel.Core.WebApi": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
}
}
}

View File

@ -42,7 +42,7 @@
* linux#ssh -oPort=12701 {root}@{ServerAddr} ServerAddr iproot
* 访访
*/
"Forward": [
"Forwards": [
{
"LocalIp": "127.0.0.1",
"LocalPort": 8090,

View File

@ -15,6 +15,7 @@ using FastTunnel.Core.Sockets;
using Microsoft.Extensions.Options;
using System.Net.WebSockets;
using System.Text.Json;
using FastTunnel.Core.Protocol;
namespace FastTunnel.Core.Client
{
@ -25,12 +26,8 @@ namespace FastTunnel.Core.Client
protected ILogger<FastTunnelClient> _logger;
System.Timers.Timer timer_heart;
double heartInterval = 10 * 1000; // 10 秒心跳
public DateTime lastHeart;
int reTrySpan = 10 * 1000; // 登陆失败后重试间隔
HttpRequestHandler _newCustomerHandler;
NewForwardHandler _newSSHHandler;
LogHandler _logHandler;
@ -54,54 +51,6 @@ namespace FastTunnel.Core.Client
_logHandler = logHandler;
_clientHeartHandler = clientHeartHandler;
_configuration = configuration;
timer_heart = new System.Timers.Timer();
timer_heart.AutoReset = false;
timer_heart.Interval = heartInterval;
timer_heart.Elapsed += HeartElapsed;
}
private async Task reConnAsync()
{
Close();
do
{
try
{
Thread.Sleep(reTrySpan);
_logger.LogInformation("登录重试...");
socket = await loginAsync(CancellationToken.None);
break;
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
} while (true);
connSuccessAsync();
}
private void HeartElapsed(object sender, ElapsedEventArgs e)
{
timer_heart.Enabled = false;
try
{
socket.SendAsync(new Message<HeartMassage> { MessageType = MessageType.Heart, Content = null }, cancellationTokenSource.Token).Wait();
}
catch (Exception)
{
// 与服务端断开连接
reConnAsync();
}
finally
{
timer_heart.Enabled = true;
}
}
/// <summary>
@ -122,52 +71,12 @@ namespace FastTunnel.Core.Client
catch (Exception ex)
{
_logger.LogError(ex.Message);
reConnAsync();
return;
}
_ = connSuccessAsync();
await ReceiveServerAsync(socket);
}
//protected virtual Socket login()
//{
// Server = _configuration.CurrentValue.Server;
// DnsSocket _client = null;
// _logger.LogInformation($"正在连接服务端 {Server.ServerAddr}:{Server.ServerPort}");
// try
// {
// // 连接到的目标IP
// if (_client == null)
// {
// _client = new DnsSocket(Server.ServerAddr, Server.ServerPort);
// }
// _client.Connect();
// _logger.LogInformation("连接成功");
// }
// catch (Exception)
// {
// throw;
// }
// loginMsg = new Message<LogInMassage>
// {
// MessageType = MessageType.C_LogIn,
// Content = new LogInMassage
// {
// Webs = _configuration.CurrentValue.Webs,
// SSH = _configuration.CurrentValue.SSH,
// },
// };
// // 登录
// _client.Send(loginMsg);
// return _client.Socket;
//}
protected virtual async Task<IFastTunnelClientSocket> loginAsync(CancellationToken cancellationToken)
{
Server = _configuration.CurrentValue.Server;
@ -194,7 +103,7 @@ namespace FastTunnel.Core.Client
Content = new LogInMassage
{
Webs = _configuration.CurrentValue.Webs,
SSH = _configuration.CurrentValue.Forwards,
Forwards = _configuration.CurrentValue.Forwards,
},
};
@ -203,125 +112,34 @@ namespace FastTunnel.Core.Client
return socket;
}
void Close()
private async Task ReceiveServerAsync(IFastTunnelClientSocket client)
{
timer_heart.Stop();
socket.CloseAsync();
}
private async Task connSuccessAsync()
{
_logger.LogDebug("通信已建立");
// 心跳开始
timer_heart.Start();
var th = new Thread(ReceiveServer);
th.Start(socket);
// await new PipeHepler(_client, ProceccLine).ProcessLinesAsync();
}
private bool ProceccLine(Socket socket, byte[] line)
{
var cmd = Encoding.UTF8.GetString(line);
HandleServerRequest(cmd);
return true;
}
private void ReceiveServer(object obj)
{
var client = obj as IFastTunnelClientSocket;
byte[] buffer = new byte[1024];
string lastBuffer = string.Empty;
int n = 0;
byte[] buffer = new byte[512];
var tunnelProtocol = new TunnelProtocol();
while (true)
{
try
{
n = client.ReceiveAsync(buffer, cancellationTokenSource.Token).GetAwaiter().GetResult();
if (n == 0)
{
client.CloseAsync();
break;
}
}
/// <see cref="https://docs.microsoft.com/zh-cn/windows/win32/winsock/windows-sockets-error-codes-2"/>
catch (SocketException socketEx)
{
// Connection timed out.
if (socketEx.ErrorCode == 10060)
{
_logger.LogInformation("Connection timed out");
}
else
{
_logger.LogError(socketEx);
}
var res = await client.ReceiveAsync(buffer, CancellationToken.None);
var cmds = tunnelProtocol.HandleBuffer(buffer, 0, res);
break;
}
catch (Exception ex)
{
_logger.LogError(ex);
break;
}
string words = Encoding.UTF8.GetString(buffer, 0, n);
if (!string.IsNullOrEmpty(lastBuffer))
{
words = lastBuffer + words;
lastBuffer = null;
}
var msgs = words.Split("\n");
_logger.LogDebug("recive from server:" + words);
try
{
for (int i = 0; i < msgs.Length - 1; i++)
{
var item = msgs[i];
if (string.IsNullOrEmpty(item))
continue;
if (item.EndsWith("}"))
{
HandleServerRequest(item);
}
else
{
lastBuffer = item;
}
}
if (string.IsNullOrEmpty(msgs[msgs.Length - 1]))
{
continue;
}
lastBuffer = msgs[msgs.Length - 1];
_logger.LogDebug($"lastBuffer={lastBuffer}");
}
catch (Exception ex)
{
_logger.LogError(ex, $"HandleMsg Error {msgs.ToJson()}");
if (cmds == null)
continue;
foreach (var item in cmds)
{
HandleServerRequestAsync(item);
}
}
_logger.LogInformation("stop receive from server");
}
private void HandleServerRequest(string lineCmd)
private async void HandleServerRequestAsync(string lineCmd)
{
Task.Run(() =>
try
{
var cmds = lineCmd.Split("||");
var type = cmds[0];
_logger.LogInformation($"处理 {type}");
TunnelMassage msg = null;
IClientHandler handler;
switch (type)
@ -346,8 +164,12 @@ namespace FastTunnel.Core.Client
throw new Exception($"未处理的消息:{lineCmd}");
}
handler.HandlerMsgAsync(this, msg);
});
await handler.HandlerMsgAsync(this, msg);
}
catch (Exception ex)
{
_logger.LogError(ex);
}
}
}
}

View File

@ -18,16 +18,13 @@ namespace FastTunnel.Core.Client
{
public class FastTunnelServer
{
[Obsolete("ResponseTasks替换", false)]
public ConcurrentDictionary<string, NewRequest> RequestTemp { get; private set; }
= new ConcurrentDictionary<string, NewRequest>();
public ConcurrentDictionary<string, NewRequest> RequestTemp { get; private set; } = new ConcurrentDictionary<string, NewRequest>();
public ConcurrentDictionary<string, TaskCompletionSource<Stream>> ResponseTasks { get; } = new();
public ConcurrentDictionary<string, WebInfo> WebList { get; private set; }
= new ConcurrentDictionary<string, WebInfo>();
public ConcurrentDictionary<string, WebInfo> WebList { get; private set; } = new();
public ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>> SSHList { get; private set; }
public ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>> ForwardList { get; private set; }
= new ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>>();
readonly ILogger _logger;

View File

@ -3,8 +3,10 @@ using FastTunnel.Core.Dispatchers;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Models;
using FastTunnel.Core.Server;
using Microsoft.AspNetCore.Hosting.Server;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
@ -26,15 +28,23 @@ namespace FastTunnel.Core.Dispatchers
_config = config;
}
public async Task DispatchAsync(Socket _socket)
public async void DispatchAsync(Socket _socket)
{
var msgid = Guid.NewGuid().ToString();
await _client.SendCmdAsync(new Message<NewForwardMessage> { MessageType = MessageType.S_NewSSH, Content = new NewForwardMessage { MsgId = msgid, SSHConfig = _config } });
_server.RequestTemp.TryAdd(msgid, new NewRequest
try
{
CustomerClient = _socket,
});
var msgid = Guid.NewGuid().ToString();
await _client.SendCmdAsync(new Message<NewForwardMessage> { MessageType = MessageType.S_NewSSH, Content = new NewForwardMessage { MsgId = msgid, SSHConfig = _config } });
var tcs = new TaskCompletionSource<Stream>();
_server.ResponseTasks.TryAdd(msgid, tcs);
using var stream1 = await tcs.Task;
using var stream2 = new NetworkStream(_socket, true);
await Task.WhenAll(stream1.CopyToAsync(stream2), stream2.CopyToAsync(stream1));
}
catch (Exception)
{
}
}
public void Dispatch(AsyncUserToken token, string words)

View File

@ -11,6 +11,6 @@ namespace FastTunnel.Core.Dispatchers
{
void Dispatch(AsyncUserToken token, string words);
Task DispatchAsync(Socket httpClient);
void DispatchAsync(Socket httpClient);
}
}

View File

@ -32,95 +32,40 @@ namespace FastTunnel.Core.Handlers.Client
public async Task HandlerMsgAsync<T>(FastTunnelClient cleint, T Msg) where T : TunnelMassage
{
var request = Msg as NewCustomerMassage;
if (request.MsgId.Contains("_"))
{
var interval = long.Parse(DateTime.Now.GetChinaTicks()) - long.Parse(request.MsgId.Split('_')[0]);
_logger.LogDebug($"Start SwapMassage {request.MsgId} 服务端耗时:{interval}ms");
}
//var webSocket = new ClientWebSocket();
//webSocket.Options.RemoteCertificateValidationCallback = delegate { return true; };
//webSocket.Options.SetRequestHeader(HeaderConst.FASTTUNNEL_FLAG, "2.0.0");
//webSocket.Options.SetRequestHeader(HeaderConst.FASTTUNNEL_TYPE, HeaderConst.TYPE_SWAP);
//var uri = new Uri($"ws://{cleint.Server.ServerAddr}:{cleint.Server.ServerPort}/{request.MsgId}");
//webSocket.ConnectAsync(uri, CancellationToken.None);
await Task.Yield();
var connecter = new DnsSocket(cleint.Server.ServerAddr, cleint.Server.ServerPort);
connecter.Connect();
// connecter.Send(new Message<SwapMassage> { MessageType = MessageType.C_SwapMsg, Content = new SwapMassage(request.MsgId) });
Stream serverConn = new NetworkStream(connecter.Socket, ownsSocket: true);
var reverse = $"PROXY /{request.MsgId} HTTP/1.1\r\nHost: {cleint.Server.ServerAddr}:{cleint.Server.ServerPort}\r\n\r\n";
var requestMsg = Encoding.ASCII.GetBytes(reverse);
serverConn.WriteAsync(requestMsg, CancellationToken.None).GetAwaiter().GetResult();
_logger.LogDebug($"连接server成功 {request.MsgId}");
var localConnecter = new DnsSocket(request.WebConfig.LocalIp, request.WebConfig.LocalPort);
try
{
localConnecter.Connect();
}
catch (SocketException sex)
{
if (sex.ErrorCode == 10061)
{
_logger.LogInformation($"内网服务不存在:{request.WebConfig.LocalIp}:{request.WebConfig.LocalPort}");
// 内网的站点不存在或无法访问
//string statusLine = "HTTP/1.1 200 OK\r\n";
//string responseHeader = "Content-Type: text/html\r\n";
//byte[] responseBody;
//responseBody = Encoding.UTF8.GetBytes(TunnelResource.Page_NoSite);
//connecter.Send(Encoding.UTF8.GetBytes(statusLine));
//connecter.Send(Encoding.UTF8.GetBytes(responseHeader));
//connecter.Send(Encoding.UTF8.GetBytes("\r\n"));
//connecter.Send(responseBody);
//connecter.Socket.Disconnect(false);
//connecter.Socket.Close();
return;
}
else
{
throw;
}
}
catch (Exception)
{
localConnecter.Close();
throw;
}
_logger.LogDebug($"连接本地成功 {request.MsgId}");
//var streamServer = new WebSocktReadWriteStream(webSocket);
//var streamLocal = new SocketReadWriteStream(localConnecter.Socket);
var localConn = new NetworkStream(localConnecter.Socket, ownsSocket: true);
using Stream serverConn = await Server(cleint, request);
using Stream localConn = await local(request);
_logger.LogDebug($"开始转发 {request.MsgId}");
var taskX = serverConn.CopyToAsync(localConn, CancellationToken.None);
var taskY = localConn.CopyToAsync(serverConn, CancellationToken.None);
await Task.WhenAny(taskX, taskY);
try
{
localConn.Close();
serverConn.Close();
_logger.LogDebug($"转发结束 {request.MsgId}");
}
catch (Exception ex)
{
_logger.LogDebug(ex, $"转发结束 {request.MsgId}");
}
}
private async Task<Stream> local(NewCustomerMassage request)
{
_logger.LogDebug($"连接server成功 {request.MsgId}");
var localConnecter = new DnsSocket(request.WebConfig.LocalIp, request.WebConfig.LocalPort);
await localConnecter.ConnectAsync();
_logger.LogDebug($"连接本地成功 {request.MsgId}");
return new NetworkStream(localConnecter.Socket, ownsSocket: true);
}
private async Task<Stream> Server(FastTunnelClient cleint, NewCustomerMassage request)
{
var connecter = new DnsSocket(cleint.Server.ServerAddr, cleint.Server.ServerPort);
await connecter.ConnectAsync();
Stream serverConn = new NetworkStream(connecter.Socket, ownsSocket: true);
var reverse = $"PROXY /{request.MsgId} HTTP/1.1\r\nHost: {cleint.Server.ServerAddr}:{cleint.Server.ServerPort}\r\n\r\n";
var requestMsg = Encoding.ASCII.GetBytes(reverse);
await serverConn.WriteAsync(requestMsg, CancellationToken.None);
return serverConn;
}
}
}

View File

@ -7,6 +7,9 @@ using System.Text;
using FastTunnel.Core.Sockets;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using System.IO;
using System.Net.Sockets;
using System.Threading;
namespace FastTunnel.Core.Handlers.Client
{
@ -21,16 +24,32 @@ namespace FastTunnel.Core.Handlers.Client
public async Task HandlerMsgAsync<T>(FastTunnelClient cleint, T Msg)
where T : TunnelMassage
{
var request_ssh = Msg as NewForwardMessage;
var request = Msg as NewForwardMessage;
await Task.Yield();
var connecter_ssh = new DnsSocket(cleint.Server.ServerAddr, cleint.Server.ServerPort);
connecter_ssh.Connect();
connecter_ssh.Send(new Message<SwapMassage> { MessageType = MessageType.C_SwapMsg, Content = new SwapMassage(request_ssh.MsgId) });
using var stream1 = await Server(cleint, request);
using var stream2 = await local(request);
var localConnecter_ssh = new DnsSocket(request_ssh.SSHConfig.LocalIp, request_ssh.SSHConfig.LocalPort);
localConnecter_ssh.Connect();
new SocketSwap(connecter_ssh.Socket, localConnecter_ssh.Socket, _logger, request_ssh.MsgId).StartSwap();
await Task.WhenAll(stream1.CopyToAsync(stream2), stream2.CopyToAsync(stream1));
}
private async Task<Stream> local(NewForwardMessage request)
{
var localConnecter = new DnsSocket(request.SSHConfig.LocalIp, request.SSHConfig.LocalPort);
await localConnecter.ConnectAsync();
return new NetworkStream(localConnecter.Socket, true);
}
private async Task<Stream> Server(FastTunnelClient cleint, NewForwardMessage request)
{
var connecter = new DnsSocket(cleint.Server.ServerAddr, cleint.Server.ServerPort);
await connecter.ConnectAsync();
Stream serverConn = new NetworkStream(connecter.Socket, ownsSocket: true);
var reverse = $"PROXY /{request.MsgId} HTTP/1.1\r\nHost: {cleint.Server.ServerAddr}:{cleint.Server.ServerPort}\r\n\r\n";
var requestMsg = Encoding.ASCII.GetBytes(reverse);
await serverConn.WriteAsync(requestMsg, CancellationToken.None);
return serverConn;
}
}
}

View File

@ -70,21 +70,14 @@ namespace FastTunnel.Core.Handlers
}
}
if (requet.SSH != null && requet.SSH.Count() > 0)
if (requet.Forwards != null && requet.Forwards.Count() > 0)
{
hasTunnel = true;
foreach (var item in requet.SSH)
foreach (var item in requet.Forwards)
{
try
{
//if (item.RemotePort.Equals(server.serverOption.CurrentValue.BindPort))
//{
// _logger.LogError($"RemotePort can not be same with BindPort: {item.RemotePort}");
// continue;
//}
if (item.RemotePort.Equals(server.serverOption.CurrentValue.WebProxyPort))
{
_logger.LogError($"RemotePort can not be same with ProxyPort_HTTP: {item.RemotePort}");
@ -92,11 +85,11 @@ namespace FastTunnel.Core.Handlers
}
ForwardInfo<ForwardHandlerArg> old;
if (server.SSHList.TryGetValue(item.RemotePort, out old))
if (server.ForwardList.TryGetValue(item.RemotePort, out old))
{
_logger.LogDebug($"Remove Listener {old.Listener.ListenIp}:{old.Listener.ListenPort}");
old.Listener.Stop();
server.SSHList.TryRemove(item.RemotePort, out ForwardInfo<ForwardHandlerArg> _);
server.ForwardList.TryRemove(item.RemotePort, out ForwardInfo<ForwardHandlerArg> _);
}
var ls = new PortProxyListener("0.0.0.0", item.RemotePort, _logger);
@ -104,7 +97,7 @@ namespace FastTunnel.Core.Handlers
ls.Start(new ForwardDispatcher(server, client, item));
// listen success
server.SSHList.TryAdd(item.RemotePort, new ForwardInfo<ForwardHandlerArg> { Listener = ls, Socket = client, SSHConfig = item });
server.ForwardList.TryAdd(item.RemotePort, new ForwardInfo<ForwardHandlerArg> { Listener = ls, Socket = client, SSHConfig = item });
_logger.LogDebug($"SSH proxy success: {item.RemotePort} => {item.LocalIp}:{item.LocalPort}");
sb.Append($" TCP | {server.serverOption.CurrentValue.WebDomain}:{item.RemotePort} => {item.LocalIp}:{item.LocalPort}");

View File

@ -37,12 +37,12 @@ namespace FastTunnel.Core.Listener
listenSocket.Bind(localEndPoint);
}
public void Start(IListenerDispatcher requestDispatcher, int backlog = 100)
public void Start(IListenerDispatcher requestDispatcher)
{
shutdown = false;
_requestDispatcher = requestDispatcher;
listenSocket.Listen(backlog);
listenSocket.Listen();
StartAccept(null);
}

View File

@ -15,6 +15,6 @@ namespace FastTunnel.Core.Models
/// <summary>
/// 端口转发隧道列表
/// </summary>
public IEnumerable<ForwardConfig> SSH { get; set; }
public IEnumerable<ForwardConfig> Forwards { get; set; }
}
}

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Sockets
{
@ -30,6 +31,13 @@ namespace FastTunnel.Core.Sockets
Socket.Connect(dnsEndPoint);
}
public async Task ConnectAsync()
{
DnsEndPoint dnsEndPoint = new DnsEndPoint(_host, _port);
await Socket.ConnectAsync(dnsEndPoint);
}
public void Send(byte[] data)
{
Socket.Send(data);

View File

@ -1,6 +0,0 @@
@*
For more information on enabling MVC for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
*@
@{
}
<div style="text-align:center;padding:50px;">Soming soon!</div>