公交车上荫蒂添的好舒服的电影-公用玩物(np双xing总受)-公用小荡货芊芊-公与妇仑乱hd-攻把受做哭边走边肉楼梯play-古装一级淫片a免费播放口

LOGO OA教程 ERP教程 模切知識交流 PMS教程 CRM教程 開發文檔 其他文檔  
 
網站管理員

如何使用 websocket 完成 socks5 網絡穿透

freeflydom
2025年5月8日 8:21 本文熱度 194

有盆友好奇所謂的網絡穿透是怎么做的

然后talk is cheap,please show code

所以只好寫個簡單且常見的websocket例子,

這里的例子大致是這個原理

瀏覽器插件(或者其他)首先將正常訪問請求 --> 轉換為socks5訪問 --> 假代理服務器建立websocket鏈接,然后傳輸socks5協議數據 --> 允許websocket的網關由于不解析websocket數據而不知道是socks5所以未做攔截 --> 真代理服務器從websocket中解析socks5進行轉發處理

代碼如下

Socks5 --> websocket 端

internal class Socks5ToWSMiddleware : ITcpProxyMiddleware
{
    private readonly IForwarderHttpClientFactory httpClientFactory;
    private readonly ILoadBalancingPolicyFactory loadBalancing;
    private readonly ProxyLogger logger;
    private readonly TimeProvider timeProvider;
    public Socks5ToWSMiddleware(IForwarderHttpClientFactory httpClientFactory, ILoadBalancingPolicyFactory loadBalancing, ProxyLogger logger, TimeProvider timeProvider)
    {
        this.httpClientFactory = httpClientFactory;
        this.loadBalancing = loadBalancing;
        this.logger = logger;
        this.timeProvider = timeProvider;
    }
    public Task InitAsync(ConnectionContext context, CancellationToken token, TcpDelegate next)
    {
        // 過濾符合的路由配置
        var feature = context.Features.Get<IL4ReverseProxyFeature>();
        if (feature is not null)
        {
            var route = feature.Route;
            if (route is not null && route.Metadata is not null
                && route.Metadata.TryGetValue("socks5ToWS", out var b) && bool.TryParse(b, out var isSocks5) && isSocks5)
            {
                feature.IsDone = true;
                route.ClusterConfig?.InitHttp(httpClientFactory);
                return Proxy(context, feature, token);
            }
        }
        return next(context, token);
    }
    private async Task Proxy(ConnectionContext context, IL4ReverseProxyFeature feature, CancellationToken token)
    { // loadBalancing 選取有效 ip
        var route = feature.Route;
        var cluster = route.ClusterConfig;
        DestinationState selectedDestination;
        if (cluster is null)
        {
            selectedDestination = null;
        }
        else
        {
            selectedDestination = feature.SelectedDestination;
            selectedDestination ??= loadBalancing.PickDestination(feature);
        }
        if (selectedDestination is null)
        {
            logger.NotFoundAvailableUpstream(route.ClusterId);
            Abort(context);
            return;
        }
        selectedDestination.ConcurrencyCounter.Increment();
        try
        {
            await SendAsync(context, feature, selectedDestination, cluster, route.Transformer, token);
            selectedDestination.ReportSuccessed();
        }
        catch
        {
            selectedDestination.ReportFailed();
            throw;
        }
        finally
        {
            selectedDestination.ConcurrencyCounter.Decrement();
        }
    }
    private async Task<ForwarderError> SendAsync(ConnectionContext context, IL4ReverseProxyFeature feature, DestinationState selectedDestination, ClusterConfig? cluster, IHttpTransformer transformer, CancellationToken token)
    {
        // 創建 websocket 請求, 這里為了簡單,只創建簡單 http1.1 websocket 
        var destinationPrefix = selectedDestination.Address;
        if (destinationPrefix is null || destinationPrefix.Length < 8)
        {
            throw new ArgumentException("Invalid destination prefix.", nameof(destinationPrefix));
        }
        var route = feature.Route;
        var requestConfig = cluster.HttpRequest ?? ForwarderRequestConfig.Empty;
        var httpClient = cluster.HttpMessageHandler ?? throw new ArgumentNullException("httpClient");
        var destinationRequest = new HttpRequestMessage();
        destinationRequest.Version = HttpVersion.Version11;
        destinationRequest.VersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
        destinationRequest.Method = HttpMethod.Get;
        destinationRequest.RequestUri ??= new Uri(destinationPrefix, UriKind.Absolute);
        destinationRequest.Headers.TryAddWithoutValidation(HeaderNames.Connection, HeaderNames.Upgrade);
        destinationRequest.Headers.TryAddWithoutValidation(HeaderNames.Upgrade, HttpForwarder.WebSocketName);
        destinationRequest.Headers.TryAddWithoutValidation(HeaderNames.SecWebSocketVersion, "13");
        destinationRequest.Headers.TryAddWithoutValidation(HeaderNames.SecWebSocketKey, ProtocolHelper.CreateSecWebSocketKey());
        destinationRequest.Content = new EmptyHttpContent();
        if (!string.IsNullOrWhiteSpace(selectedDestination.Host))
        {
            destinationRequest.Headers.TryAddWithoutValidation(HeaderNames.Host, selectedDestination.Host);
        }
        
        // 建立websocket 鏈接,成功則直接 復制原始 req/resp 數據,不做任何而外處理
        var destinationResponse = await httpClient.SendAsync(destinationRequest, token);
        if (destinationResponse.StatusCode == HttpStatusCode.SwitchingProtocols)
        {
            using var destinationStream = await destinationResponse.Content.ReadAsStreamAsync(token);
            var clientStream = new DuplexPipeStreamAdapter<Stream>(null, context.Transport, static i => i);
            var activityCancellationSource = ActivityCancellationTokenSource.Rent(route.Timeout);
            var requestTask = StreamCopier.CopyAsync(isRequest: true, clientStream, destinationStream, StreamCopier.UnknownLength, timeProvider, activityCancellationSource,
                autoFlush: destinationResponse.Version == HttpVersion.Version20, token).AsTask();
            var responseTask = StreamCopier.CopyAsync(isRequest: false, destinationStream, clientStream, StreamCopier.UnknownLength, timeProvider, activityCancellationSource, token).AsTask();
            var task = await Task.WhenAny(requestTask, responseTask);
            await clientStream.DisposeAsync();
            if (task.IsCanceled)
            {
                Abort(context);
                activityCancellationSource.Cancel();
                if (task.Exception is not null)
                {
                    throw task.Exception;
                }
            }
        }
        else
        {
            Abort(context);
            return ForwarderError.UpgradeRequestDestination;
        }
        return ForwarderError.None;
    }
    public Task<ReadOnlyMemory<byte>> OnRequestAsync(ConnectionContext context, ReadOnlyMemory<byte> source, CancellationToken token, TcpProxyDelegate next)
    {
        return next(context, source, token);
    }
    public Task<ReadOnlyMemory<byte>> OnResponseAsync(ConnectionContext context, ReadOnlyMemory<byte> source, CancellationToken token, TcpProxyDelegate next)
    {
        return next(context, source, token);
    }
    private static void Abort(ConnectionContext upstream)
    {
        upstream.Transport.Input.CancelPendingRead();
        upstream.Transport.Output.CancelPendingFlush();
        upstream.Abort();
    }
}

