This commit is contained in:
SpringHgui 2021-08-01 18:32:32 +08:00
parent d98b7fea76
commit 3e5020b890
60 changed files with 1085 additions and 288 deletions

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ publish
/SuiDao.Client/Properties/PublishProfiles/FolderProfile.pubxml
/FastTunnel.Core/*.user
/build
/FastTunnel.Server/.config

View File

@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using FastTunnel.Core.Extensions;
using Microsoft.Extensions.DependencyInjection;
using FastTunnel.Core.Client;
using System;
namespace FastTunnel.Client
{
@ -10,7 +11,14 @@ namespace FastTunnel.Client
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
try
{
CreateHostBuilder(args).Build().Run();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>

View File

@ -13,7 +13,7 @@
//"ServerAddr": "my.com",
"ServerAddr": "127.0.0.1",
//
"ServerPort": 1271
"ServerPort": 1270
},
"Webs": [
{
@ -37,11 +37,12 @@
],
/**
* ssh穿ssh访/mysql/erp/
*
* 访/mysql/erp/TCP
* linux#ssh -oPort=12701 {root}@{ServerAddr} ServerAddr iproot
* 访访
*/
"SSH": [
"Forward": [
{
"LocalIp": "127.0.0.1",
"LocalPort": 8090,

View File

@ -1,6 +1,4 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using FastTunnel.Core.Config;
using FastTunnel.Core.Config;
using FastTunnel.Core.Models;
using System;
using System.Net.Sockets;
@ -15,12 +13,15 @@ using Microsoft.Extensions.Configuration;
using FastTunnel.Core.Server;
using FastTunnel.Core.Sockets;
using Microsoft.Extensions.Options;
using System.Net.WebSockets;
using System.Text.Json;
namespace FastTunnel.Core.Client
{
public class FastTunnelClient : IFastTunnelClient
{
Socket _client;
//Socket _client;
private IFastTunnelClientSocket socket;
protected ILogger<FastTunnelClient> _logger;
@ -31,19 +32,19 @@ namespace FastTunnel.Core.Client
int reTrySpan = 10 * 1000; // 登陆失败后重试间隔
HttpRequestHandler _newCustomerHandler;
NewSSHHandler _newSSHHandler;
NewForwardHandler _newSSHHandler;
LogHandler _logHandler;
ClientHeartHandler _clientHeartHandler;
Func<Socket> lastLogin;
Message<LogInMassage> loginMsg;
protected readonly IOptionsMonitor<DefaultClientConfig> _configuration;
private readonly CancellationTokenSource cancellationTokenSource = new();
public SuiDaoServer Server { get; protected set; }
public FastTunnelClient(
ILogger<FastTunnelClient> logger,
HttpRequestHandler newCustomerHandler,
NewSSHHandler newSSHHandler, LogHandler logHandler,
NewForwardHandler newSSHHandler, LogHandler logHandler,
IOptionsMonitor<DefaultClientConfig> configuration,
ClientHeartHandler clientHeartHandler)
{
@ -60,7 +61,7 @@ namespace FastTunnel.Core.Client
timer_heart.Elapsed += HeartElapsed;
}
private void reConn()
private async Task reConnAsync()
{
Close();
@ -71,7 +72,7 @@ namespace FastTunnel.Core.Client
Thread.Sleep(reTrySpan);
_logger.LogInformation("登录重试...");
_client = lastLogin.Invoke();
socket = await loginAsync(CancellationToken.None);
break;
}
@ -90,12 +91,12 @@ namespace FastTunnel.Core.Client
try
{
_client.SendCmd(new Message<HeartMassage> { MessageType = MessageType.Heart, Content = null });
socket.SendAsync(new Message<HeartMassage> { MessageType = MessageType.Heart, Content = null }, cancellationTokenSource.Token).Wait();
}
catch (Exception)
{
// 与服务端断开连接
reConn();
reConnAsync();
}
finally
{
@ -108,43 +109,77 @@ namespace FastTunnel.Core.Client
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="customLoginMsg">自定义登录信息,可进行扩展业务</param>
public void Start()
public async Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("===== FastTunnel Client Start =====");
CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, this.cancellationTokenSource.Token);
lastLogin = login;
_logger.LogInformation("===== FastTunnel Client Start =====");
try
{
_client = lastLogin.Invoke();
socket = await loginAsync(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
reConn();
reConnAsync();
return;
}
_ = connSuccessAsync();
}
//protected virtual Socket login()
//{
// Server = _configuration.CurrentValue.Server;
protected virtual Socket login()
// 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;
DnsSocket _client = null;
_logger.LogInformation($"正在连接服务端 {Server.ServerAddr}:{Server.ServerPort}");
try
{
// 连接到的目标IP
if (_client == null)
{
_client = new DnsSocket(Server.ServerAddr, Server.ServerPort);
}
socket = new DefultClientSocket();
_client.Connect();
await socket.ConnectAsync(
new Uri($"ws://{_configuration.CurrentValue.Server.ServerAddr}:{_configuration.CurrentValue.Server.ServerPort}"), cancellationToken);
_logger.LogInformation("连接成功");
}
@ -159,29 +194,19 @@ namespace FastTunnel.Core.Client
Content = new LogInMassage
{
Webs = _configuration.CurrentValue.Webs,
SSH = _configuration.CurrentValue.SSH,
SSH = _configuration.CurrentValue.Forwards,
},
};
// 登录
_client.Send(loginMsg);
return _client.Socket;
await socket.SendAsync(loginMsg, cancellationToken);
return socket;
}
void Close()
{
timer_heart.Stop();
try
{
_client?.Shutdown(SocketShutdown.Both);
}
catch (Exception)
{
}
_client?.Close();
socket.CloseAsync();
}
private async Task connSuccessAsync()
@ -192,7 +217,7 @@ namespace FastTunnel.Core.Client
timer_heart.Start();
var th = new Thread(ReceiveServer);
th.Start(_client);
th.Start(socket);
// await new PipeHepler(_client, ProceccLine).ProcessLinesAsync();
}
@ -205,7 +230,7 @@ namespace FastTunnel.Core.Client
private void ReceiveServer(object obj)
{
var client = obj as Socket;
var client = obj as IFastTunnelClientSocket;
byte[] buffer = new byte[1024];
string lastBuffer = string.Empty;
@ -215,10 +240,10 @@ namespace FastTunnel.Core.Client
{
try
{
n = client.Receive(buffer);
n = client.ReceiveAsync(buffer, cancellationTokenSource.Token).GetAwaiter().GetResult();
if (n == 0)
{
client.Shutdown(SocketShutdown.Both);
client.CloseAsync();
break;
}
}
@ -290,36 +315,38 @@ namespace FastTunnel.Core.Client
_logger.LogInformation("stop receive from server");
}
private void HandleServerRequest(string words)
private void HandleServerRequest(string lineCmd)
{
Task.Run(() =>
{
var Msg = JsonConvert.DeserializeObject<Message<JObject>>(words);
if (Msg.MessageType != MessageType.Heart)
{
_logger.LogDebug($"HandleServerRequest {words}");
}
var cmds = lineCmd.Split("||");
var type = cmds[0];
TunnelMassage msg = null;
IClientHandler handler;
switch (Msg.MessageType)
switch (type)
{
case MessageType.Heart:
case "Heart":
handler = _clientHeartHandler;
msg = JsonSerializer.Deserialize<HeartMassage>(cmds[1]);
break;
case MessageType.S_NewCustomer:
case "S_NewCustomer":
handler = _newCustomerHandler;
msg = JsonSerializer.Deserialize<NewCustomerMassage>(cmds[1]);
break;
case MessageType.S_NewSSH:
case "S_NewSSH":
handler = _newSSHHandler;
msg = JsonSerializer.Deserialize<NewForwardMessage>(cmds[1]);
break;
case MessageType.Log:
case "Log":
handler = _logHandler;
msg = JsonSerializer.Deserialize<LogMassage>(cmds[1]);
break;
default:
throw new Exception($"未处理的消息:{Msg.MessageType} {Msg.Content}");
throw new Exception($"未处理的消息:{lineCmd}");
}
handler.HandlerMsg(this, Msg);
handler.HandlerMsgAsync(this, msg);
});
}
}

View File

@ -27,35 +27,37 @@ namespace FastTunnel.Core.Client
public ConcurrentDictionary<string, WebInfo> WebList { get; private set; }
= new ConcurrentDictionary<string, WebInfo>();
public ConcurrentDictionary<int, SSHInfo<SSHHandlerArg>> SSHList { get; private set; }
= new ConcurrentDictionary<int, SSHInfo<SSHHandlerArg>>();
public ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>> SSHList { get; private set; }
= new ConcurrentDictionary<int, ForwardInfo<ForwardHandlerArg>>();
readonly ILogger _logger;
readonly ClientListenerV2 clientListener;
readonly HttpListenerV2 http_listener;
//readonly ClientListenerV2 clientListener;
//readonly HttpListenerV2 http_listener;
public readonly IOptionsMonitor<DefaultServerConfig> serverOption;
public IProxyConfigProvider proxyConfig;
public FastTunnelServer(ILogger<FastTunnelServer> logger, IProxyConfigProvider proxyConfig, IOptionsMonitor<DefaultServerConfig> serverSettings)
{
_logger = logger;
serverOption = serverSettings;
this.proxyConfig = proxyConfig;
clientListener = new ClientListenerV2(this, proxyConfig, "0.0.0.0", serverOption.CurrentValue.BindPort, _logger);
http_listener = new HttpListenerV2("0.0.0.0", serverOption.CurrentValue.WebProxyPort, _logger);
// clientListener = new ClientListenerV2(this, proxyConfig, "0.0.0.0", serverOption.CurrentValue.BindPort, _logger);
// http_listener = new HttpListenerV2("0.0.0.0", serverOption.CurrentValue.WebProxyPort, _logger);
}
public void Run()
{
_logger.LogInformation("===== FastTunnel Server Starting =====");
listenClient();
//listenHttp();
// listenClient();
// listenHttp();
}
private void listenClient()
{
clientListener.Start();
}
//private void listenClient()
//{
// clientListener.Start();
//}
//private void listenHttp()
//{
@ -66,8 +68,8 @@ namespace FastTunnel.Core.Client
{
_logger.LogInformation("===== FastTunnel Server Stoping =====");
clientListener.Stop();
http_listener.Stop();
//clientListener.Stop();
//http_listener.Stop();
}
}
}

