Обновить Program.cs
This commit is contained in:
184
Program.cs
184
Program.cs
@@ -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,
|
||||||
@@ -111,40 +112,47 @@ class Program
|
|||||||
|
|
||||||
// ================= CONNECT =================
|
// ================= CONNECT =================
|
||||||
|
|
||||||
static async Task HandleConnect(NetworkStream clientStream, string target)
|
static async Task HandleConnect(NetworkStream clientStream, string target)
|
||||||
|
{
|
||||||
|
var parts = target.Split(':');
|
||||||
|
string host = parts[0];
|
||||||
|
int port = int.Parse(parts[1]);
|
||||||
|
|
||||||
|
var proxy = new WinHttpProxy();
|
||||||
|
var proxyUri = proxy.GetProxy(new Uri($"https://{host}:{port}"));
|
||||||
|
|
||||||
|
using var upstream = new TcpClient();
|
||||||
|
await upstream.ConnectAsync(proxyUri.Host, proxyUri.Port);
|
||||||
|
|
||||||
|
var upstreamStream = upstream.GetStream();
|
||||||
|
|
||||||
|
// CONNECT к upstream proxy
|
||||||
|
string connectReq =
|
||||||
|
$"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));
|
||||||
|
|
||||||
|
// читаем ответ upstream полностью до \r\n\r\n
|
||||||
|
var resp = await ReadHeaders(upstreamStream);
|
||||||
|
|
||||||
|
if (!resp.HeadersRaw.Contains("200"))
|
||||||
{
|
{
|
||||||
var parts = target.Split(':');
|
await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 502 Bad Gateway\r\n\r\n"));
|
||||||
string host = parts[0];
|
return;
|
||||||
int port = int.Parse(parts[1]);
|
|
||||||
|
|
||||||
var proxy = new WinHttpProxy();
|
|
||||||
var proxyUri = proxy.GetProxy(new Uri($"https://{host}:{port}"));
|
|
||||||
|
|
||||||
using var upstream = new TcpClient();
|
|
||||||
await upstream.ConnectAsync(proxyUri.Host, proxyUri.Port);
|
|
||||||
|
|
||||||
var upstreamStream = upstream.GetStream();
|
|
||||||
|
|
||||||
// CONNECT к upstream
|
|
||||||
string connectReq =
|
|
||||||
$"CONNECT {host}:{port} HTTP/1.1\r\nHost: {host}:{port}\r\n\r\n";
|
|
||||||
|
|
||||||
await upstreamStream.WriteAsync(Encoding.ASCII.GetBytes(connectReq));
|
|
||||||
|
|
||||||
// читаем ответ от upstream
|
|
||||||
var buffer = new byte[8192];
|
|
||||||
int read = await upstreamStream.ReadAsync(buffer);
|
|
||||||
|
|
||||||
// отвечаем клиенту что туннель открыт
|
|
||||||
await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n"));
|
|
||||||
|
|
||||||
// двунаправленный туннель
|
|
||||||
var t1 = upstreamStream.CopyToAsync(clientStream);
|
|
||||||
var t2 = clientStream.CopyToAsync(upstreamStream);
|
|
||||||
|
|
||||||
await Task.WhenAny(t1, t2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// говорим клиенту ОК
|
||||||
|
await clientStream.WriteAsync(Encoding.ASCII.GetBytes("HTTP/1.1 200 Connection Established\r\n\r\n"));
|
||||||
|
|
||||||
|
// туннель
|
||||||
|
var t1 = Pump(upstreamStream, clientStream);
|
||||||
|
var t2 = Pump(clientStream, upstreamStream);
|
||||||
|
|
||||||
|
await Task.WhenAny(t1, t2);
|
||||||
|
}
|
||||||
|
|
||||||
// ================= AUTH =================
|
// ================= AUTH =================
|
||||||
|
|
||||||
static bool CheckAuth(string headers)
|
static bool CheckAuth(string headers)
|
||||||
@@ -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))
|
||||||
|
{
|
||||||
|
var first = proxy.Split(';')[0];
|
||||||
|
return new Uri(Normalize(first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(selectedProxy))
|
WinHttpCloseHandle(session);
|
||||||
return url;
|
return url; // direct fallback
|
||||||
|
|
||||||
return new Uri(NormalizeProxy(selectedProxy));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== helpers =====
|
|
||||||
|
|
||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user