From 40939c17adcceda73e617a9dd83bb6c01d13a075 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 20:28:47 +0000 Subject: [PATCH] feat: Implement detailed proxy checking This commit introduces a number of new features to the proxy checker, including: - Proxy type detection (HTTP, HTTPS, SOCKS5) - Anonymity level detection (elite, anonymous, transparent) - Geolocation and ASN information - Outgoing IP address - Additional headers added by the proxy The code has also been refactored to improve organization and performance. The new `ProxyChecker` class encapsulates all the logic for checking a proxy, and the output has been updated to display the new information in a clear and organized table. --- CheckProxy.csproj | 1 + Program.cs | 192 +++++++++------------------------------ ProxyChecker.cs | 223 ++++++++++++++++++++++++++++++++++++++++++++++ ProxyInfo.cs | 46 ++++++++++ 4 files changed, 312 insertions(+), 150 deletions(-) create mode 100644 ProxyChecker.cs create mode 100644 ProxyInfo.cs diff --git a/CheckProxy.csproj b/CheckProxy.csproj index 9a3020c..c775d0e 100644 --- a/CheckProxy.csproj +++ b/CheckProxy.csproj @@ -15,6 +15,7 @@ + diff --git a/Program.cs b/Program.cs index dda05cf..5b6f02f 100644 --- a/Program.cs +++ b/Program.cs @@ -1,7 +1,5 @@ using Spectre.Console; using System.Net; -using System.Net.NetworkInformation; -using System.Net.Sockets; using System.CommandLine; /// @@ -53,21 +51,26 @@ public static async Task Main(string[] args) /// The timeout in milliseconds for each check. static async Task CheckSingleProxy(string proxyAddress, int timeout) { - if (IPEndPoint.TryParse(proxyAddress, out var proxyEndPoint)) + if (IPEndPoint.TryParse(proxyAddress, out _)) { var webProxy = new WebProxy(proxyAddress); - AnsiConsole.MarkupLine("Target: [bold]" + proxyAddress + "[/]"); - - var columns = new List - { - new Markup("[bold]Ping Check[/]"), - new Markup("[bold]TCP Connection[/]"), - new Markup("[bold]HTTP Proxy Check[/]"), - }; - AnsiConsole.Write(new Columns(columns)); - - var results = await RunChecksInParallel(webProxy, proxyEndPoint, timeout); - AnsiConsole.Write(new Columns(results.Select(r => new Markup(r.ToString())))); + var checker = new ProxyChecker(webProxy); + var proxyInfo = await checker.CheckProxyAsync(); + + var table = new Table(); + table.AddColumn("Property"); + table.AddColumn("Value"); + + table.AddRow("Address", proxyInfo.Address ?? "N/A"); + table.AddRow("Type", proxyInfo.Type ?? "N/A"); + table.AddRow("Anonymity", proxyInfo.Anonymity ?? "N/A"); + table.AddRow("Country", proxyInfo.Country ?? "N/A"); + table.AddRow("ASN", proxyInfo.Asn ?? "N/A"); + table.AddRow("Outgoing IP", proxyInfo.OutgoingIp ?? "N/A"); + table.AddRow("Alive", proxyInfo.IsAlive ? "[green]Yes[/]" : "[red]No[/]"); + table.AddRow("Additional Headers", proxyInfo.AdditionalHeaders ?? "N/A"); + + AnsiConsole.Write(table); } else { @@ -91,14 +94,14 @@ static async Task CheckProxiesFromFile(FileInfo file, int timeout) var proxies = await File.ReadAllLinesAsync(file.FullName); AnsiConsole.MarkupLine($"[yellow]Found {proxies.Length} proxies in {file.Name}. Starting checks...[/]"); - var columns = new List - { - new Markup("[bold]Proxy[/]"), - new Markup("[bold]Ping[/]"), - new Markup("[bold]TCP[/]"), - new Markup("[bold]HTTP[/]"), - }; - AnsiConsole.Write(new Columns(columns)); + var table = new Table(); + table.AddColumn("Address"); + table.AddColumn("Type"); + table.AddColumn("Anonymity"); + table.AddColumn("Country"); + table.AddColumn("ASN"); + table.AddColumn("Outgoing IP"); + table.AddColumn("Alive"); var tasks = new List(); var semaphore = new SemaphoreSlim(10); // Limit to 10 concurrent checks @@ -111,25 +114,25 @@ static async Task CheckProxiesFromFile(FileInfo file, int timeout) { try { - if (IPEndPoint.TryParse(proxyAddress, out var proxyEndPoint)) + if (IPEndPoint.TryParse(proxyAddress, out _)) { var webProxy = new WebProxy(proxyAddress); - var results = await RunChecksInParallel(webProxy, proxyEndPoint, timeout); - AnsiConsole.Write(new Columns( - new Markup(proxyAddress), - new Markup(results[0].ToString()), - new Markup(results[1].ToString()), - new Markup(results[2].ToString()) - )); + var checker = new ProxyChecker(webProxy); + var proxyInfo = await checker.CheckProxyAsync(); + + table.AddRow( + proxyInfo.Address ?? "N/A", + proxyInfo.Type ?? "N/A", + proxyInfo.Anonymity ?? "N/A", + proxyInfo.Country ?? "N/A", + proxyInfo.Asn ?? "N/A", + proxyInfo.OutgoingIp ?? "N/A", + proxyInfo.IsAlive ? "[green]Yes[/]" : "[red]No[/]" + ); } else { - AnsiConsole.Write(new Columns( - new Markup(proxyAddress), - new Markup("[red]Invalid[/]"), - new Markup("[red]Invalid[/]"), - new Markup("[red]Invalid[/]") - )); + table.AddRow(proxyAddress, "[red]Invalid[/]", "[red]Invalid[/]", "[red]Invalid[/]", "[red]Invalid[/]", "[red]Invalid[/]", "[red]No[/]"); } } finally @@ -140,118 +143,7 @@ static async Task CheckProxiesFromFile(FileInfo file, int timeout) } await Task.WhenAll(tasks); + AnsiConsole.Write(table); AnsiConsole.MarkupLine("[green]All proxy checks complete.[/]"); } - - /// - /// Runs a series of checks for a given proxy in parallel. - /// - /// The WebProxy object for the proxy. - /// The IPEndPoint for the proxy. - /// The timeout in milliseconds for each check. - /// A boolean array indicating the result of each check. - static async Task RunChecksInParallel(WebProxy wp, IPEndPoint proxyEp, int timeout) - { - var pingCheckTask = PingCheckAsync(proxyEp.Address.ToString(), timeout); - var tcpConnectionTask = TcpConnectionCheckAsync(wp.Address.Host, wp.Address.Port, timeout); - var httpProxyCheckTask = HttpProxyCheckAsync(wp, timeout); - - await Task.WhenAll( - pingCheckTask, - tcpConnectionTask, - httpProxyCheckTask); - - return new[] - { - pingCheckTask.Result, - tcpConnectionTask.Result, - httpProxyCheckTask.Result, - }; - } - - /// - /// Performs an HTTP GET request through the proxy to check its functionality. - /// - /// The WebProxy to use for the request. - /// The timeout in milliseconds for the request. - /// True if the request is successful; otherwise, false. - static async Task HttpProxyCheckAsync(WebProxy wp, int timeout) - { - var handler = new HttpClientHandler - { - Proxy = wp, - UseProxy = true, - }; - - using var client = new HttpClient(handler); - client.Timeout = TimeSpan.FromMilliseconds(timeout); - - try - { - using var response = await client.GetAsync("http://www.google.com/generate_204"); - return response.IsSuccessStatusCode; - } - catch - { - return false; - } - } - - /// - /// Pings the specified address to check for reachability. - /// - /// The IP address or hostname to ping. - /// The timeout in milliseconds for the ping. - /// True if the ping is successful; otherwise, false. - static async Task PingCheckAsync(string address, int timeout) - { - using var ping = new Ping(); - try - { - var reply = await ping.SendPingAsync(address, timeout); - return reply?.Status == IPStatus.Success; - } - catch (PingException) - { - return false; - } - } - - /// - /// Attempts to open a TCP socket to the specified host and port. - /// - /// The target host. - /// The target port. - /// The timeout in milliseconds for the connection attempt. - /// True if the connection is successful; otherwise, false. - static async Task TcpConnectionCheckAsync(string host, int port, int timeout) - { - using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - try - { - var connectTask = socket.ConnectAsync(host, port); - var timeoutTask = Task.Delay(TimeSpan.FromMilliseconds(timeout)); - - var completedTask = await Task.WhenAny(connectTask, timeoutTask); - - if (completedTask == timeoutTask) - { - return false; - } - - await connectTask; - return socket.Connected; - } - catch - { - return false; - } - finally - { - if (socket.Connected) - { - socket.Disconnect(false); - } - } - } -} \ No newline at end of file +} diff --git a/ProxyChecker.cs b/ProxyChecker.cs new file mode 100644 index 0000000..91fc3b0 --- /dev/null +++ b/ProxyChecker.cs @@ -0,0 +1,223 @@ +using Newtonsoft.Json; +using System.Net; +using System.Net.Http.Headers; + +/// +/// A class to check the validity of a proxy server. +/// +public class ProxyChecker +{ + private readonly HttpClient _httpClient; + private readonly WebProxy _proxy; + + /// + /// Initializes a new instance of the class. + /// + /// The proxy to use for the checks. + public ProxyChecker(WebProxy proxy) + { + _proxy = proxy; + var handler = new HttpClientHandler + { + Proxy = proxy, + UseProxy = true, + }; + + _httpClient = new HttpClient(handler); + _httpClient.Timeout = TimeSpan.FromMilliseconds(5000); + } + + /// + /// Checks the proxy and returns a object with the results. + /// + /// A object with the results of the check. + public async Task CheckProxyAsync() + { + var proxyInfo = new ProxyInfo + { + Address = _proxy.Address.ToString(), + IsAlive = false, + }; + + try + { + var response = await _httpClient.GetStringAsync("http://httpbin.org/headers"); + var headers = JsonConvert.DeserializeObject(response); + + var publicIpAddress = headers.Origin; + var headerDictionary = headers.Headers; + + proxyInfo.IsAlive = true; + proxyInfo.OutgoingIp = publicIpAddress; + + var ipInfo = await GetIpInfoAsync(publicIpAddress); + if (ipInfo != null) + { + proxyInfo.Country = ipInfo.Country; + proxyInfo.Asn = ipInfo.Asn; + } + + proxyInfo.Type = await GetProxyTypeAsync(); + proxyInfo.Anonymity = GetAnonymity(publicIpAddress, headerDictionary); + proxyInfo.AdditionalHeaders = string.Join(", ", headerDictionary.Keys); + } + catch + { + // Ignore exceptions + } + + return proxyInfo; + } + + /// + /// Gets the public IP address of the system. + /// + /// Whether to use the proxy to get the public IP address. + /// The public IP address of the system. + private async Task GetPublicIpAddressAsync(bool useProxy) + { + try + { + var httpClient = useProxy ? _httpClient : new HttpClient(); + var response = await httpClient.GetStringAsync("https://api.ipify.org"); + return response.Trim(); + } + catch + { + return null; + } + } + + /// + /// Gets the geolocation and ASN information for the specified IP address. + /// + /// The IP address to get the information for. + /// A object with the geolocation and ASN information. + private async Task GetIpInfoAsync(string ipAddress) + { + try + { + var response = await new HttpClient().GetStringAsync($"http://ip-api.com/json/{ipAddress}"); + return JsonConvert.DeserializeObject(response); + } + catch + { + return null; + } + } + + /// + /// Gets the proxy type. + /// + /// The proxy type. + private async Task GetProxyTypeAsync() + { + if (await IsSocks5ProxyAsync()) + { + return "SOCKS5"; + } + + if (await IsHttpsProxyAsync()) + { + return "HTTPS"; + } + + return "HTTP"; + } + + /// + /// Checks if the proxy is a SOCKS5 proxy. + /// + /// True if the proxy is a SOCKS5 proxy; otherwise, false. + private async Task IsSocks5ProxyAsync() + { + try + { + var socket = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); + await socket.ConnectAsync(_proxy.Address.Host, _proxy.Address.Port); + var buffer = new byte[] { 5, 1, 0 }; + await socket.SendAsync(buffer, System.Net.Sockets.SocketFlags.None); + var response = new byte[2]; + await socket.ReceiveAsync(response, System.Net.Sockets.SocketFlags.None); + return response[0] == 5 && response[1] == 0; + } + catch + { + return false; + } + } + + /// + /// Checks if the proxy is an HTTPS proxy. + /// + /// True if the proxy is an HTTPS proxy; otherwise, false. + private async Task IsHttpsProxyAsync() + { + try + { + var response = await _httpClient.GetAsync("https://www.google.com"); + return response.IsSuccessStatusCode; + } + catch + { + return false; + } + } + + /// + /// Gets the anonymity level of the proxy. + /// + /// The public IP address of the system. + /// The headers from the proxy. + /// The anonymity level of the proxy. + private string GetAnonymity(string publicIpAddress, Dictionary headers) + { + if (headers.ContainsKey("X-Forwarded-For") || headers.ContainsKey("Via")) + { + var forwardedFor = headers.ContainsKey("X-Forwarded-For") ? headers["X-Forwarded-For"] : ""; + if (forwardedFor.Contains(publicIpAddress)) + { + return "Transparent"; + } + return "Anonymous"; + } + + return "Elite"; + } +} + +/// +/// Represents the headers returned by httpbin.org/headers. +/// +public class HttpBinHeaders +{ + /// + /// Gets or sets the origin IP address. + /// + [JsonProperty("origin")] + public string Origin { get; set; } + + /// + /// Gets or sets the headers. + /// + [JsonProperty("headers")] + public Dictionary Headers { get; set; } +} + +/// +/// Represents the geolocation and ASN information for an IP address. +/// +public class IpInfo +{ + /// + /// Gets or sets the country of the IP address. + /// + [JsonProperty("country")] + public string Country { get; set; } + + /// + /// Gets or sets the Autonomous System Number (ASN) of the IP address. + /// + [JsonProperty("as")] + public string Asn { get; set; } +} diff --git a/ProxyInfo.cs b/ProxyInfo.cs new file mode 100644 index 0000000..69e51fa --- /dev/null +++ b/ProxyInfo.cs @@ -0,0 +1,46 @@ +/// +/// Represents the information about a proxy server. +/// +public class ProxyInfo +{ + /// + /// Gets or sets the proxy address. + /// + public string Address { get; set; } + + /// + /// Gets or sets the proxy type (e.g., HTTP, HTTPS, SOCKS5). + /// + public string Type { get; set; } + + /// + /// Gets or sets the anonymity level (e.g., Elite, Anonymous, Transparent). + /// + public string Anonymity { get; set; } + + /// + /// Gets or sets the country of the proxy. + /// + public string Country { get; set; } + + /// + /// Gets or sets the Autonomous System Number (ASN) of the proxy. + /// + public string Asn { get; set; } + + /// + /// Gets or sets the outgoing IP address of the proxy. + /// + public string OutgoingIp { get; set; } + + /// + /// Gets or sets a value indicating whether the proxy is alive. + /// + public bool IsAlive { get; set; } + + /// + summary> + /// Gets or sets any additional headers added by the proxy. + /// + public string AdditionalHeaders { get; set; } +}