websocket --> Socks5 端

internal class WSToSocks5HttpMiddleware : IMiddleware
{
    private static ReadOnlySpan<byte> EncodedWebSocketKey => "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"u8;
    private WebSocketMiddleware middleware;
    private readonly Socks5Middleware socks5Middleware;
    public WSToSocks5HttpMiddleware(IOptions<WebSocketOptions> options, ILoggerFactory loggerFactory, Socks5Middleware socks5Middleware)
    {
        middleware = new WebSocketMiddleware(Scoks5, options, loggerFactory);
        this.socks5Middleware = socks5Middleware;
    }
    private async Task Scoks5(HttpContext context)
    {
        var upgradeFeature = context.Features.Get<IHttpUpgradeFeature>();
        // 檢查是否未正確 websocket 請求
        var f = context.Features.Get<IHttpWebSocketFeature>();
        if (f.IsWebSocketRequest)
        {
            // 返回 websocket 接受信息
            var responseHeaders = context.Response.Headers;
            responseHeaders.Connection = HeaderNames.Upgrade;
            responseHeaders.Upgrade = HttpForwarder.WebSocketName;
            responseHeaders.SecWebSocketAccept = CreateResponseKey(context.Request.Headers.SecWebSocketKey.ToString());
            var stream = await upgradeFeature!.UpgradeAsync(); // Sets status code to 101
            
            // 建原始 websocket stream 包裝成 pipe 方便使用原來的 socks5Middleware 實現
            var memoryPool = context is IMemoryPoolFeature s ? s.MemoryPool : MemoryPool<byte>.Shared;
            StreamPipeReaderOptions readerOptions = new StreamPipeReaderOptions
            (
                pool: memoryPool,
                bufferSize: memoryPool.GetMinimumSegmentSize(),
                minimumReadSize: memoryPool.GetMinimumAllocSize(),
                leaveOpen: true,
                useZeroByteReads: true
            );
            var writerOptions = new StreamPipeWriterOptions
            (
                pool: memoryPool,
                leaveOpen: true
            );
            var input = PipeReader.Create(stream, readerOptions);
            var output = PipeWriter.Create(stream, writerOptions);
            var feature = context.Features.Get<IReverseProxyFeature>();
            var route = feature.Route;
            using var cts = CancellationTokenSourcePool.Default.Rent(route.Timeout);
            var token = cts.Token;
            context.Features.Set<IL4ReverseProxyFeature>(new L4ReverseProxyFeature() { IsDone = true, Route = route });
            // socks5Middleware 進行轉發
            await socks5Middleware.Proxy(new WebSocketConnection(context.Features)
            {
                Transport = new WebSocketDuplexPipe() { Input = input, Output = output },
                ConnectionId = context.Connection.Id,
                Items = context.Items,
            }, null, token);
        }
        else
        {
            context.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }
    public static string CreateResponseKey(string requestKey)
    {
        // "The value of this header field is constructed by concatenating /key/, defined above in step 4
        // in Section 4.2.2, with the string "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of
        // this concatenated value to obtain a 20-byte value and base64-encoding"
        // https://tools.ietf.org/html/rfc6455#section-4.2.2
        // requestKey is already verified to be small (24 bytes) by 'IsRequestKeyValid()' and everything is 1:1 mapping to UTF8 bytes
        // so this can be hardcoded to 60 bytes for the requestKey + static websocket string
        Span<byte> mergedBytes = stackalloc byte[60];
        Encoding.UTF8.GetBytes(requestKey, mergedBytes);
        EncodedWebSocketKey.CopyTo(mergedBytes[24..]);
        Span<byte> hashedBytes = stackalloc byte[20];
        var written = SHA1.HashData(mergedBytes, hashedBytes);
        if (written != 20)
        {
            throw new InvalidOperationException("Could not compute the hash for the 'Sec-WebSocket-Accept' header.");
        }
        return Convert.ToBase64String(hashedBytes);
    }
    public Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
       // 過濾符合的路由配置
        var feature = context.Features.Get<IReverseProxyFeature>();
        if (feature is not null)
        {
            var route = feature.Route;
            if (route is not null && route.Metadata is not null
                && route.Metadata.TryGetValue("WSToSocks5", out var b) && bool.TryParse(b, out var isSocks5) && isSocks5)
            {
                // 這里偷個懶,利用現成的 WebSocketMiddleware 檢查 websocket 請求,
                return middleware.Invoke(context);
            }
        }
        return next(context);
    }
}
internal class WebSocketConnection : ConnectionContext
{
    public WebSocketConnection(IFeatureCollection features)
    {
        this.features = features;
    }
    public override IDuplexPipe Transport { get; set; }
    public override string ConnectionId { get; set; }
    private IFeatureCollection features;
    public override IFeatureCollection Features => features;
    public override IDictionary<object, object?> Items { get; set; }
}
internal class WebSocketDuplexPipe : IDuplexPipe
{
    public PipeReader Input { get; set; }
    public PipeWriter Output { get; set; }
}
C# 折疊 復制 全屏