View File

@ -2,12 +2,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Client
{
public interface IFastTunnelClient
{
void Start();
Task StartAsync(CancellationToken cancellationToken);
}
}

View File

@ -12,6 +12,6 @@ namespace FastTunnel.Core.Config
public IEnumerable<WebConfig> Webs { get; set; }
public IEnumerable<SSHConfig> SSH { get; set; }
public IEnumerable<ForwardConfig> Forwards { get; set; }
}
}

View File

@ -7,7 +7,7 @@ namespace FastTunnel.Core.Config
{
public class DefaultServerConfig : IServerConfig
{
public int BindPort { get; set; }
// public int BindPort { get; set; }
public string WebDomain { get; set; }
@ -17,6 +17,6 @@ namespace FastTunnel.Core.Config
public bool WebHasNginxProxy { get; set; } = false;
public bool SSHEnabled { get; set; } = false;
public bool EnableForward { get; set; } = false;
}
}

View File

@ -13,7 +13,7 @@ namespace FastTunnel.Core.Config
public IEnumerable<WebConfig> Webs { get; set; }
public IEnumerable<SSHConfig> SSH { get; set; }
public IEnumerable<ForwardConfig> Forwards { get; set; }
}
public class SuiDaoServer

View File

@ -6,7 +6,7 @@ namespace FastTunnel.Core.Config
{
public interface IServerConfig
{
int BindPort { get; set; }
// int BindPort { get; set; }
#region Web相关配置
@ -28,6 +28,6 @@ namespace FastTunnel.Core.Config
#endregion
bool SSHEnabled { get; set; }
bool EnableForward { get; set; }
}
}

View File

