Обновить Program.cs

This commit is contained in:
2026-04-09 18:32:01 +02:00
parent 3f34f06cd5
commit 2d8f3d07e1

View File

@@ -63,6 +63,7 @@ class Program
static async Task HandleHttp(NetworkStream clientStream, HttpRequestData req) static async Task HandleHttp(NetworkStream clientStream, HttpRequestData req)
{ {
DefaultProxyCredentials = CredentialCache.DefaultCredentials
var handler = new SocketsHttpHandler var handler = new SocketsHttpHandler
{ {
UseProxy = true, UseProxy = true,
@@ -125,22 +126,29 @@ class Program
var upstreamStream = upstream.GetStream(); var upstreamStream = upstream.GetStream();
// CONNECT к upstream // CONNECT к upstream proxy
string connectReq = string connectReq =
$"CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n\r\n"; $"CONNECT {host}:{port} HTTP/1.1\r\n" +
$"Host: {host}:{port}\r\n" +
$"Proxy-Connection: Keep-Alive\r\n\r\n";
await upstreamStream.WriteAsync(Encoding.ASCII.GetBytes(connectReq)); await upstreamStream.WriteAsync(Encoding.ASCII.GetBytes(connectReq));
// читаем ответ от upstream // читаем ответ upstream полностью до \r\n\r\n
var buffer = new byte[8192]; var resp = await ReadHeaders(upstreamStream);
int read = await upstreamStream.ReadAsync(buffer);
// отвечаем клиенту что туннель открыт if (!resp.HeadersRaw.Contains("200"))
{
await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 502 Bad Gateway\r\n\r\n"));
return;
}
// говорим клиенту ОК
await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n")); await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n"));
// двунаправленный туннель // туннель
var t1 = upstreamStream.CopyToAsync(clientStream); var t1 = Pump(upstreamStream, clientStream);
var t2 = clientStream.CopyToAsync(upstreamStream); var t2 = Pump(clientStream, upstreamStream);
await Task.WhenAny(t1, t2); await Task.WhenAny(t1, t2);
} }
@@ -233,80 +241,96 @@ class WinHttpProxy : IWebProxy
public bool IsBypassed(Uri host) => false; public bool IsBypassed(Uri host) => false;
} }
using System.Runtime.InteropServices;
static class WinHttpHelper static class WinHttpHelper
{ {
[DllImport("winhttp.dll", SetLastError = true)] [DllImport("winhttp.dll", SetLastError = true)]
static extern bool WinHttpGetIEProxyConfigForCurrentUser( static extern IntPtr WinHttpOpen(string agent, int accessType, string proxy, string bypass, int flags);
out WINHTTP_CURRENT_USER_IE_PROXY_CONFIG config);
[DllImport("winhttp.dll", SetLastError = true)]
static extern bool WinHttpGetProxyForUrl(
IntPtr hSession,
string url,
ref WINHTTP_AUTOPROXY_OPTIONS options,
out WINHTTP_PROXY_INFO proxyInfo);
[DllImport("winhttp.dll")]
static extern bool WinHttpCloseHandle(IntPtr handle);
public static Uri GetProxyForUrl(Uri url) public static Uri GetProxyForUrl(Uri url)
{ {
if (!WinHttpGetIEProxyConfigForCurrentUser(out var cfg)) IntPtr session = WinHttpOpen("proxy", 0, null, null, 0);
return url;
string proxy = PtrToString(cfg.lpszProxy); var options = new WINHTTP_AUTOPROXY_OPTIONS
{
dwFlags = 0x00000001 | 0x00000002, // AUTO_DETECT + CONFIG_URL
dwAutoDetectFlags = 0x00000001 | 0x00000002 // DHCP + DNS_A
};
if (string.IsNullOrWhiteSpace(proxy)) if (WinHttpGetProxyForUrl(session, url.ToString(), ref options, out var info))
return url; {
string proxy = PtrToString(info.lpszProxy);
string selectedProxy = ParseProxy(proxy, url.Scheme); if (!string.IsNullOrEmpty(proxy))
{
if (string.IsNullOrEmpty(selectedProxy)) var first = proxy.Split(';')[0];
return url; return new Uri(Normalize(first));
}
return new Uri(NormalizeProxy(selectedProxy));
} }
// ===== helpers ===== WinHttpCloseHandle(session);
return url; // direct fallback
}
static string PtrToString(IntPtr ptr) static string PtrToString(IntPtr ptr)
{ {
if (ptr == IntPtr.Zero) if (ptr == IntPtr.Zero) return null;
return null;
return Marshal.PtrToStringUni(ptr); return Marshal.PtrToStringUni(ptr);
} }
static string ParseProxy(string proxy, string scheme) static string Normalize(string proxy)
{ {
// варианты: if (!proxy.StartsWith("http"))
// "proxy:8080"
// "http=proxy:8080;https=proxy2:8080"
if (!proxy.Contains("="))
return proxy;
var entries = proxy.Split(';', StringSplitOptions.RemoveEmptyEntries);
foreach (var entry in entries)
{
var parts = entry.Split('=', 2);
if (parts.Length != 2)
continue;
if (parts[0].Equals(scheme, StringComparison.OrdinalIgnoreCase))
return parts[1];
}
// fallback — берём первый
return entries[0].Split('=')[1];
}
static string NormalizeProxy(string proxy)
{
if (!proxy.StartsWith("http://") && !proxy.StartsWith("https://"))
return "http://" + proxy; return "http://" + proxy;
return proxy; return proxy;
} }
// ===== struct ===== struct WINHTTP_AUTOPROXY_OPTIONS
struct WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
{ {
public bool fAutoDetect; public int dwFlags;
public int dwAutoDetectFlags;
public IntPtr lpszAutoConfigUrl; public IntPtr lpszAutoConfigUrl;
public IntPtr lpvReserved;
public int dwReserved;
public bool fAutoLogonIfChallenged;
}
struct WINHTTP_PROXY_INFO
{
public int dwAccessType;
public IntPtr lpszProxy; public IntPtr lpszProxy;
public IntPtr lpszProxyBypass; public IntPtr lpszProxyBypass;
} }
} }
static async Task Pump(Stream from, Stream to)
{
var buffer = ArrayPool<byte>.Shared.Rent(8192);
try
{
while (true)
{
int read = await from.ReadAsync(buffer);
if (read <= 0) break;
await to.WriteAsync(buffer.AsMemory(0, read));
}
}
catch { }
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
}