From 9da7f18b9d838db521a3230b4c9296cad5153854 Mon Sep 17 00:00:00 2001 From: xljiulang <366193849@qq.com> Date: Fri, 13 Aug 2021 20:50:03 +0800 Subject: [PATCH] =?UTF-8?q?github=20ssh=E8=BD=AC=E5=8F=91=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FastGithub.ReverseProxy/GitubSshHandler.cs | 136 ++++++++++++++++++ .../KestrelServerOptionsExtensions.cs | 25 ++++ FastGithub/Program.cs | 1 + 3 files changed, 162 insertions(+) create mode 100644 FastGithub.ReverseProxy/GitubSshHandler.cs diff --git a/FastGithub.ReverseProxy/GitubSshHandler.cs b/FastGithub.ReverseProxy/GitubSshHandler.cs new file mode 100644 index 0000000..35c01c4 --- /dev/null +++ b/FastGithub.ReverseProxy/GitubSshHandler.cs @@ -0,0 +1,136 @@ +using FastGithub.DomainResolve; +using Microsoft.AspNetCore.Connections; +using System; +using System.IO; +using System.IO.Pipelines; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace FastGithub.ReverseProxy +{ + /// + /// github的ssl处理者 + /// + sealed class GitubSshHandler : ConnectionHandler + { + private const int SSH_PORT = 22; + private const string GITHUB_COM = "github.com"; + private readonly IDomainResolver domainResolver; + + /// + /// github的ssl处理者 + /// + /// + public GitubSshHandler(IDomainResolver domainResolver) + { + this.domainResolver = domainResolver; + } + + /// + /// ssh连接后 + /// + /// + /// + public override async Task OnConnectedAsync(ConnectionContext connection) + { + var address = await this.domainResolver.ResolveAsync(GITHUB_COM, CancellationToken.None); + var socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp); + await socket.ConnectAsync(new IPEndPoint(address, SSH_PORT)); + + using var upStream = new NetworkStream(socket, ownsSocket: true); + var downStream = new SshStream(connection.Transport); + + var task1 = upStream.CopyToAsync(downStream); + var task2 = downStream.CopyToAsync(upStream); + await Task.WhenAny(task1, task2); + } + + /// + /// 表示Ssh的流 + /// + private class SshStream : Stream + { + private readonly Stream readStream; + private readonly Stream wirteStream; + + /// + /// Ssh的流 + /// + /// + public SshStream(IDuplexPipe transport) + { + this.readStream = transport.Input.AsStream(); + this.wirteStream = transport.Output.AsStream(); + } + + 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 ReadAsync(Memory buffer, CancellationToken cancellationToken = default) + { + return this.readStream.ReadAsync(buffer, cancellationToken); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return this.readStream.ReadAsync(buffer, offset, count, cancellationToken); + } + + public override void Write(ReadOnlySpan 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 buffer, CancellationToken cancellationToken = default) + { + await this.wirteStream.WriteAsync(buffer, cancellationToken); + } + } + } +} diff --git a/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs b/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs index 353fc02..091c260 100644 --- a/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs +++ b/FastGithub.ReverseProxy/KestrelServerOptionsExtensions.cs @@ -1,5 +1,6 @@ using FastGithub.Configuration; using FastGithub.ReverseProxy; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.DependencyInjection; @@ -69,5 +70,29 @@ namespace FastGithub https.ServerCertificateSelector = (ctx, domain) => certService.GetOrCreateServerCert(domain); })); } + + /// + /// 监听github的ssh的代理 + /// + /// + public static void ListenGithubSshProxy(this KestrelServerOptions kestrel) + { + const int SSH_PORT = 22; + if (OperatingSystem.IsWindows()) + { + TcpTable.KillPortOwner(SSH_PORT); + } + + if (LocalMachine.CanListenTcp(SSH_PORT) == false) + { + var loggerFactory = kestrel.ApplicationServices.GetRequiredService(); + var logger = loggerFactory.CreateLogger($"{nameof(FastGithub)}.{nameof(ReverseProxy)}"); + logger.LogWarning($"由于tcp端口{SSH_PORT}已经被其它进程占用,github的ssh代理功能将受限"); + } + else + { + kestrel.Listen(IPAddress.Any, SSH_PORT, listen => listen.UseConnectionHandler()); + } + } } } diff --git a/FastGithub/Program.cs b/FastGithub/Program.cs index 19ce9d4..51548d2 100644 --- a/FastGithub/Program.cs +++ b/FastGithub/Program.cs @@ -49,6 +49,7 @@ namespace FastGithub webBuilder.UseKestrel(kestrel => { kestrel.Limits.MaxRequestBodySize = null; + kestrel.ListenGithubSshProxy(); kestrel.ListenHttpReverseProxy(); kestrel.ListenHttpsReverseProxy(); });