@ -6,27 +6,31 @@ using FastTunnel.Core.Server;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Dispatchers
{
public class SSHDispatcher : IListenerDispatcher
public class ForwardDispatcher : IListenerDispatcher
{
private FastTunnelServer _server;
private Socket _client;
private SSHConfig _config;
private WebSocket _client;
private ForwardConfig _config;
public SSHDispatcher(FastTunnelServer server, Socket client, SSHConfig config)
public ForwardDispatcher(FastTunnelServer server, WebSocket client, ForwardConfig config)
{
_server = server;
_client = client;
_config = config;
}
public void Dispatch(Socket _socket)
public async Task DispatchAsync(Socket _socket)
{
var msgid = Guid.NewGuid().ToString();
_client.SendCmd(new Message<NewSSHRequest> { MessageType = MessageType.S_NewSSH, Content = new NewSSHRequest { MsgId = msgid, SSHConfig = _config } });
await _client.SendCmdAsync(new Message<NewForwardMessage> { MessageType = MessageType.S_NewSSH, Content = new NewForwardMessage { MsgId = msgid, SSHConfig = _config } });
_server.RequestTemp.TryAdd(msgid, new NewRequest
{
CustomerClient = _socket,

View File

@ -3,6 +3,7 @@ using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Dispatchers
{
@ -10,6 +11,6 @@ namespace FastTunnel.Core.Dispatchers
{
void Dispatch(AsyncUserToken token, string words);
void Dispatch(Socket httpClient);
Task DispatchAsync(Socket httpClient);
}
}

View File

@ -1,12 +0,0 @@
using Newtonsoft.Json;
using FastTunnel.Core.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace FastTunnel.Core.Extensions
{
public static class MessageExtension
{
}
}

View File

@ -1,7 +1,7 @@
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
namespace FastTunnel.Core.Extensions
{
@ -9,7 +9,13 @@ namespace FastTunnel.Core.Extensions
{
public static string ToJson(this object message)
{
return JsonConvert.SerializeObject(message, Formatting.None);
if (message == null)
{
return null;
}
var jsonOptions = new JsonSerializerOptions { WriteIndented = false };
return JsonSerializer.Serialize(message, message.GetType(), jsonOptions);
}
}
}

View File

@ -37,7 +37,7 @@ namespace FastTunnel.Core.Extensions
.AddSingleton<ClientHeartHandler>()
.AddSingleton<LogHandler>()
.AddSingleton<HttpRequestHandler>()
.AddSingleton<NewSSHHandler>();
.AddSingleton<NewForwardHandler>();
services.AddHostedService<ServiceFastTunnelClient>();
}

View File

@ -0,0 +1,30 @@
using FastTunnel.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Extensions
{
public static class WebSocketExtension
{
public static async Task SendCmdAsync<T>(this WebSocket socket, Message<T> message,
WebSocketMessageType webSocketMessage, bool end, CancellationToken cancellationToken)
where T : TunnelMassage
{
var msg = Encoding.UTF8.GetBytes($"{message.MessageType.ToString()}||{message.Content.ToJson()}\n");
await socket.SendAsync(msg, webSocketMessage, end, cancellationToken);
}
public static async Task SendCmdAsync<T>(this WebSocket socket, Message<T> message)
where T : TunnelMassage
{
var msg = Encoding.UTF8.GetBytes($"{message.MessageType.ToString()}||{message.Content.ToJson()}\n");
await socket.SendAsync(msg, WebSocketMessageType.Binary, false, CancellationToken.None);
}
}
}

View File

@ -22,14 +22,17 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="Client\FastTunnelClient - Copy.cs" />
<Compile Remove="Client\SuiDaoServer.cs.BASE.cs" />
<Compile Remove="Client\SuiDaoServer.cs.LOCAL.cs" />
<Compile Remove="Client\SuiDaoServer.cs.REMOTE.cs" />
<Compile Remove="Dispatchers\ClientDispatcher.cs" />
<Compile Remove="Dispatchers\HttpDispatcher.cs" />
<Compile Remove="Dispatchers\HttpDispatcherV2.cs" />
<Compile Remove="Handlers\Server\SwapMessageHandler.cs" />
<Compile Remove="Listener.cs" />
<Compile Remove="Listener\ClientListener.cs" />
<Compile Remove="Listener\ClientListenerV2.cs" />
<Compile Remove="Listener\HttpListener.cs" />
<Compile Remove="Server.cs" />
<Compile Remove="Sockets\AsyncSocketSwap.cs" />
@ -39,7 +42,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0-preview.6.21352.12" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Microsoft.VisualStudio.Validation" Version="17.0.16-alpha" />
<PackageReference Include="Yarp.ReverseProxy" Version="1.0.0-preview.12.21328.2" />
</ItemGroup>

View File

@ -2,7 +2,6 @@
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -53,7 +52,6 @@ namespace FastTunnel.Core.Forwarder
public async ValueTask<Stream> proxyAsync(string host, CancellationToken cancellation)
{
WebInfo web;
if (!_fastTunnelServer.WebList.TryGetValue(host, out web))
{
@ -63,16 +61,18 @@ namespace FastTunnel.Core.Forwarder
try
{
var RequestId = Guid.NewGuid().ToString().Replace("-", "");
_logger.LogDebug($"[send swap]:{RequestId}");
_logger.LogInformation($"[发送swap指令]:{RequestId}");
// 发送指令给客户端,等待建立隧道
web.Socket.SendCmd(new Message<NewCustomerMassage> { MessageType = MessageType.S_NewCustomer, Content = new NewCustomerMassage { MsgId = RequestId, WebConfig = web.WebConfig } });
await web.Socket.SendCmdAsync(new Message<NewCustomerMassage> { MessageType = MessageType.S_NewCustomer, Content = new NewCustomerMassage { MsgId = RequestId, WebConfig = web.WebConfig } });
// TODO:超时处理
TaskCompletionSource<Stream> task = new(cancellation);
_fastTunnelServer.ResponseTasks.TryAdd(RequestId, task);
return await task.Task;
var res = await task.Task;
_logger.LogInformation($"[收到swap指令]:{RequestId}");
return res;
}
catch (Exception ex)
{

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder
{
public interface IReadWriteStream
{
int Read(byte[] buffer);
void Write(byte[] buffer, int index, int num);
}
}

View File

@ -1,24 +1,103 @@
using Microsoft.AspNetCore.Http;
using FastTunnel.Core.Client;
using FastTunnel.Core.Forwarder;
using FastTunnel.Core.Models;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using Yarp.ReverseProxy.Configuration;
namespace FastTunnel.Core.MiddleWares
{
public class FastTunnelClientHandler
{
public static async Task Handle(HttpContext context, Func<Task> next)
ILogger<FastTunnelClientHandler> logger;
FastTunnelServer fastTunnelServer;
public FastTunnelClientHandler(ILogger<FastTunnelClientHandler> logger, FastTunnelServer fastTunnelServer)
{
if (!context.WebSockets.IsWebSocketRequest)
this.logger = logger;
this.fastTunnelServer = fastTunnelServer;
}
public async Task Handle(HttpContext context, Func<Task> next)
{
if (!context.WebSockets.IsWebSocketRequest
|| !context.Request.Headers.TryGetValue(HeaderConst.FASTTUNNEL_FLAG, out var version)
|| !context.Request.Headers.TryGetValue(HeaderConst.FASTTUNNEL_TYPE, out var type))
{
await next();
return;
};
if (HeaderConst.TYPE_CLIENT.Equals(type))
{
await Client(context, next);
}
else if (HeaderConst.TYPE_SWAP.Equals(type))
{
await Swap(context, next);
}
else
{
logger.LogError($"参数异常ConnectionType类型为{type}");
}
}
private async Task Swap(HttpContext context, Func<Task> next)
{
var requestId = context.Request.Path.Value.Trim('/');
if (!fastTunnelServer.ResponseTasks.TryGetValue(requestId, out var response))
{
logger.LogError($"requestId不存在:{requestId}");
return;
};
var lifetime = context.Features.Get<IConnectionLifetimeFeature>();
var transport = context.Features.Get<IConnectionTransportFeature>();
if (lifetime == null || transport == null)
{
await next();
return;
}
using var stream = new WebSocketStream(lifetime, transport);
response.TrySetResult(stream);
logger.LogInformation($"Swap Set {requestId}");
var closedAwaiter = new TaskCompletionSource();
lifetime.ConnectionClosed.Register((task) => { (task as TaskCompletionSource).SetResult(); }, closedAwaiter);
await closedAwaiter.Task;
logger.LogInformation($"Swap Completion {requestId}");
}
private async Task Client(HttpContext context, Func<Task> next)
{
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
var client = new TunnelClient(logger, webSocket, fastTunnelServer);
this.logger.LogInformation($"{client} 客户端连接成功");
try
{
await client.ReviceAsync();
}
catch (Exception ex)
{
logger.LogError(ex, "通信异常");
}
this.logger.LogInformation($"{client} 客户端断开连接");
}
}
}

View File

@ -0,0 +1,60 @@
using FastTunnel.Core.Client;
using FastTunnel.Core.MiddleWares;
using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder.MiddleWare
{
public class FastTunnelSwapHandler
{
ILogger<FastTunnelClientHandler> logger;
FastTunnelServer fastTunnelServer;
public FastTunnelSwapHandler(ILogger<FastTunnelClientHandler> logger, FastTunnelServer fastTunnelServer)
{
this.logger = logger;
this.fastTunnelServer = fastTunnelServer;
}
public async Task Handle(HttpContext context, Func<Task> next)
{
if (context.Request.Method != "PROXY")
{
await next();
return;
}
var requestId = context.Request.Path.Value.Trim('/');
if (!fastTunnelServer.ResponseTasks.TryRemove(requestId, out var responseAwaiter))
{
logger.LogError($"requestId不存在:{requestId}");
return;
};
var lifetime = context.Features.Get<IConnectionLifetimeFeature>();
var transport = context.Features.Get<IConnectionTransportFeature>();
if (lifetime == null || transport == null)
{
await next();
return;
}
logger.LogInformation($"Swap Set {requestId}");
using var reverseConnection = new WebSocketStream(lifetime, transport);
responseAwaiter.TrySetResult(reverseConnection);
var closedAwaiter = new TaskCompletionSource<object>();
lifetime.ConnectionClosed.Register((task) => { (task as TaskCompletionSource<object>).SetResult(null); }, closedAwaiter);
await closedAwaiter.Task;
logger.LogInformation($"Swap close {requestId}");
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder
{
public class SocketReadWriteStream : IReadWriteStream
{
Socket socket;
public SocketReadWriteStream(Socket socket)
{
this.socket = socket;
}
public int Read(byte[] buffer)
{
return socket.Receive(buffer);
}
public void Write(byte[] buffer, int index, int num)
{
socket.Send(buffer, index, num, SocketFlags.None);
}
}
}

View File

@ -0,0 +1,112 @@
using Microsoft;
using Microsoft.AspNetCore.Connections.Features;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder
{
sealed class WebSocketStream : Stream
{
private readonly Stream readStream;
private readonly Stream wirteStream;
private readonly IConnectionLifetimeFeature lifetimeFeature;
public WebSocketStream(IConnectionLifetimeFeature lifetimeFeature, IConnectionTransportFeature transportFeature)
{
this.readStream = transportFeature.Transport.Input.AsStream();
this.wirteStream = transportFeature.Transport.Output.AsStream();
this.lifetimeFeature = lifetimeFeature;
}
public WebSocketStream(Stream stream)
{
this.readStream = stream;
this.wirteStream = stream;
this.lifetimeFeature = null;
}
public override bool CanRead => true;
public override bool CanSeek => false;
public override bool CanWrite => true;
public override long Length => throw new NotSupportedException();
public override long Position
{
get => throw new NotSupportedException();
set => throw new NotSupportedException();
}
public override void Flush()
{
this.wirteStream.Flush();
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
return this.wirteStream.FlushAsync(cancellationToken);
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
return this.readStream.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
this.wirteStream.Write(buffer, offset, count);
}
public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
{
return this.readStream.ReadAsync(buffer, cancellationToken);
}
public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return this.readStream.ReadAsync(buffer, offset, count, cancellationToken);
}
public override void Write(ReadOnlySpan<byte> buffer)
{
this.wirteStream.Write(buffer);
}
public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
return this.wirteStream.WriteAsync(buffer, offset, count, cancellationToken);
}
public override async ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
{
await this.wirteStream.WriteAsync(buffer, cancellationToken);
}
protected override void Dispose(bool disposing)
{
this.lifetimeFeature?.Abort();
}
public override ValueTask DisposeAsync()
{
this.lifetimeFeature?.Abort();
return ValueTask.CompletedTask;
}
}
}

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Forwarder
{
public class WebSocktReadWriteStream : IReadWriteStream
{
WebSocket webSocket;
public WebSocktReadWriteStream(WebSocket webSocket)
{
this.webSocket = webSocket;
}
public int Read(byte[] buffer)
{
if (this.webSocket.CloseStatus.HasValue)
{
return 0;
}
return webSocket.ReceiveAsync(buffer, CancellationToken.None).GetAwaiter().GetResult().Count;
}
public void Write(byte[] buffer, int offset, int count)
{
this.webSocket.SendAsync(new ArraySegment<byte>(buffer, offset, count), WebSocketMessageType.Binary, true, CancellationToken.None);
}
}
}

View File

@ -1,18 +1,19 @@
using FastTunnel.Core.Config;
using FastTunnel.Core.Client;
using FastTunnel.Core.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Handlers.Client
{
public class ClientHeartHandler : IClientHandler
{
public void HandlerMsg(FastTunnelClient cleint, Message<JObject> Msg)
public async Task HandlerMsgAsync<T>(FastTunnelClient cleint, T Msg) where T : TunnelMassage
{
cleint.lastHeart = DateTime.Now;
await Task.Yield();
}
}
}

View File

@ -1,7 +1,6 @@
using FastTunnel.Core.Config;
using FastTunnel.Core.Client;
using FastTunnel.Core.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.IO;
@ -12,6 +11,12 @@ using System.Threading.Tasks;
using FastTunnel.Core.Sockets;
using Microsoft.Extensions.Logging;
using FastTunnel.Core.Utility.Extensions;
using System.Net.WebSockets;
using FastTunnel.Core.Forwarder;
using Microsoft;
using Microsoft.AspNetCore.DataProtection;
using FastTunnel.Core.Server;
using System.Data.Common;
namespace FastTunnel.Core.Handlers.Client
{
@ -24,9 +29,9 @@ namespace FastTunnel.Core.Handlers.Client
_logger = logger;
}
public void HandlerMsg(FastTunnelClient cleint, Message<JObject> Msg)
public async Task HandlerMsgAsync<T>(FastTunnelClient cleint, T Msg) where T : TunnelMassage
{
var request = Msg.Content.ToObject<NewCustomerMassage>();
var request = Msg as NewCustomerMassage;
if (request.MsgId.Contains("_"))
{
var interval = long.Parse(DateTime.Now.GetChinaTicks()) - long.Parse(request.MsgId.Split('_')[0]);
@ -34,10 +39,25 @@ namespace FastTunnel.Core.Handlers.Client
_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) });
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);
@ -45,28 +65,25 @@ namespace FastTunnel.Core.Handlers.Client
try
{
localConnecter.Connect();
_logger.LogDebug($"连接本地成功 {request.MsgId}");
new SocketSwap(connecter.Socket, localConnecter.Socket, _logger, request.MsgId).StartSwap();
}
catch (SocketException sex)
{
localConnecter.Close();
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);
//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.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();
//connecter.Socket.Disconnect(false);
//connecter.Socket.Close();
return;
}
else
@ -79,6 +96,31 @@ namespace FastTunnel.Core.Handlers.Client
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);
_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}");
}
}
}
}

