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();
});