using Extreme.Net; using LiteDB; using System; using System.Linq; using System.Text.RegularExpressions; namespace RuriLib.Models { /// /// The Status of a CProxy. /// public enum Status { /// The proxy is not assigned to any bot. AVAILABLE, /// The proxy is assigned to at least 1 bot. BUSY, /// The proxy didn't accept the connection. BAD, /// The proxy was banned. BANNED } /// /// The Working Status of a CProxy. /// public enum ProxyWorking { /// The proxy is working fine. YES, /// The proxy is down or not accepting connections. NO, /// The proxy was not tested. UNTESTED } /// /// A proxy that supports http(s) and socks4/4a/5 protocols, authorization and chaining. /// public class CProxy { /// Needed for NoSQL storage. public Guid Id { get; set; } /// The unparsed proxy string. public string Proxy { get; set; } = ""; /// The username used for authentication (empty if none). public string Username { get; set; } = ""; /// The password used for authentication (empty if none). public string Password { get; set; } = ""; /// The type of proxy. public ProxyType Type { get; set; } = ProxyType.Http; /// The country of the proxy's ip. public string Country { get; set; } = ""; /// The response delay of the proxy. public int Ping { get; set; } = 0; /// The next proxy object in a Proxy Chain. public CProxy Next { get; set; } = null; /// When the proxy was last used. public DateTime LastUsed { get; set; } = new DateTime(1970, 1, 1); /// When the proxy was last checked. public DateTime LastChecked { get; set; } = new DateTime(1970, 1, 1); /// The Working Status of the proxy. public ProxyWorking Working { get; set; } = ProxyWorking.UNTESTED; /// Whether the proxy has a successor in the Proxy Chain. [BsonIgnore] public bool HasNext { get { return Next != null; } } /// The number of times the proxy was used for a check. [BsonIgnore] public int Uses { get; set; } = 0; /// The number of bots the proxy is hooked to. [BsonIgnore] public int Hooked { get; set; } = 0; /// The clearance cookie from Cloudflare. [BsonIgnore] public string Clearance { get; set; } = ""; /// The cfduid cookie from Cloudflare. [BsonIgnore] public string Cfduid { get; set; } = ""; /// The status of the proxy. [BsonIgnore] public Status Status { get; set; } = Status.AVAILABLE; /// Needed for NoSQL deserialization. public CProxy() { } /// /// Creates a proxy given a string and a proxy type. /// /// The unparsed proxy string /// The proxy type public CProxy(string proxy, ProxyType type) { Proxy = proxy; Type = type; } /// /// Parses a CProxy object from an advanced string. Supports Proxy Chains. /// /// The string to parse the proxy from /// The default type to use when not specified /// The parsed CProxy object public CProxy Parse(string proxy, ProxyType defaultType = ProxyType.Http) { // Take the first proxy of the chain var chain = proxy.Split(new string[] { "->" }, 2, StringSplitOptions.None); var current = chain[0]; // If the type was specified, parse it and remove it from the string if (current.StartsWith("(")) { var groups = Regex.Match(current, @"^\((.*)\)(.*)").Groups; Enum.TryParse(groups[1].Value, true, out var type); Type = type; current = current.Replace($"({groups[1].Value})", ""); } else // Otherwise set the default type passed { Type = defaultType; } // Split the host:port:user:pass portion and set the first two var fields = current.Split(':'); Proxy = $"{fields[0]}:{fields[1]}"; // Set the other two if they are present if (fields.Count() > 2) { Username = fields[2]; Password = fields[3]; } // If the chain is not finished, set the next proxy by parsing recursively if (chain.Count() > 1) { Next = (new CProxy()).Parse(chain[1], defaultType); } // Finally return this proxy return this; } /// /// Gets the ProxyClient related to the specific proxy type. /// /// The ProxyClient to be used in a HttpRequest public ProxyClient GetClient() { if (HasNext) { ChainProxyClient cpc = new ChainProxyClient(); var current = this; while (current != null) { switch (current.Type) { case ProxyType.Http: cpc.AddHttpProxy(current.Proxy); break; case ProxyType.Socks4: cpc.AddSocks4Proxy(current.Proxy); break; case ProxyType.Socks4a: cpc.AddSocks4aProxy(current.Proxy); break; case ProxyType.Socks5: cpc.AddSocks5Proxy(current.Proxy); break; } current = current.Next; } return cpc; } else { switch (Type) { case ProxyType.Http: return HttpProxyClient.Parse(Proxy); case ProxyType.Socks4: return Socks4ProxyClient.Parse(Proxy); case ProxyType.Socks4a: return Socks4aProxyClient.Parse(Proxy); case ProxyType.Socks5: return Socks5ProxyClient.Parse(Proxy); default: return null; } } } /// The Host string parsed from the proxy. [BsonIgnore] public string Host { get { try { return Proxy.Split(':').First(); } catch { return ""; } } } /// The Port string parsed from the proxy. [BsonIgnore] public string Port { get { try { return Proxy.Split(':').Last(); } catch { return ""; } } } /// Whether the proxy is a valid numeric proxy. [BsonIgnore] public bool IsValidNumeric { get { return !( Host == "" || Port == "" || Port.Any(c => !char.IsDigit(c)) || Host.Split('.').Count() != 4 || !IsNumeric ); } } /// Whether the proxy is numeric. [BsonIgnore] public bool IsNumeric { get { return !Host.Split('.').Any(x => x.Any(c => !char.IsDigit(c))); } } /// How long ago the proxy was used. [BsonIgnore] public TimeSpan UsedAgo { get { return DateTime.Now - LastUsed; } } } }