View File

@ -1,15 +1,15 @@
using FastTunnel.Core.Config;
using FastTunnel.Core.Client;
using FastTunnel.Core.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Handlers.Client
{
public interface IClientHandler
{
public void HandlerMsg(FastTunnelClient cleint, Message<JObject> Msg);
Task HandlerMsgAsync<T>(FastTunnelClient cleint, T Msg) where T : TunnelMassage;
}
}

View File

@ -1,10 +1,10 @@
using FastTunnel.Core.Config;
using FastTunnel.Core.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using System;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Client;
using System.Threading.Tasks;
namespace FastTunnel.Core.Handlers.Client
{
@ -17,11 +17,13 @@ namespace FastTunnel.Core.Handlers.Client
_logger = logger;
}
public void HandlerMsg(FastTunnelClient cleint, Message<JObject> Msg)
public async Task HandlerMsgAsync<T>(FastTunnelClient cleint, T Msg)
where T : TunnelMassage
{
try
{
var msg = Msg.Content.ToObject<LogMassage>();
await Task.Yield();
var msg = Msg as LogMassage;
switch (msg.MsgType)
{

View File

@ -1,33 +1,35 @@
using FastTunnel.Core.Config;
using FastTunnel.Core.Client;
using FastTunnel.Core.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text;
using FastTunnel.Core.Sockets;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
namespace FastTunnel.Core.Handlers.Client
{
public class NewSSHHandler : IClientHandler
public class NewForwardHandler : IClientHandler
{
ILogger<NewSSHHandler> _logger;
public NewSSHHandler(ILogger<NewSSHHandler> logger)
ILogger<NewForwardHandler> _logger;
public NewForwardHandler(ILogger<NewForwardHandler> logger)
{
_logger = logger;
}
public void HandlerMsg(FastTunnelClient cleint, Message<JObject> Msg)
public async Task HandlerMsgAsync<T>(FastTunnelClient cleint, T Msg)
where T : TunnelMassage
{
var request_ssh = Msg.Content.ToObject<NewSSHRequest>();
var request_ssh = 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) });
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();
}
}

View File

@ -1,16 +0,0 @@
using FastTunnel.Core.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text;
namespace FastTunnel.Core.Handlers.Server
{
public class ConfigHandler : IConfigHandler
{
public LogInMassage GetConfig(JObject content)
{
return content.ToObject<LogInMassage>();
}
}
}

View File

@ -1,11 +1,12 @@
using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Handlers.Server
{
@ -13,9 +14,11 @@ namespace FastTunnel.Core.Handlers.Server
{
public bool NeedRecive => true;
public void HandlerMsg(FastTunnelServer server, Socket client, Message<JObject> msg)
public async Task<bool> HandlerMsg<T>(FastTunnelServer server, WebSocket client, T msg)
where T : TunnelMassage
{
client.SendCmd(new Message<HeartMassage>() { MessageType = MessageType.Heart, Content = null });
await client.SendCmdAsync(new Message<HeartMassage>() { MessageType = MessageType.Heart, Content = new HeartMassage { } });
return NeedRecive;
}
}
}

View File

@ -1,10 +1,11 @@
using FastTunnel.Core.Client;
using FastTunnel.Core.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Handlers
{
@ -12,6 +13,8 @@ namespace FastTunnel.Core.Handlers
{
Boolean NeedRecive { get; }
void HandlerMsg(FastTunnelServer server, Socket client, Message<JObject> msg);
Task<bool> HandlerMsg<T>(FastTunnelServer server, WebSocket client, T msg) where T : TunnelMassage;
//void HandlerMsg(FastTunnelServer server, Socket client, Message<JObject> msg);
}
}

View File

@ -1,13 +0,0 @@
using FastTunnel.Core.Models;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Text;
namespace FastTunnel.Core.Handlers
{
public interface IConfigHandler
{
LogInMassage GetConfig(JObject content);
}
}

View File

@ -7,12 +7,13 @@ using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Listener;
using FastTunnel.Core.Models;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
using Yarp.ReverseProxy.Configuration;
using Yarp.Sample;
@ -23,7 +24,6 @@ namespace FastTunnel.Core.Handlers
ILogger _logger;
public bool NeedRecive => true;
IConfigHandler _configHandler;
static object _locker = new object();
IProxyConfigProvider proxyConfig;
@ -32,47 +32,12 @@ namespace FastTunnel.Core.Handlers
{
this.proxyConfig = proxyConfig;
this._logger = logger;
var custome = FastTunnelGlobal.GetCustomHandler<IConfigHandler>();
this._configHandler = custome == null ? new ConfigHandler() : custome;
}
public LogInMassage GetConfig(JObject content)
{
return _configHandler.GetConfig(content);
}
public void HandlerMsg(FastTunnelServer server, Socket client, Message<JObject> msg)
{
lock (_locker)
{
HandleLogin(server, client, GetConfig(msg.Content));
}
}
public void HandleLogin(FastTunnelServer server, Socket client, LogInMassage requet)
private async Task HandleLoginAsync(FastTunnelServer server, WebSocket client, LogInMassage requet)
{
bool hasTunnel = false;
var filters = FastTunnelGlobal.GetFilters(typeof(IAuthenticationFilter));
if (filters.Count() > 0)
{
foreach (IAuthenticationFilter item in filters)
{
var result = item.Authentication(server, requet);
if (!result)
{
client.SendCmd(new Message<LogMassage>
{
MessageType = MessageType.Log,
Content = new LogMassage(LogMsgType.Error, "认证失败")
});
return;
}
}
}
var sb = new StringBuilder($"{Environment.NewLine}=====隧道已建立成功,可通过以下方式访问内网服务====={Environment.NewLine}{Environment.NewLine}");
sb.Append($"穿透协议 | 映射关系(公网=>内网){Environment.NewLine}");
if (requet.Webs != null && requet.Webs.Count() > 0)
@ -114,11 +79,11 @@ namespace FastTunnel.Core.Handlers
{
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.BindPort))
//{
// _logger.LogError($"RemotePort can not be same with BindPort: {item.RemotePort}");
// continue;
//}
if (item.RemotePort.Equals(server.serverOption.CurrentValue.WebProxyPort))
{
@ -126,20 +91,20 @@ namespace FastTunnel.Core.Handlers
continue;
}
SSHInfo<SSHHandlerArg> old;
ForwardInfo<ForwardHandlerArg> old;
if (server.SSHList.TryGetValue(item.RemotePort, out old))
{
_logger.LogDebug($"Remove Listener {old.Listener.ListenIp}:{old.Listener.ListenPort}");
old.Listener.Stop();
server.SSHList.TryRemove(item.RemotePort, out SSHInfo<SSHHandlerArg> _);
server.SSHList.TryRemove(item.RemotePort, out ForwardInfo<ForwardHandlerArg> _);
}
var ls = new PortProxyListener("0.0.0.0", item.RemotePort, _logger);
ls.Start(new SSHDispatcher(server, client, item));
ls.Start(new ForwardDispatcher(server, client, item));
// listen success
server.SSHList.TryAdd(item.RemotePort, new SSHInfo<SSHHandlerArg> { Listener = ls, Socket = client, SSHConfig = item });
server.SSHList.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}");
@ -149,7 +114,7 @@ namespace FastTunnel.Core.Handlers
{
_logger.LogError($"SSH proxy error: {item.RemotePort} => {item.LocalIp}:{item.LocalPort}");
_logger.LogError(ex.Message);
client.SendCmd(new Message<LogMassage> { MessageType = MessageType.Log, Content = new LogMassage(LogMsgType.Info, ex.Message) });
await client.SendCmdAsync(new Message<LogMassage> { MessageType = MessageType.Log, Content = new LogMassage(LogMsgType.Info, ex.Message) });
continue;
}
}
@ -157,13 +122,20 @@ namespace FastTunnel.Core.Handlers
if (!hasTunnel)
{
client.SendCmd(new Message<LogMassage> { MessageType = MessageType.Log, Content = new LogMassage(LogMsgType.Info, TunnelResource.NoTunnel) });
await client.SendCmdAsync(new Message<LogMassage> { MessageType = MessageType.Log, Content = new LogMassage(LogMsgType.Info, TunnelResource.NoTunnel) });
}
else
{
sb.Append($"{Environment.NewLine}====================================================");
client.SendCmd(new Message<LogMassage> { MessageType = MessageType.Log, Content = new LogMassage(LogMsgType.Info, sb.ToString()) });
await client.SendCmdAsync(new Message<LogMassage> { MessageType = MessageType.Log, Content = new LogMassage(LogMsgType.Info, sb.ToString()) });
}
}
public async Task<bool> HandlerMsg<T>(FastTunnelServer server, WebSocket client, T msg)
where T : TunnelMassage
{
await HandleLoginAsync(server, client, msg as LogInMassage);
return NeedRecive;
}
}
}