所以利用 websocket 偽裝的例子大致就是這樣就可以完成 tcp的 socks5 處理了 udp我就不來了
最后有興趣的同學給 L4/L7的代理 VKProxy點個贊唄 (暫時沒有使用文檔,等啥時候有空把配置ui站點完成了再來吧)

轉自https://www.cnblogs.com/fs7744/p/18863856


該文章在 2025/5/8 8:21:47 編輯過
關鍵字查詢
相關文章
正在查詢...
點晴ERP是一款針對中小制造業的專業生產管理軟件系統,系統成熟度和易用性得到了國內大量中小企業的青睞。
點晴PMS碼頭管理系統主要針對港口碼頭集裝箱與散貨日常運作、調度、堆場、車隊、財務費用、相關報表等業務管理,結合碼頭的業務特點,圍繞調度、堆場作業而開發的。集技術的先進性、管理的有效性于一體,是物流碼頭及其他港口類企業的高效ERP管理信息系統。
點晴WMS倉儲管理系統提供了貨物產品管理,銷售管理,采購管理,倉儲管理,倉庫管理,保質期管理,貨位管理,庫位管理,生產管理,WMS管理系統,標簽打印,條形碼,二維碼管理,批號管理軟件。
點晴免費OA是一款軟件和通用服務都免費,不限功能、不限時間、不限用戶的免費OA協同辦公管理系統。
Copyright 2010-2025 ClickSun All Rights Reserved

主站蜘蛛池模板: 国产精品成人午夜电影 | 国产三级电影在线 | 高清精品视频一区二区三区 | av资源站最稳定的资源站 | 成人国产一区二区精品小说 | 成人午夜动漫在线观看 | 按摩已婚人妻精品中文字幕 | 国产精品视频观看 | 国产成在线观看免费视频成本人 | a级毛片免费播放无码 | 国产自慰在线免费观看 | 国产亚洲日韩网爆欧美 | 韩国三级无码不卡在线观看 | 国产欧美精品综合一区 | 国产一级毛片一级毛片视频 | 国产人妖高清一区二区 | 国产精品成人va在线观看软件 | 国产精品成人观看视频国产奇米 | 国产精品日日摸夜夜添夜夜 | 国产午夜在线视频 | 国产精品无码国产字幕av | 国产精品精品国产一区二区 | 国产成人精品免费视频软件 | 国内自拍天天爱 | 国产极品熟女沙发内射av | 精品无码在线播放国产 | 国产在线观看精品 | 精品国产高清久久久久久小说 | 国产熟女高潮精选视频 | 国产精品福利一区 | 国产午夜精华2025在线 | 爆乳女教师高清bd | 国产原创大胆私拍视频 | 国产精品白嫩美女 | 成人国内精品久久久久影院 | 国产在线永久免费 | 国产一区二区三区鲁婷婷 | 精品国产99久久久久久 | 国产午夜福利电影免费在线观看 | 国产爆初菊在线观看免费视频 | 成人精品在线免费观看 |