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 Headers = new(); public Stream Body; } static async Task 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) { WinHttpGetIEProxyConfigForCurrentUser(out var cfg); if (!string.IsNullOrEmpty(cfg.lpszProxy)) { var parts = cfg.lpszProxy.Split(':'); return new Uri($"http://{parts[0]}:{parts[1]}"); } return url; // direct } struct WINHTTP_CURRENT_USER_IE_PROXY_CONFIG { public bool fAutoDetect; public IntPtr lpszAutoConfigUrl; public IntPtr lpszProxy; public IntPtr lpszProxyBypass; } }