View File

@ -8,6 +8,7 @@ using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading.Tasks;
@ -38,7 +39,6 @@ namespace FastTunnel.Core.Handlers.Server
server.ResponseTasks.TryRemove(SwapMsg.msgId, out _);
_logger.LogDebug($"SwapMassage{SwapMsg.msgId}");
response.SetResult(new NetworkStream(client, true));
}
else
@ -59,5 +59,10 @@ namespace FastTunnel.Core.Handlers.Server
client.Close();
}
}
public Task<bool> HandlerMsg(FastTunnelServer server, WebSocket client, Message<JObject> msg)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core
{
public class HeaderConst
{
public const string FASTTUNNEL_FLAG = "FASTTUNNEL_VERSION";
public const string FASTTUNNEL_TYPE = "FASTTUNNEL_TYPE";
public const string FASTTUNNEL_MSGID = "FASTTUNNEL_MSGID";
public const string TYPE_CLIENT = "CLIENT";
public const string TYPE_SWAP = "SWAP";
}
}

View File

@ -43,7 +43,7 @@ namespace FastTunnel.Core.Listener
_heartHandler = new HeartMessageHandler();
_swapMsgHandler = new SwapMessageHandler(_logger);
server = new Server.Server(2000, 100, false, _logger);
server = new Server.Server(10000, 100, false, _logger);
}
public void Start()

