Files
anti-syktsu-proxy/Program.cs
2026-04-09 18:26:49 +02:00

312 lines
8.5 KiB
C#

using System;
using System.Net;
using System.Net.Http;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Runtime.InteropServices;
class Program
{
static string USER = "user";
static string PASS = "pass";
static async Task Main()
{
int port = 8888;
Console.WriteLine($"Local proxy:");
Console.WriteLine($" http://127.0.0.1:{port}");
Console.WriteLine($" login: {USER}");
Console.WriteLine($" pass : {PASS}");
var listener = new TcpListener(IPAddress.Any, port);
listener.Start();
while (true)
{
var client = await listener.AcceptTcpClientAsync();
_ = Task.Run(() => HandleClient(client));
}
}
static async Task HandleClient(TcpClient client)
{
using (client)
{
var stream = client.GetStream();
var request = await ReadHeaders(stream);
if (request == null) return;
if (!CheckAuth(request.HeadersRaw))
{
await Write407(stream);
return;
}
if (request.Method == "CONNECT")
{
await HandleConnect(stream, request.Target);
}
else
{
await HandleHttp(stream, request);
}
}
}
// ================= HTTP =================
static async Task HandleHttp(NetworkStream clientStream, HttpRequestData req)
{
var handler = new SocketsHttpHandler
{
UseProxy = true,
Proxy = new WinHttpProxy(),
DefaultProxyCredentials = CredentialCache.DefaultCredentials,
AllowAutoRedirect = false
};
using var http = new HttpClient(handler);
var message = new HttpRequestMessage(new HttpMethod(req.Method), req.Url);
foreach (var h in req.Headers)
{
if (!h.Key.Equals("Proxy-Authorization", StringComparison.OrdinalIgnoreCase))
message.Headers.TryAddWithoutValidation(h.Key, h.Value);
}
if (req.Body != null)
message.Content = new StreamContent(req.Body);
var resp = await http.SendAsync(message, HttpCompletionOption.ResponseHeadersRead);
// статус
var statusLine = $"HTTP/1.1 {(int)resp.StatusCode} {resp.ReasonPhrase}\r\n";
await clientStream.WriteAsync(Encoding.ASCII.GetBytes(statusLine));
// заголовки
foreach (var h in resp.Headers)
{
foreach (var v in h.Value)
await WriteHeader(clientStream, h.Key, v);
}
foreach (var h in resp.Content.Headers)
{
foreach (var v in h.Value)
await WriteHeader(clientStream, h.Key, v);
}
await clientStream.WriteAsync(Encoding.ASCII.GetBytes("\r\n"));
// тело (стриминг)
await resp.Content.CopyToAsync(clientStream);
}
// ================= CONNECT =================
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
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);
}
// ================= AUTH =================
static bool CheckAuth(string headers)
{
string expected = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{USER}:{PASS}"));
return headers.Contains($"Proxy-Authorization: Basic {expected}");
}
static async Task Write407(NetworkStream stream)
{
string resp =
"HTTP/1.1 407 Proxy Authentication Required\r\n" +
"Proxy-Authenticate: Basic realm=\"proxy\"\r\n\r\n";
await stream.WriteAsync(Encoding.ASCII.GetBytes(resp));
}
// ================= REQUEST PARSER =================
class HttpRequestData
{
public string Method;
public string Url;
public string Target;
public string HeadersRaw;
public Dictionary<string, string> Headers = new();
public Stream Body;
}
static async Task<HttpRequestData> ReadHeaders(NetworkStream stream)
{
var buffer = new byte[8192];
int read = await stream.ReadAsync(buffer);
if (read <= 0) return null;
string text = Encoding.ASCII.GetString(buffer, 0, read);
var lines = text.Split("\r\n");
var first = lines[0].Split(' ');
var data = new HttpRequestData
{
Method = first[0],
Target = first[1],
HeadersRaw = text
};
foreach (var line in lines.Skip(1))
{
if (string.IsNullOrWhiteSpace(line)) break;
int idx = line.IndexOf(':');
if (idx > 0)
data.Headers[line[..idx]] = line[(idx + 1)..].Trim();
}
if (data.Method != "CONNECT")
{
data.Url = data.Target.StartsWith("http")
? data.Target
: $"http://{data.Headers["Host"]}{data.Target}";
}
return data;
}
static async Task WriteHeader(NetworkStream stream, string key, string value)
{
string line = $"{key}: {value}\r\n";
await stream.WriteAsync(Encoding.ASCII.GetBytes(line));
}
}
// ================= WINHTTP PROXY =================
class WinHttpProxy : IWebProxy
{
public ICredentials Credentials { get; set; }
public Uri GetProxy(Uri destination)
{
return WinHttpHelper.GetProxyForUrl(destination);
}
public bool IsBypassed(Uri host) => false;
}
static class WinHttpHelper
{
[DllImport("winhttp.dll", SetLastError = true)]
static extern bool WinHttpGetIEProxyConfigForCurrentUser(
out WINHTTP_CURRENT_USER_IE_PROXY_CONFIG config);
public static Uri GetProxyForUrl(Uri url)
{
if (!WinHttpGetIEProxyConfigForCurrentUser(out var cfg))
return url;
string proxy = PtrToString(cfg.lpszProxy);
if (string.IsNullOrWhiteSpace(proxy))
return url;
string selectedProxy = ParseProxy(proxy, url.Scheme);
if (string.IsNullOrEmpty(selectedProxy))
return url;
return new Uri(NormalizeProxy(selectedProxy));
}
// ===== helpers =====
static string PtrToString(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
return Marshal.PtrToStringUni(ptr);
}
static string ParseProxy(string proxy, string scheme)
{
// варианты:
// "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 proxy;
}
// ===== struct =====
struct WINHTTP_CURRENT_USER_IE_PROXY_CONFIG
{
public bool fAutoDetect;
public IntPtr lpszAutoConfigUrl;
public IntPtr lpszProxy;
public IntPtr lpszProxyBypass;
}
}