View File

@ -108,7 +108,7 @@ namespace FastTunnel.Core.Listener
try
{
// 将此客户端交由Dispatcher进行管理
_requestDispatcher.Dispatch(accept);
_requestDispatcher.DispatchAsync(accept);
}
catch (Exception ex)
{

View File

@ -4,7 +4,7 @@ using System.Text;
namespace FastTunnel.Core.Models
{
public class SSHConfig
public class ForwardConfig
{
/// <summary>
/// 局域网IP地址

View File

@ -5,9 +5,9 @@ using System.Text;
namespace FastTunnel.Core.Models
{
public class SSHHandlerArg
public class ForwardHandlerArg
{
public SSHConfig SSHConfig { get; internal set; }
public ForwardConfig SSHConfig { get; internal set; }
public Socket LocalClient { get; internal set; }
}

View File

@ -2,15 +2,16 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
namespace FastTunnel.Core.Models
{
public class SSHInfo<T>
public class ForwardInfo<T>
{
public Socket Socket { get; set; }
public WebSocket Socket { get; set; }
public SSHConfig SSHConfig { get; set; }
public ForwardConfig SSHConfig { get; set; }
public IListener Listener { get; set; }
}

View File

@ -6,5 +6,6 @@ namespace FastTunnel.Core.Models
{
public class HeartMassage : TunnelMassage
{
public string Time { get; set; }
}
}

View File

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

View File

@ -4,10 +4,10 @@ using System.Text;
namespace FastTunnel.Core.Models
{
public class NewSSHRequest : TunnelMassage
public class NewForwardMessage : TunnelMassage
{
public string MsgId { get; set; }
public SSHConfig SSHConfig { get; set; }
public ForwardConfig SSHConfig { get; set; }
}
}

View File

@ -1,11 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace FastTunnel.Core.Models
{
public enum Protocol
{
TCP = 0,
}
}

View File

@ -0,0 +1,90 @@
using FastTunnel.Core.Client;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Handlers;
using FastTunnel.Core.Handlers.Server;
using FastTunnel.Core.Protocol;
using Microsoft.Extensions.Logging;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Yarp.ReverseProxy.Configuration;
namespace FastTunnel.Core.Models
{
public class TunnelClient
{
readonly LoginHandler _loginHandler;
readonly HeartMessageHandler _heartHandler;
//readonly SwapMessageHandler _swapMsgHandler;
FastTunnelServer fastTunnelServer;
ILogger logger;
WebSocket webSocket;
public TunnelClient(ILogger logger, WebSocket webSocket, FastTunnelServer fastTunnelServer)
{
this.webSocket = webSocket;
this.logger = logger;
this.fastTunnelServer = fastTunnelServer;
this._loginHandler = new LoginHandler(logger, fastTunnelServer.proxyConfig);
this._heartHandler = new HeartMessageHandler();
// this._swapMsgHandler = new SwapMessageHandler(logger);
}
public async Task ReviceAsync()
{
var buffer = new byte[512];
var tunnelProtocol = new TunnelProtocol();
while (true)
{
var res = await webSocket.ReceiveAsync(buffer, CancellationToken.None);
var cmds = tunnelProtocol.HandleBuffer(buffer, 0, res.Count);
foreach (var item in cmds)
{
if (!await HandleCmdAsync(webSocket, item))
{
return;
};
}
}
}
private async Task<bool> HandleCmdAsync(WebSocket webSocket, string lineCmd)
{
try
{
logger.LogInformation($"client{lineCmd}");
var cmds = lineCmd.Split("||");
var type = cmds[0];
TunnelMassage msg = null;
IClientMessageHandler handler = null;
switch (type)
{
case "C_LogIn": // 登录
handler = _loginHandler;
msg = JsonSerializer.Deserialize<LogInMassage>(cmds[1]);
break;
case "Heart": // 心跳
handler = _heartHandler;
break;
default:
throw new Exception($"未知的通讯指令 {lineCmd}");
}
return await handler.HandlerMsg(fastTunnelServer, webSocket, msg);
}
catch (Exception ex)
{
logger.LogError(ex, $"处理客户端消息失败cmd={lineCmd}");
return false;
}
}
}
}

View File

@ -1,13 +1,15 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
namespace FastTunnel.Core.Models
{
public class WebInfo
{
public Socket Socket { get; set; }
public WebSocket Socket { get; set; }
public WebConfig WebConfig { get; set; }
}
}

View File

@ -0,0 +1,40 @@
using FastTunnel.Core.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Protocol
{
public class TunnelProtocol
{
string massgeTemp;
string m_sectionFlag = "\n";
public IEnumerable<string> HandleBuffer(byte[] buffer, int offset, int count)
{
var words = buffer.GetString(offset, count);
var sum = massgeTemp + words;
if (sum.Contains(m_sectionFlag))
{
var array = (sum).Split(m_sectionFlag);
massgeTemp = null;
var fullMsg = words.EndsWith(m_sectionFlag);
if (!fullMsg)
{
massgeTemp = array[array.Length - 1];
}
return array.Take(array.Length - 1);
}
else
{
massgeTemp = sum;
return null;
}
}
}
}

View File

@ -7,7 +7,6 @@ using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Runtime.ExceptionServices;
using System.IO;
@ -31,7 +30,7 @@ namespace FastTunnel.Core.Services
{
return Task.Run(() =>
{
_fastTunnelClient.Start();
_fastTunnelClient.StartAsync(cancellationToken);
}, cancellationToken);
}
@ -46,7 +45,6 @@ namespace FastTunnel.Core.Services
try
{
_logger.LogError("【UnhandledException】" + e.ExceptionObject);
_logger.LogError("【UnhandledException】" + JsonConvert.SerializeObject(e.ExceptionObject));
var type = e.ExceptionObject.GetType();
_logger.LogError("ExceptionObject GetType " + type);
}

View File

@ -5,7 +5,6 @@ using FastTunnel.Core.Global;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
@ -40,7 +39,6 @@ namespace FastTunnel.Core.Services
try
{
_logger.LogError("【UnhandledException】" + e.ExceptionObject);
_logger.LogError("【UnhandledException】" + JsonConvert.SerializeObject(e.ExceptionObject));
var type = e.ExceptionObject.GetType();
_logger.LogError("ExceptionObject GetType " + type);
}

View File

@ -0,0 +1,51 @@
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Sockets
{
public class DefultClientSocket : IFastTunnelClientSocket
{
ClientWebSocket webSocket;
public DefultClientSocket()
{
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_CLIENT);
}
public async Task ConnectAsync(Uri url, CancellationToken cancellationToken)
{
await webSocket.ConnectAsync(url, cancellationToken);
}
public async Task CloseAsync()
{
if (webSocket.State == WebSocketState.Closed)
return;
await webSocket.CloseAsync(WebSocketCloseStatus.Empty, null, CancellationToken.None);
}
public async Task<int> ReceiveAsync(byte[] buffer, CancellationToken cancellationToken)
{
var res = await webSocket.ReceiveAsync(buffer, cancellationToken);
return res.Count;
}
public async Task SendAsync<T>(Message<T> msg, CancellationToken cancellationToken)
where T : TunnelMassage
{
await webSocket.SendCmdAsync(msg, WebSocketMessageType.Binary, false, cancellationToken);
}
}
}

View File

@ -0,0 +1,22 @@
using FastTunnel.Core.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FastTunnel.Core.Sockets
{
public interface IFastTunnelClientSocket
{
Task<int> ReceiveAsync(byte[] buffer, CancellationToken cancellationToken);
Task SendAsync<T>(Message<T> loginMsg, CancellationToken cancellationToken)
where T : TunnelMassage;
Task ConnectAsync(Uri url, CancellationToken cancellationToken);
Task CloseAsync();
}
}

View File

@ -0,0 +1,99 @@
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Channels;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Sockets;
using FastTunnel.Core.Forwarder;
namespace FastTunnel.Core.Sockets
{
public class ReadWriteStreamSwap
{
IReadWriteStream stream;
IReadWriteStream stream1;
ILogger logger;
string msgId;
public ReadWriteStreamSwap(IReadWriteStream stream, IReadWriteStream stream1, ILogger logger, string msgId)
{
this.stream = stream;
this.stream1 = stream1;
this.logger = logger;
this.msgId = msgId;
}
public async Task StartSwapAsync()
{
logger.LogDebug($"[StartSwapStart] {msgId}");
var task = new Task(() =>
{
work(stream, stream1);
});
var task1 = new Task(() =>
{
work(stream1, stream);
});
await Task.WhenAll(task1, task);
logger.LogDebug($"[StartSwapEnd] {msgId}");
}
private void work(IReadWriteStream streamRevice, IReadWriteStream streamSend)
{
byte[] buffer = new byte[512];
while (true)
{
int num;
try
{
try
{
num = streamRevice.Read(buffer);
Console.WriteLine($"{Encoding.UTF8.GetString(buffer, 0, num)}");
}
catch (Exception)
{
close("Revice Fail");
break;
}
if (num == 0)
{
close("Normal Close");
break;
}
try
{
streamSend.Write(buffer, 0, num);
}
catch (Exception)
{
close("Send Fail");
break;
}
}
catch (Exception ex)
{
logger.LogCritical(ex, "致命异常");
break;
}
}
}
private void close(string msg)
{
logger.LogError($"Sarp Error {msg}");
}
}
}

View File

@ -2,6 +2,7 @@
using FastTunnel.Core.Utility.Extensions;
using Microsoft.Extensions.Logging;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Sockets;

View File

@ -0,0 +1,98 @@
using FastTunnel.Core.Handlers.Client;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace FastTunnel.Core.Sockets
{
public class StreamSwap
{
private Stream stream1;
private Stream stream2;
private ILogger<HttpRequestHandler> logger;
private string msgId;
public StreamSwap(Stream serverConnection, NetworkStream localConn, ILogger<HttpRequestHandler> logger, string msgId)
{
this.stream1 = serverConnection;
this.stream2 = localConn;
this.logger = logger;
this.msgId = msgId;
}
public async Task StartSwapAsync()
{
logger.LogDebug($"[StartSwapStart] {msgId}");
var task = new Task(() =>
{
work(stream1, stream2);
});
var task1 = new Task(() =>
{
work(stream2, stream1);
});
await Task.WhenAll(task1, task);
logger.LogDebug($"[StartSwapEnd] {msgId}");
}
private void work(Stream streamRevice, Stream streamSend)
{
byte[] buffer = new byte[512];
while (true)
{
int num;
try
{
try
{
num = streamRevice.Read(buffer);
Console.WriteLine($"{Encoding.UTF8.GetString(buffer, 0, num)}");
}
catch (Exception)
{
close("Revice Fail");
break;
}
if (num == 0)
{
close("Normal Close");
break;
}
try
{
streamSend.Write(buffer, 0, num);
}
catch (Exception)
{
close("Send Fail");
break;
}
}
catch (Exception ex)
{
logger.LogCritical(ex, "致命异常");
break;
}
}
}
private void close(string msg)
{
logger.LogError($"Sarp Error {msg}");
}
}
}

View File

@ -1,11 +0,0 @@
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<div style="text-align:center;">Coming soon!</div>
</body>
</html>

View File

@ -1,12 +0,0 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "3.1.9",
"commands": [
"dotnet-ef"
]
}
}
}

View File

@ -21,10 +21,10 @@
</ItemGroup>
<ItemGroup>
<None Update="卸载服务.bat">
<None Update="uninstall.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="安装服务.bat">
<None Update="install.bat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

View File

@ -2,6 +2,8 @@ using System;
using System.Collections.Generic;
using FastTunnel.Core.Extensions;
using FastTunnel.Core.Forwarder;
using FastTunnel.Core.Forwarder.MiddleWare;
using FastTunnel.Core.MiddleWares;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -35,6 +37,8 @@ namespace FastTunnel.Server
// ------------------------Custom Business------------------------------
services.AddSingleton<IForwarderHttpClientFactory, FastTunnelForwarderHttpClientFactory>();
services.AddSingleton<FastTunnelClientHandler, FastTunnelClientHandler>();
services.AddSingleton<FastTunnelSwapHandler, FastTunnelSwapHandler>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -44,6 +48,13 @@ namespace FastTunnel.Server
{
}
app.UseWebSockets();
var swapHandler = app.ApplicationServices.GetRequiredService<FastTunnelSwapHandler>();
var clientHandler = app.ApplicationServices.GetRequiredService<FastTunnelClientHandler>();
app.Use(clientHandler.Handle);
app.Use(swapHandler.Handle);
app.UseRouting();
app.UseEndpoints(endpoints =>

View File

@ -1,4 +1,5 @@
{
"urls": "http://*:1270;", // Http¼àÌýË¿Ú
"Logging": {
"LogLevel": {
// Trace Debug Information Warning Error
@ -10,14 +11,11 @@
"AllowedHosts": "*",
"ServerSettings": {
//
"BindPort": 1271,
//"BindPort": 1271,
//
"WebDomain": "test.cc",
// Http, 访url http://{SubDomain}.{WebDomain}:{WebProxyPort}/
"WebProxyPort": 1270,
// ngixn访
"WebHasNginxProxy": false,
@ -25,6 +23,6 @@
"WebAllowAccessIps": [],
// SSHSSH.false
"SSHEnabled": true
"EnableForward": true
}
}