diff options
author | crupest <crupest@outlook.com> | 2024-10-03 14:29:09 +0800 |
---|---|---|
committer | crupest <crupest@outlook.com> | 2024-10-03 14:29:09 +0800 |
commit | 39c51129d32659dd93b4d1ab9bd945a0c57df2f9 (patch) | |
tree | 300745f607c2e40aa7525408c9025c6a1e768110 /tools | |
parent | 29f91f56829266668f0b65620e8e902218d61c33 (diff) | |
download | crupest-39c51129d32659dd93b4d1ab9bd945a0c57df2f9.tar.gz crupest-39c51129d32659dd93b4d1ab9bd945a0c57df2f9.tar.bz2 crupest-39c51129d32659dd93b4d1ab9bd945a0c57df2f9.zip |
Rename secret tool.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Crupest.SecretTool/.gitignore (renamed from tools/Crupest.V2ray/.gitignore) | 0 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool.sln (renamed from tools/Crupest.V2ray/CrupestV2ray.sln) | 15 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore (renamed from tools/Crupest.V2ray/Crupest.V2ray/.gitignore) | 0 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/Config.cs) | 24 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs) | 20 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj (renamed from tools/Crupest.V2ray/Crupest.V2ray/Crupest.V2ray.csproj) | 0 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/FileWatcher.cs) | 2 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/GeoDataManager.cs) | 46 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs | 123 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/Program.cs) | 50 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml (renamed from tools/Crupest.V2ray/Crupest.V2ray/Properties/PublishProfiles/FolderProfile.pubxml) | 2 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs | 58 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs | 97 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs | 40 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/SurgeConfigGenerator.cs) | 26 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/Template.cs) | 2 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs) | 31 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/V2rayV4ConfigJsonObjects.cs) | 4 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs (renamed from tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigJsonObjects.cs) | 4 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template (renamed from tools/Crupest.V2ray/Crupest.V2ray/config.json.template) | 4 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/config.v5.json.template (renamed from tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template) | 0 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt (renamed from tools/Crupest.V2ray/Crupest.V2ray/hosts.txt) | 0 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt (renamed from tools/Crupest.V2ray/Crupest.V2ray/proxy.txt) | 0 | ||||
-rwxr-xr-x | tools/Crupest.SecretTool/build-secret.bash (renamed from tools/Crupest.V2ray/build-secret.bash) | 2 | ||||
-rwxr-xr-x | tools/Crupest.SecretTool/tools/cru-proxy-edit (renamed from tools/Crupest.V2ray/tools/cru-proxy-edit) | 2 | ||||
-rwxr-xr-x | tools/Crupest.SecretTool/tools/cru-proxy-log (renamed from tools/Crupest.V2ray/tools/cru-proxy-log) | 4 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/tools/crupest-secret-tool.service (renamed from tools/Crupest.V2ray/tools/crupest-v2ray.service) | 2 | ||||
-rw-r--r-- | tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist (renamed from tools/Crupest.V2ray/tools/life.crupest.v2ray.plist) | 8 | ||||
-rw-r--r-- | tools/Crupest.V2ray/Crupest.V2ray/ProxyFile.cs | 14 | ||||
-rw-r--r-- | tools/Crupest.V2ray/Crupest.V2ray/V2rayHostMacherConfig.cs | 109 | ||||
-rw-r--r-- | tools/Crupest.V2ray/Crupest.V2ray/V2rayHosts.cs | 42 | ||||
-rw-r--r-- | tools/Crupest.V2ray/Crupest.V2ray/V2rayProxy.cs | 58 | ||||
-rw-r--r-- | tools/Crupest.V2ray/Crupest.V2ray/V2rayRouting.cs | 87 | ||||
-rw-r--r-- | tools/Crupest.V2ray/Crupest.V2ray/V2rayV5StaticHostRule.cs | 122 |
34 files changed, 445 insertions, 553 deletions
diff --git a/tools/Crupest.V2ray/.gitignore b/tools/Crupest.SecretTool/.gitignore index ac4d8a4..ac4d8a4 100644 --- a/tools/Crupest.V2ray/.gitignore +++ b/tools/Crupest.SecretTool/.gitignore diff --git a/tools/Crupest.V2ray/CrupestV2ray.sln b/tools/Crupest.SecretTool/Crupest.SecretTool.sln index 3045b4e..fde4347 100644 --- a/tools/Crupest.V2ray/CrupestV2ray.sln +++ b/tools/Crupest.SecretTool/Crupest.SecretTool.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.7.34024.191 @@ -8,7 +8,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .gitignore = .gitignore EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crupest.V2ray", "Crupest.V2ray\Crupest.V2ray.csproj", "{154D49F2-242E-4384-8D34-73774231AA75}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crupest.SecretTool", "Crupest.SecretTool\Crupest.SecretTool.csproj", "{D6335AE4-FD22-49CD-9624-37371F3B4F82}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -16,17 +16,14 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {154D49F2-242E-4384-8D34-73774231AA75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {154D49F2-242E-4384-8D34-73774231AA75}.Debug|Any CPU.Build.0 = Debug|Any CPU - {154D49F2-242E-4384-8D34-73774231AA75}.Release|Any CPU.ActiveCfg = Release|Any CPU - {154D49F2-242E-4384-8D34-73774231AA75}.Release|Any CPU.Build.0 = Release|Any CPU + {D6335AE4-FD22-49CD-9624-37371F3B4F82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6335AE4-FD22-49CD-9624-37371F3B4F82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6335AE4-FD22-49CD-9624-37371F3B4F82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6335AE4-FD22-49CD-9624-37371F3B4F82}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {154D49F2-242E-4384-8D34-73774231AA75} = {F4C2CE80-CDF8-4B08-8912-D1F0F14196AD} - EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B1E8FD9C-9157-4F4E-8265-4B37F30EEC5E} EndGlobalSection diff --git a/tools/Crupest.V2ray/Crupest.V2ray/.gitignore b/tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore index c936492..c936492 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/.gitignore +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Config.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs index 2ef18f0..ff58551 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/Config.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs @@ -1,4 +1,4 @@ -namespace Crupest.V2ray; +namespace Crupest.SecretTool; public record ConfigItem(string Value, int LineNumber); @@ -12,20 +12,24 @@ public class DictionaryConfig(string configString, List<string>? requiredKeys = foreach (var line in lines) { - var trimmedLine = line.Trim(); - if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith('#')) + var l = line; + var beginOfComment = l.IndexOf('#'); + if (beginOfComment >= 0) { - lineNumber++; - continue; + l = line[..beginOfComment]; } - - var equalIndex = trimmedLine.IndexOf('='); - if (equalIndex == -1) + l = l.Trim(); + if (!string.IsNullOrEmpty(l)) { - throw new FormatException($"No '=' found in line {lineNumber}."); + var equalIndex = l.IndexOf('='); + if (equalIndex == -1) + { + throw new FormatException($"No '=' found in line {lineNumber}."); + } + + config.Add(l[..equalIndex].Trim(), new ConfigItem(l[(equalIndex + 1)..].Trim(), lineNumber)); } - config.Add(trimmedLine[..equalIndex].Trim(), new ConfigItem(trimmedLine[(equalIndex + 1)..].Trim(), lineNumber)); lineNumber++; } diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs index 4656216..0803b01 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs @@ -1,10 +1,10 @@ using System.Diagnostics; -namespace Crupest.V2ray; +namespace Crupest.SecretTool; -public class V2rayController(string executablePath, string configPath, string? assetPath) +public class Controller(string executablePath, string configPath, string? assetPath) { - public const string V2rayAssetEnvironmentVariableName = "v2ray.location.asset"; + public const string ToolAssetEnvironmentVariableName = "v2ray.location.asset"; public static string? FindExecutable(string contentDir, out bool isLocal, string? executableName = null) { @@ -16,11 +16,11 @@ public class V2rayController(string executablePath, string configPath, string? a executableName += ".exe"; } - var localV2rayPath = Path.Combine(contentDir, executableName); - if (File.Exists(localV2rayPath)) + var localToolPath = Path.Combine(contentDir, executableName); + if (File.Exists(localToolPath)) { isLocal = true; - return localV2rayPath; + return localToolPath; } var paths = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator); @@ -28,10 +28,10 @@ public class V2rayController(string executablePath, string configPath, string? a { foreach (var p in paths) { - var v2rayPath = Path.Combine(p, executableName); - if (File.Exists(v2rayPath)) + var toolPath = Path.Combine(p, executableName); + if (File.Exists(toolPath)) { - return v2rayPath; + return toolPath; } } } @@ -57,7 +57,7 @@ public class V2rayController(string executablePath, string configPath, string? a startInfo.ArgumentList.Add(ConfigPath); if (AssetPath is not null) { - startInfo.EnvironmentVariables[V2rayAssetEnvironmentVariableName] = AssetPath; + startInfo.EnvironmentVariables[ToolAssetEnvironmentVariableName] = AssetPath; } process.StartInfo = startInfo; diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Crupest.V2ray.csproj b/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj index 0812e4c..0812e4c 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/Crupest.V2ray.csproj +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj diff --git a/tools/Crupest.V2ray/Crupest.V2ray/FileWatcher.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs index 547adeb..193874f 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/FileWatcher.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs @@ -1,4 +1,4 @@ -namespace Crupest.V2ray; +namespace Crupest.SecretTool; public class FileWatcher(string directory, List<string> fileNames) { diff --git a/tools/Crupest.V2ray/Crupest.V2ray/GeoDataManager.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs index 3dce3f6..2d9e2eb 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/GeoDataManager.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs @@ -1,6 +1,6 @@ using System.IO.Compression; -namespace Crupest.V2ray; +namespace Crupest.SecretTool; public interface IGeoSiteEntry { @@ -13,7 +13,7 @@ public record GeoSiteIncludeEntry(string Value, string ContainingSite) : IGeoSit public bool IsInclude => true; } -public record GeoSiteRuleEntry(V2rayHostMatcherKind Kind, string Value, List<string> Attributes, string ContainingSite) : IGeoSiteEntry +public record GeoSiteRuleEntry(HostMatchKind Kind, string Value, List<string> Attributes, string ContainingSite) : IGeoSiteEntry { public bool IsInclude => false; } @@ -45,26 +45,26 @@ public record GeoSite(string Name, List<IGeoSiteEntry> Entries) throw new FormatException($"Invalid geo site rule '{name}' in line {line}. More than one ':'."); } - V2rayHostMatcherKind kind; + HostMatchKind kind; if (segments.Length == 2) { kind = segments[0] switch { - "domain" => kind = V2rayHostMatcherKind.DomainSuffix, - "full" => kind = V2rayHostMatcherKind.DomainFull, - "keyword" => kind = V2rayHostMatcherKind.DomainKeyword, - "regexp" => kind = V2rayHostMatcherKind.DomainRegex, + "domain" => kind = HostMatchKind.DomainSuffix, + "full" => kind = HostMatchKind.DomainFull, + "keyword" => kind = HostMatchKind.DomainKeyword, + "regexp" => kind = HostMatchKind.DomainRegex, _ => throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Unknown matcher.") }; } else { - kind = V2rayHostMatcherKind.DomainSuffix; + kind = HostMatchKind.DomainSuffix; } var domainSegments = segments[^1].Split('@', StringSplitOptions.TrimEntries); var domain = domainSegments[0]; - if (kind != V2rayHostMatcherKind.DomainRegex && Uri.CheckHostName(domain) != UriHostNameType.Dns) + if (kind != HostMatchKind.DomainRegex && Uri.CheckHostName(domain) != UriHostNameType.Dns) { throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Invalid domain."); } @@ -108,11 +108,11 @@ public class GeoSiteData(string directory) } public List<GeoSiteRuleEntry> GetEntriesRecursive(List<string> sites, - List<V2rayHostMatcherKind>? onlyMatcherKinds = null, List<string>? onlyAttributes = null) + List<HostMatchKind>? onlyMatcherKinds = null, List<string>? onlyAttributes = null) { List<GeoSiteRuleEntry> entries = []; HashSet<string> visited = []; - HashSet<V2rayHostMatcherKind>? kinds = onlyMatcherKinds?.ToHashSet(); + HashSet<HostMatchKind>? kinds = onlyMatcherKinds?.ToHashSet(); void Visit(string site) { @@ -164,12 +164,16 @@ public class GeoDataManager public const string GeoSiteFileName = "geosite.dat"; public const string GeoIpFileName = "geoip.dat"; public const string GeoIpCnFileName = "geoip-only-cn-private.dat"; - public const string V2rayGithubOrganization = "v2fly"; - public const string V2rayGeoSiteGithubRepository = "domain-list-community"; - public const string V2rayGeoIpGithubRepository = "geoip"; - public const string V2rayGeoSiteCnGithubReleaseFilename = "dlc.dat"; - public const string V2rayGeoIpGithubReleaseFilename = "geoip.dat"; - public const string V2rayGeoIpCnGithubReleaseFilename = "geoip-only-cn-private.dat"; + + public static class ToolGithub + { + public const string Organization = "v2fly"; + public const string GeoSiteRepository = "domain-list-community"; + public const string GeoIpRepository = "geoip"; + public const string GeoSiteReleaseFilename = "dlc.dat"; + public const string GeoIpReleaseFilename = "geoip.dat"; + public const string GeoIpCnReleaseFilename = "geoip-only-cn-private.dat"; + } public static GeoDataManager Instance { get; } = new GeoDataManager(); @@ -179,9 +183,9 @@ public class GeoDataManager { Assets = [ - new("geosite", GeoSiteFileName, V2rayGithubOrganization, V2rayGeoSiteGithubRepository, V2rayGeoSiteGithubRepository), - new("geoip", GeoIpFileName, V2rayGithubOrganization, V2rayGeoIpGithubRepository, V2rayGeoIpGithubReleaseFilename), - new("geoip-cn", GeoIpCnFileName, V2rayGithubOrganization, V2rayGeoIpGithubRepository, V2rayGeoIpCnGithubReleaseFilename), + new("geosite", GeoSiteFileName, ToolGithub.Organization, ToolGithub.GeoSiteRepository, ToolGithub.GeoSiteRepository), + new("geoip", GeoIpFileName, ToolGithub.Organization, ToolGithub.GeoIpRepository, ToolGithub.GeoIpReleaseFilename), + new("geoip-cn", GeoIpCnFileName, ToolGithub.Organization, ToolGithub.GeoIpRepository, ToolGithub.GeoIpCnReleaseFilename), ]; } @@ -272,7 +276,7 @@ public class GeoDataManager { var archivePath = Path.Combine(tempDirectoryPath, zipFileName); var extractPath = Path.Combine(tempDirectoryPath, "repo"); - GithubDownloadRepository(httpClient, V2rayGithubOrganization, V2rayGeoSiteGithubRepository, archivePath, silent); + GithubDownloadRepository(httpClient, ToolGithub.Organization, ToolGithub.GeoSiteRepository, archivePath, silent); if (!silent) { Console.WriteLine($"Extract geo data to {extractPath}."); } Directory.CreateDirectory(extractPath); Unzip(archivePath, extractPath); diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs new file mode 100644 index 0000000..5cc0c3d --- /dev/null +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs @@ -0,0 +1,123 @@ +namespace Crupest.SecretTool; + +public enum HostMatchKind +{ + DomainFull, + DomainSuffix, + DomainKeyword, + DomainRegex, + Ip, + GeoSite, + GeoIp, +} + +public static class HostMatchKindExtensions +{ + public static bool IsDomain(this HostMatchKind kind) + { + return kind.IsNonRegexDomain() || kind == HostMatchKind.DomainRegex; + } + + public static bool IsNonRegexDomain(this HostMatchKind kind) + { + return kind is HostMatchKind.DomainFull or HostMatchKind.DomainSuffix or HostMatchKind.DomainKeyword; + } + + + public static List<HostMatchKind> DomainMatchKinds { get; } = [HostMatchKind.DomainFull, HostMatchKind.DomainSuffix, HostMatchKind.DomainKeyword, HostMatchKind.DomainRegex]; + + public static List<HostMatchKind> NonRegexDomainMatchKinds { get; } = [HostMatchKind.DomainFull, HostMatchKind.DomainSuffix, HostMatchKind.DomainKeyword]; +} + +public record HostMatchConfigItem(HostMatchKind Kind, string MatchString, List<string> Values); + +public class HostMatchConfig(string configString, List<HostMatchKind> allowedMatchKinds, int minComponentCount = -1, int maxComponentCount = -1) +{ + private static List<HostMatchConfigItem> Parse(string configString, List<HostMatchKind> allowedMatchKinds, int minComponentCount = -1, int maxComponentCount = -1) + { + var items = new ListConfig(configString).Config; + var result = new List<HostMatchConfigItem>(); + + foreach (var item in items) + { + var lineNumber = item.LineNumber; + var line = item.Value; + var hasExplicitMatchKind = false; + var segments = line.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(); + + foreach (var matchKind in Enum.GetValues<HostMatchKind>()) + { + var matchKindName = Enum.GetName(matchKind) ?? throw new Exception("No such match kind."); + hasExplicitMatchKind = true; + if (segments[0] == matchKindName) + { + if (segments.Count < 2) + { + throw new FormatException($"Explicit match item needs a value in line {lineNumber}."); + } + if (allowedMatchKinds.Contains(matchKind)) + { + if (matchKind.IsNonRegexDomain() && Uri.CheckHostName(matchKindName) != UriHostNameType.Dns) + { + throw new FormatException($"Invalid domain format in line {lineNumber}."); + } + + var components = segments[2..].ToList(); + if (minComponentCount > 0 && components.Count < minComponentCount) + { + throw new FormatException($"Too few components in line {lineNumber}, at least {minComponentCount} required."); + } + if (maxComponentCount >= 0 && components.Count > maxComponentCount) + { + throw new FormatException($"Too many components in line {lineNumber}, only {maxComponentCount} allowed."); + } + result.Add(new HostMatchConfigItem(matchKind, segments[1], components)); + } + else + { + throw new FormatException($"Match kind {matchKindName} is not allowed at line {lineNumber}."); + } + } + } + + if (!hasExplicitMatchKind) + { + if (minComponentCount > 0 && segments.Count - 1 < minComponentCount) + { + throw new FormatException($"Too few components in line {lineNumber}, at least {minComponentCount} required."); + } + if (maxComponentCount >= 0 && segments.Count - 1 > maxComponentCount) + { + throw new FormatException($"Too many components in line {lineNumber}, only {maxComponentCount} allowed."); + } + result.Add(new HostMatchConfigItem(HostMatchKind.DomainSuffix, segments[0], segments.Count == 1 ? [] : segments[1..])); + } + } + return result; + } + + public string ConfigString { get; } = configString; + public List<HostMatchKind> AllowedMatchKinds { get; } = allowedMatchKinds; + public int MinComponentCount { get; } = minComponentCount; + public int MaxComponentCount { get; } = maxComponentCount; + public List<HostMatchConfigItem> Items { get; } = Parse(configString, allowedMatchKinds, minComponentCount, maxComponentCount); +} + +public class HostMatchConfigFile +{ + public HostMatchConfigFile(string path, List<HostMatchKind> allowedMatchKinds, int minComponentCount = -1, int maxComponentCount = -1) + { + Path = path; + FileContent = File.ReadAllText(path); + Config = new HostMatchConfig(FileContent, allowedMatchKinds, minComponentCount, maxComponentCount); ; + } + + public string Path { get; } + public string FileContent { get; } + public HostMatchConfig Config { get; } +} + +public class ProxyFile(string path) : + HostMatchConfigFile(path, [.. Enum.GetValues<HostMatchKind>()], maxComponentCount: 0) +{ +} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Program.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs index 0e98861..afbcde9 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/Program.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs @@ -1,56 +1,56 @@ using System.Reflection; -namespace Crupest.V2ray; +namespace Crupest.SecretTool; public static class Program { - public static string Name { get; } = typeof(Program).Namespace ?? throw new Exception("Can't get the name of Crupest.V2ray."); + public static string Name { get; } = typeof(Program).Namespace ?? throw new Exception("Can't get the name of Crupest.SecretTool."); - public static string CrupestV2rayDirectory { get; } = + public static string CrupestSecretToolDirectory { get; } = Environment.GetEnvironmentVariable("CRUPEST_V2RAY_DIR") ?? Path.GetFullPath(Path.GetDirectoryName( - Assembly.GetExecutingAssembly().Location) ?? throw new Exception("Can't get the path of Crupest.V2ray.")); + Assembly.GetExecutingAssembly().Location) ?? throw new Exception("Can't get the path of Crupest.SecretTool.")); private const string ConfigOutputFileName = "config.json"; private const string SurgeRuleSetChinaOutputFileName = "ChinaRuleSet.txt"; private const string SurgeRuleSetGlobalOutputFileName = "GlobalRuleSet.txt"; - public static void RunV2rayAndWatchConfigChange() + public static void RunToolAndWatchConfigChange() { - var v2rayPath = V2rayController.FindExecutable(CrupestV2rayDirectory, out var isLocal) ?? - throw new Exception("Can't find v2ray executable either in Crupest.V2ray directory or in PATH."); + var executablePath = Controller.FindExecutable(CrupestSecretToolDirectory, out var isLocal) ?? + throw new Exception("Can't find v2ray executable either in Crupest.SecretTool directory or in PATH."); string? assetsPath; if (isLocal) { - assetsPath = CrupestV2rayDirectory; - var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestV2rayDirectory, out var missing); + assetsPath = CrupestSecretToolDirectory; + var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestSecretToolDirectory, out var missing); if (!assetsComplete) { - throw new Exception($"Missing assets: {string.Join(", ", missing)} in {CrupestV2rayDirectory}. This v2ray is local. So only use assets in Crupest.V2ray directory."); + throw new Exception($"Missing assets: {string.Join(", ", missing)} in {CrupestSecretToolDirectory}. This v2ray is local. So only use assets in Crupest.SecretTool directory."); } } else { - assetsPath = CrupestV2rayDirectory; - var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestV2rayDirectory, out var missing); + assetsPath = CrupestSecretToolDirectory; + var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestSecretToolDirectory, out var missing); if (!assetsComplete) { - Console.WriteLine($"Missing assets: {string.Join(", ", missing)} in {CrupestV2rayDirectory}. This v2ray is global. So fallback to its own assets."); + Console.WriteLine($"Missing assets: {string.Join(", ", missing)} in {CrupestSecretToolDirectory}. This v2ray is global. So fallback to its own assets."); assetsPath = null; } } - var v2rayController = new V2rayController(v2rayPath, Path.Combine(CrupestV2rayDirectory, ConfigOutputFileName), assetsPath); - var configFileWatcher = new FileWatcher(CrupestV2rayDirectory, V2rayConfig.ConfigFileNames); + var controller = new Controller(executablePath, Path.Combine(CrupestSecretToolDirectory, ConfigOutputFileName), assetsPath); + var configFileWatcher = new FileWatcher(CrupestSecretToolDirectory, ToolConfig.ConfigFileNames); - V2rayConfig.FromDirectoryAndWriteToFile(CrupestV2rayDirectory, Path.Join(CrupestV2rayDirectory, ConfigOutputFileName)); - v2rayController.Start(); + ToolConfig.FromDirectoryAndWriteToFile(CrupestSecretToolDirectory, Path.Join(CrupestSecretToolDirectory, ConfigOutputFileName)); + controller.Start(); configFileWatcher.OnChanged += () => { - V2rayConfig.FromDirectoryAndWriteToFile(CrupestV2rayDirectory, Path.Join(CrupestV2rayDirectory, ConfigOutputFileName)); - v2rayController.Restart(); + ToolConfig.FromDirectoryAndWriteToFile(CrupestSecretToolDirectory, Path.Join(CrupestSecretToolDirectory, ConfigOutputFileName)); + controller.Restart(); }; configFileWatcher.Run(); @@ -67,28 +67,28 @@ public static class Program var verb = args[0].ToLower(); if (verb == "download-geodata" || verb == "dg") { - GeoDataManager.Instance.Download(CrupestV2rayDirectory, false); + GeoDataManager.Instance.Download(CrupestSecretToolDirectory, false); return; } else if (verb == "generate-surge-rule-set" || verb == "gs") { SurgeConfigGenerator.GenerateTo( - Path.Join(CrupestV2rayDirectory, "proxy.txt"), - Path.Join(CrupestV2rayDirectory, SurgeRuleSetChinaOutputFileName), - Path.Join(CrupestV2rayDirectory, SurgeRuleSetGlobalOutputFileName), + Path.Join(CrupestSecretToolDirectory, "proxy.txt"), + Path.Join(CrupestSecretToolDirectory, SurgeRuleSetChinaOutputFileName), + Path.Join(CrupestSecretToolDirectory, SurgeRuleSetGlobalOutputFileName), true, false ); return; } else if (verb == "generate" || verb == "g") { - var config = V2rayConfig.FromDirectory(CrupestV2rayDirectory); + var config = ToolConfig.FromDirectory(CrupestSecretToolDirectory); Console.Out.WriteLine(config.ToJsonStringV4()); return; } throw new Exception("Invalid command line arguments."); } - RunV2rayAndWatchConfigChange(); + RunToolAndWatchConfigChange(); } } diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Properties/PublishProfiles/FolderProfile.pubxml b/tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml index bbdd2ad..5fca454 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/Properties/PublishProfiles/FolderProfile.pubxml +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml @@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. <PropertyGroup> <Configuration>Release</Configuration> <Platform>Any CPU</Platform> - <PublishDir>bin\Release\net7.0\publish\</PublishDir> + <PublishDir>bin\Release\net8.0\publish\</PublishDir> <PublishProtocol>FileSystem</PublishProtocol> <_TargetId>Folder</_TargetId> </PropertyGroup> diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs new file mode 100644 index 0000000..638edb6 --- /dev/null +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs @@ -0,0 +1,58 @@ +namespace Crupest.SecretTool; + +public abstract class Proxy(string tag) : IV4ConfigObject +{ + public string Tag { get; set; } = tag; + + public abstract V4ConfigJsonObjects.Outbound ToJsonObjectV4(); + + object IV4ConfigObject.ToJsonObjectV4() + { + return ToJsonObjectV4(); + } +} + +public class HttpProxy(string host, int port, string tag) : Proxy(tag) +{ + public string Host { get; set; } = host; + public int Port { get; set; } = port; + + public override V4ConfigJsonObjects.Outbound ToJsonObjectV4() + { + return new V4ConfigJsonObjects.Outbound(Tag, "http", + new V4ConfigJsonObjects.HttpOutboundSettings([new V4ConfigJsonObjects.HttpOutboundServer(Host, Port, [])]), + null + ); + } +} + + +public class VmessProxy(string host, int port, string userId, string path, string tag) : Proxy(tag) +{ + public string Host { get; set; } = host; + public int Port { get; set; } = port; + public string Path { get; set; } = path; + public string UserId { get; set; } = userId; + + public override V4ConfigJsonObjects.Outbound ToJsonObjectV4() + { + return new V4ConfigJsonObjects.Outbound(Tag, "vmess", + new V4ConfigJsonObjects.VmessOutboundSettings( + [new V4ConfigJsonObjects.VnextServer(Host, Port, [new V4ConfigJsonObjects.VnextServerUser(UserId, 0, "auto", 0)])]), + new V4ConfigJsonObjects.WsStreamSettings("ws", "tls", new V4ConfigJsonObjects.WsSettings(Path, new() { ["Host"] = Host })) + ); + } + + public static VmessProxy CreateFromConfigString(string configString, string tag) + { + var config = new DictionaryConfig(configString, ["host", "port", "userid", "path"]); + var portString = config.GetItemCaseInsensitive("port").Value; + if (!int.TryParse(portString, out var port) || port <= 0) + { + throw new FormatException($"Invalid port number: {portString}: not an integer or is a invalid number."); + } + return new VmessProxy(config.GetItemCaseInsensitive("host").Value, port, + config.GetItemCaseInsensitive("userid").Value, config.GetItemCaseInsensitive("path").Value, tag + ); + } +} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs new file mode 100644 index 0000000..dbced0e --- /dev/null +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs @@ -0,0 +1,97 @@ +namespace Crupest.SecretTool; + +public record RoutingRule(HostMatchKind MatchKind, string MatchString, string OutboundTag) : IV4ConfigObject +{ + public string ToolConfigString => MatchKind switch + { + HostMatchKind.DomainFull => $"full:{MatchString}", + HostMatchKind.DomainSuffix => $"domain:{MatchString}", + HostMatchKind.DomainKeyword => MatchString, + HostMatchKind.DomainRegex => $"regexp:{MatchString}", + HostMatchKind.Ip => MatchString, + HostMatchKind.GeoSite => $"geosite:{MatchString}", + HostMatchKind.GeoIp => $"geoip:{MatchString}", + _ => throw new ArgumentException("Invalid matcher kind.") + }; + + public static Dictionary<string, List<RoutingRule>> GroupByOutboundTag(List<RoutingRule> rules) + => rules.GroupBy(r => r.OutboundTag).Select(g => (g.Key, g.ToList())).ToDictionary(); + + public static Dictionary<HostMatchKind, List<RoutingRule>> GroupByMatchKind(List<RoutingRule> rules) + => rules.GroupBy(r => r.MatchKind).Select(g => (g.Key, g.ToList())).ToDictionary(); + + public static List<List<RoutingRule>> GroupByOutboundTagAndMatcherKind(List<RoutingRule> rules) + => GroupByOutboundTag(rules).Values.SelectMany((groupByTag) => GroupByMatchKind(groupByTag).Values).ToList(); + + public static V4ConfigJsonObjects.RoutingRule ListToJsonObject(List<RoutingRule> rules) + { + if (rules.Count == 0) + { + throw new ArgumentException("Rule list is empty."); + } + + var matchKind = rules[0].MatchKind; + var outboundTag = rules[0].OutboundTag; + + if (rules.Any(r => r.OutboundTag != outboundTag) || rules.Any(r => r.MatchKind != matchKind)) + { + throw new ArgumentException("Rules must have the same matcher kind and outbound tag."); + } + + List<string> toolConfigList = rules.Select(r => r.ToolConfigString).ToList(); + + return new V4ConfigJsonObjects.RoutingRule(OutboundTag: outboundTag, + Ip: (matchKind is HostMatchKind.Ip or HostMatchKind.GeoIp) ? toolConfigList : null, + Domains: (matchKind.IsDomain() || matchKind == HostMatchKind.GeoSite) ? toolConfigList : null + ); + } + + public RoutingRule CloneGeositeWithCnAttribute(string outboundTag) + { + if (MatchKind is not HostMatchKind.GeoSite) + { + throw new ArgumentException("Matcher kind must be GeoSite."); + } + + return new RoutingRule(HostMatchKind.GeoSite, $"{MatchString}@cn", outboundTag); + } + + public V4ConfigJsonObjects.RoutingRule ToJsonObjectV4() => ListToJsonObject([this]); + + object IV4ConfigObject.ToJsonObjectV4() => ToJsonObjectV4(); +} + +public record Routing(List<RoutingRule> Rules, bool DirectGeositeCn = true, string DomainStrategy = "IpOnDemand") : IV4ConfigObject +{ + public List<RoutingRule> CreateGeositeCnDirectRules() + { + return Rules.Where(r => r.MatchKind is HostMatchKind.GeoSite) + .Select(r => r.CloneGeositeWithCnAttribute("direct")).ToList(); + } + + public V4ConfigJsonObjects.Routing ToJsonObjectV4(bool directGeositeCn = true) + { + List<V4ConfigJsonObjects.RoutingRule> ruleJsonObjects = []; + + if (directGeositeCn) + { + ruleJsonObjects.Add(RoutingRule.ListToJsonObject(CreateGeositeCnDirectRules())); + } + + ruleJsonObjects.AddRange(RoutingRule.GroupByOutboundTagAndMatcherKind(Rules).Select(RoutingRule.ListToJsonObject)); + + return new V4ConfigJsonObjects.Routing(ruleJsonObjects); + } + + object IV4ConfigObject.ToJsonObjectV4() => ToJsonObjectV4(); + + public static Routing FromProxyFile(ProxyFile proxyFile, string outboundTag, bool directGeositeCn) + { + + return new Routing( + proxyFile.Config.Items.Select( + i => new RoutingRule(i.Kind, i.MatchString, outboundTag)).ToList(), + directGeositeCn + ); + } +} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs new file mode 100644 index 0000000..b112e1c --- /dev/null +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs @@ -0,0 +1,40 @@ +namespace Crupest.SecretTool; + +public record StaticHostRule(HostMatchKind MatchKind, string MatchString, List<string> ResolveResult) +{ + public string AddressString() + { + return MatchKind switch + { + HostMatchKind.DomainFull => MatchString, + HostMatchKind.DomainSuffix => $"domain:{MatchString}", + HostMatchKind.DomainKeyword => $"keyword:{MatchString}", + HostMatchKind.DomainRegex => $"regexp:{MatchString}", + _ => throw new ArgumentOutOfRangeException($"Match kind {MatchKind} is not allowed in static host rule."), + }; + } + + public object ResolveResultToJsonObject() + { + return ResolveResult.Count == 1 ? ResolveResult[0] : ResolveResult; + } +} + +public class StaticHosts(List<StaticHostRule> rules) : IV4ConfigObject +{ + public List<StaticHostRule> Rules { get; } = rules; + + public Dictionary<string, object> ToJsonObjectV4() => + Rules.ToDictionary(rule => rule.AddressString(), rule => rule.ResolveResultToJsonObject()); + + object IV4ConfigObject.ToJsonObjectV4() + { + return ToJsonObjectV4(); + } + + public static StaticHosts CreateFromHostMatchConfigString(string configString) + { + var config = new HostMatchConfig(configString, HostMatchKindExtensions.DomainMatchKinds, minComponentCount: 1); + return new StaticHosts(config.Items.Select(i => new StaticHostRule(i.Kind, i.MatchString, [.. i.Values])).ToList()); + } +} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/SurgeConfigGenerator.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs index bd52234..451db3e 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/SurgeConfigGenerator.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs @@ -1,44 +1,44 @@ -namespace Crupest.V2ray; +namespace Crupest.SecretTool; public class SurgeConfigGenerator(ProxyFile proxyFile, GeoSiteData geoSiteData) { public ProxyFile ProxyFile { get; } = proxyFile; public GeoSiteData GeoSiteData { get; } = geoSiteData; - private static string ToSurgeRuleString(V2rayHostMatcherKind kind, string value) + private static string ToSurgeRuleString(HostMatchKind kind, string value) { var ruleType = kind switch { - V2rayHostMatcherKind.DomainFull => "DOMAIN", - V2rayHostMatcherKind.DomainSuffix => "DOMAIN-SUFFIX", - V2rayHostMatcherKind.DomainKeyword => "DOMAIN-KEYWORD", - V2rayHostMatcherKind.DomainRegex => "URL-REGEX", + HostMatchKind.DomainFull => "DOMAIN", + HostMatchKind.DomainSuffix => "DOMAIN-SUFFIX", + HostMatchKind.DomainKeyword => "DOMAIN-KEYWORD", + HostMatchKind.DomainRegex => "URL-REGEX", _ => throw new Exception("Unacceptable matcher kind for Surge rule.") }; return $"{ruleType},{value}"; } - private static List<V2rayHostMatcherKind> DomainMatcherKinds { get; } = [ - V2rayHostMatcherKind.DomainFull, V2rayHostMatcherKind.DomainKeyword, - V2rayHostMatcherKind.DomainRegex, V2rayHostMatcherKind.DomainSuffix, + private static List<HostMatchKind> DomainMatcherKinds { get; } = [ + HostMatchKind.DomainFull, HostMatchKind.DomainKeyword, + HostMatchKind.DomainRegex, HostMatchKind.DomainSuffix, ]; public string GenerateChinaRuleSet() { - var geoSites = ProxyFile.MatcherConfig.Items.Where(i => i.Kind == V2rayHostMatcherKind.GeoSite).Select(i => i.Matcher).ToList(); + var geoSites = ProxyFile.Config.Items.Where(i => i.Kind == HostMatchKind.GeoSite).Select(i => i.MatchString).ToList(); var cnRules = GeoSiteData.GetEntriesRecursive(geoSites, DomainMatcherKinds, ["cn"]).ToList(); return string.Join('\n', cnRules.Select(r => ToSurgeRuleString(r.Kind, r.Value))); } public string GenerateGlobalRuleSet() { - var geoSites = ProxyFile.MatcherConfig.Items.Where(i => i.Kind == V2rayHostMatcherKind.GeoSite).Select(i => i.Matcher).ToList(); + var geoSites = ProxyFile.Config.Items.Where(i => i.Kind == HostMatchKind.GeoSite).Select(i => i.MatchString).ToList(); var nonCnRules = GeoSiteData.GetEntriesRecursive(geoSites, DomainMatcherKinds).Where(e => !e.Attributes.Contains("cn")).ToList(); - var domainRules = ProxyFile.MatcherConfig.Items.Where(i => DomainMatcherKinds.Contains(i.Kind)).ToList(); + var domainRules = ProxyFile.Config.Items.Where(i => DomainMatcherKinds.Contains(i.Kind)).ToList(); return string.Join('\n', [ ..nonCnRules.Select(r => ToSurgeRuleString(r.Kind, r.Value)), - ..domainRules.Select(r => ToSurgeRuleString(r.Kind, r.Matcher)) + ..domainRules.Select(r => ToSurgeRuleString(r.Kind, r.MatchString)) ]); } diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Template.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs index 9c137b0..1fe91b1 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/Template.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs @@ -1,7 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text; -namespace Crupest.V2ray; +namespace Crupest.SecretTool; public class Template { diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs index e81a6cb..4fe9a40 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs @@ -1,14 +1,14 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Crupest.V2ray; +namespace Crupest.SecretTool; -public interface IV2rayV4ConfigObject +public interface IV4ConfigObject { object ToJsonObjectV4(); } -public class V2rayConfig(Template template, List<V2rayProxy> proxies, V2rayRouting router, V2rayHosts? hosts) +public class ToolConfig(Template template, List<Proxy> proxies, Routing router, StaticHosts? hosts) { private class JsonInterfaceConverter<Interface> : JsonConverter<Interface> { @@ -42,7 +42,7 @@ public class V2rayConfig(Template template, List<V2rayProxy> proxies, V2rayRouti private const string RoutingAnchor = "ROUTING_ANCHOR"; private const string HostsAnchor = "HOSTS_ANCHOR"; - public const string AddCnAttributeToGeositeEnvironmentVariable = "CRUPEST_V@RAY_GEOSITE_USE_CN"; + public const string AddCnAttributeToGeositeEnvironmentVariable = "CRUPEST_V2RAY_GEOSITE_USE_CN"; private static bool UseCnGeoSite => Environment.GetEnvironmentVariable(AddCnAttributeToGeositeEnvironmentVariable) switch { @@ -51,9 +51,9 @@ public class V2rayConfig(Template template, List<V2rayProxy> proxies, V2rayRouti }; public Template Template { get; set; } = template; - public List<V2rayProxy> Proxies { get; set; } = proxies; - public V2rayRouting Routing { get; set; } = router; - public V2rayHosts Hosts { get; set; } = hosts is null ? new V2rayHosts([]) : hosts; + public List<Proxy> Proxies { get; set; } = proxies; + public Routing Routing { get; set; } = router; + public StaticHosts Hosts { get; set; } = hosts is null ? new StaticHosts([]) : hosts; public string ToJsonStringV4(bool pretty = true) { @@ -63,8 +63,9 @@ public class V2rayConfig(Template template, List<V2rayProxy> proxies, V2rayRouti DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }); - jsonOptions.Converters.Add(new JsonInterfaceConverter<V2rayV4ConfigJsonObjects.IOutboundSettings>()); - jsonOptions.Converters.Add(new JsonInterfaceConverter<V2rayV4ConfigJsonObjects.IOutboundStreamSettings>()); + // TODO: Make interface converter generic. + jsonOptions.Converters.Add(new JsonInterfaceConverter<V4ConfigJsonObjects.IOutboundSettings>()); + jsonOptions.Converters.Add(new JsonInterfaceConverter<V4ConfigJsonObjects.IOutboundStreamSettings>()); var templateValues = new Dictionary<string, string> { @@ -89,7 +90,7 @@ public class V2rayConfig(Template template, List<V2rayProxy> proxies, V2rayRouti } } - public static V2rayConfig FromFiles(string templatePath, string vmessPath, string proxyPath, string? hostsPath) + public static ToolConfig FromFiles(string templatePath, string vmessPath, string proxyPath, string? hostsPath) { foreach (var path in new List<string>([templatePath, vmessPath, proxyPath])) { @@ -122,12 +123,12 @@ public class V2rayConfig(Template template, List<V2rayProxy> proxies, V2rayRouti file = templatePath; var template = new Template(templateString); file = vmessPath; - var vmess = V2rayVmessProxy.CreateFromConfigString(vmessString, "proxy"); + var vmess = VmessProxy.CreateFromConfigString(vmessString, "proxy"); file = proxyPath; - var routing = proxyFile.ToV2rayRouting("proxy", UseCnGeoSite); + var routing = Routing.FromProxyFile(proxyFile, "proxy", UseCnGeoSite); file = hostsPath ?? ""; - var hosts = hostsString is not null ? V2rayHosts.CreateFromHostMatcherConfigString(hostsString) : null; - return new V2rayConfig(template, [vmess], routing, hosts); + var hosts = hostsString is not null ? StaticHosts.CreateFromHostMatchConfigString(hostsString) : null; + return new ToolConfig(template, [vmess], routing, hosts); } catch (Exception e) { @@ -135,7 +136,7 @@ public class V2rayConfig(Template template, List<V2rayProxy> proxies, V2rayRouti } } - public static V2rayConfig FromDirectory(string directory) + public static ToolConfig FromDirectory(string directory) { return FromFiles( Path.Join(directory, ConfigTemplateFileName), diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV4ConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs index 672af71..3e81dbb 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV4ConfigJsonObjects.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs @@ -1,6 +1,6 @@ -namespace Crupest.V2ray; +namespace Crupest.SecretTool; -public static class V2rayV4ConfigJsonObjects +public static class V4ConfigJsonObjects { public interface IObject; public interface IOutboundSettings : IObject; diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs index 56d64ca..a50e9be 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigJsonObjects.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs @@ -1,6 +1,6 @@ -namespace Crupest.V2ray; +namespace Crupest.SecretTool; -public static class V2rayV5ConfigJsonObjects +public static class V5ConfigJsonObjects { public record OutboundObject(string Protocol, object Settings, string Tag, object? StreamSettings) { diff --git a/tools/Crupest.V2ray/Crupest.V2ray/config.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template index 686006c..424e996 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/config.json.template +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template @@ -4,7 +4,7 @@ }, "inbounds": [ { - "port": 3081, + "port": 2081, "listen": "127.0.0.1", "tag": "socks-inbound", "protocol": "socks", @@ -13,7 +13,7 @@ } }, { - "port": 3080, + "port": 2080, "listen": "127.0.0.1", "tag": "http-inbound", "protocol": "http", diff --git a/tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/config.v5.json.template index 01ccf7a..01ccf7a 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/config.v5.json.template diff --git a/tools/Crupest.V2ray/Crupest.V2ray/hosts.txt b/tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt index 88d5015..88d5015 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/hosts.txt +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt diff --git a/tools/Crupest.V2ray/Crupest.V2ray/proxy.txt b/tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt index 6273e35..6273e35 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/proxy.txt +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt diff --git a/tools/Crupest.V2ray/build-secret.bash b/tools/Crupest.SecretTool/build-secret.bash index bc5c7ee..8878049 100755 --- a/tools/Crupest.V2ray/build-secret.bash +++ b/tools/Crupest.SecretTool/build-secret.bash @@ -34,7 +34,7 @@ echo "Enter \"secret\" dir..." pushd "$secret_dir" echo "Begin to build..." -dotnet publish Crupest.V2ray -c Release -o "$secret_dir/publish" --sc -r "$1" +dotnet publish Crupest.SecretTool -c Release -o "$secret_dir/publish" --sc -r "$1" popd diff --git a/tools/Crupest.V2ray/tools/cru-proxy-edit b/tools/Crupest.SecretTool/tools/cru-proxy-edit index 9ba6cbc..51a33e1 100755 --- a/tools/Crupest.V2ray/tools/cru-proxy-edit +++ b/tools/Crupest.SecretTool/tools/cru-proxy-edit @@ -2,7 +2,7 @@ set -e -p="$HOME/codes/crupest/tools/Crupest.V2ray/publish/proxy.txt" +p="$HOME/codes/crupest/tools/Crupest.SecretTool/publish/proxy.txt" if [[ ! -f "$p" ]]; then echo "File $p does not exist!" >&2 diff --git a/tools/Crupest.V2ray/tools/cru-proxy-log b/tools/Crupest.SecretTool/tools/cru-proxy-log index 0ac800c..6ec6ee1 100755 --- a/tools/Crupest.V2ray/tools/cru-proxy-log +++ b/tools/Crupest.SecretTool/tools/cru-proxy-log @@ -4,9 +4,9 @@ set -e if [[ -e /proc ]]; then # I don't believe your system is Linux but there is no /proc. - exec journalctl --user -u crupest-v2ray "$@" + exec journalctl --user -u crupest-secret-tool "$@" elif [[ "$(uname)" == "Darwin" ]]; then - exec less "$HOME/.local/state/Crupest.V2ray/log" + exec less "$HOME/.local/state/Crupest.SecretTool/log" else echo "Not supported on systems other than macOS and Linux now." >&2 exit 1 diff --git a/tools/Crupest.V2ray/tools/crupest-v2ray.service b/tools/Crupest.SecretTool/tools/crupest-secret-tool.service index afe840f..df6d172 100644 --- a/tools/Crupest.V2ray/tools/crupest-v2ray.service +++ b/tools/Crupest.SecretTool/tools/crupest-secret-tool.service @@ -2,7 +2,7 @@ Description=crupest v2ray service [Service] -ExecStart=%h/.local/bin/Crupest.V2ray +ExecStart=%h/.local/bin/Crupest.SecretTool [Install] WantedBy=default.target diff --git a/tools/Crupest.V2ray/tools/life.crupest.v2ray.plist b/tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist index 4569ae2..bdfe490 100644 --- a/tools/Crupest.V2ray/tools/life.crupest.v2ray.plist +++ b/tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist @@ -3,16 +3,16 @@ <plist version="1.0"> <dict> <key>Label</key> - <string>life.crupest.v2ray</string> + <string>life.crupest.secret-tool</string> <key>ProgramArguments</key> <array> - <string>/Users/crupest/.local/bin/Crupest.V2ray</string> + <string>/Users/crupest/.local/bin/Crupest.SecretTool</string> </array> <key>KeepAlive</key> <true/> <key>StandardOutPath</key> - <string>/Users/crupest/.local/state/Crupest.V2ray/log</string> + <string>/Users/crupest/.local/state/Crupest.SecretTool/log</string> <key>StandardErrorPath</key> - <string>/Users/crupest/.local/state/Crupest.V2ray/error</string> + <string>/Users/crupest/.local/state/Crupest.SecretTool/error</string> </dict> </plist> diff --git a/tools/Crupest.V2ray/Crupest.V2ray/ProxyFile.cs b/tools/Crupest.V2ray/Crupest.V2ray/ProxyFile.cs deleted file mode 100644 index ca5ca56..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/ProxyFile.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Crupest.V2ray; - -public class ProxyFile(string path) : - HostMatcherConfigFile(path, [.. Enum.GetValues<V2rayHostMatcherKind>()], maxComponentCount: 0) -{ - public V2rayRouting ToV2rayRouting(string outboundTag, bool directGeositeCn) - { - return new V2rayRouting( - MatcherConfig.Items.Select( - i => new V2rayRoutingRule(i.Kind, i.Matcher, outboundTag)).ToList(), - directGeositeCn - ); - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayHostMacherConfig.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayHostMacherConfig.cs deleted file mode 100644 index 36ae44b..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayHostMacherConfig.cs +++ /dev/null @@ -1,109 +0,0 @@ -namespace Crupest.V2ray; - -public enum V2rayHostMatcherKind -{ - DomainFull, - DomainSuffix, - DomainKeyword, - DomainRegex, - Ip, - GeoSite, - GeoIp, -} - -public record V2rayHostMatcherItem(V2rayHostMatcherKind Kind, string Matcher, List<string> Values); - -public class V2rayHostMatcherConfig(string configString, List<V2rayHostMatcherKind> allowedMatchers, int minComponentCount = -1, int maxComponentCount = -1) -{ - static bool IsDomainMatcher(V2rayHostMatcherKind kind) => kind switch - { - V2rayHostMatcherKind.DomainFull => true, - V2rayHostMatcherKind.DomainSuffix => true, - V2rayHostMatcherKind.DomainKeyword => true, - V2rayHostMatcherKind.DomainRegex => true, - _ => false, - }; - - private static List<V2rayHostMatcherItem> Parse(string configString, List<V2rayHostMatcherKind> allowedMatchers, int minComponentCount = -1, int maxComponentCount = -1) - { - var items = new ListConfig(configString).Config; - var result = new List<V2rayHostMatcherItem>(); - - foreach (var item in items) - { - var lineNumber = item.LineNumber; - var line = item.Value; - var hasExplicitMatcher = false; - var segments = line.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(); - - foreach (var matcher in Enum.GetValues<V2rayHostMatcherKind>()) - { - var matcherName = Enum.GetName(matcher) ?? throw new Exception("No such matcher."); - hasExplicitMatcher = true; - if (segments[0] == matcherName) - { - if (segments.Count < 2) - { - throw new FormatException($"Explicit matcher needs a value in line {lineNumber}."); - } - if (allowedMatchers.Contains(matcher)) - { - if (IsDomainMatcher(matcher) && matcher == V2rayHostMatcherKind.DomainRegex && Uri.CheckHostName(matcherName) != UriHostNameType.Dns) - { - throw new FormatException($"Invalid domain format in line {lineNumber}."); - } - - var components = segments[2..].ToList(); - if (minComponentCount > 0 && components.Count < minComponentCount) - { - throw new FormatException($"Too few components in line {lineNumber}, at least {minComponentCount} required."); - } - if (maxComponentCount >= 0 && components.Count > maxComponentCount) - { - throw new FormatException($"Too many components in line {lineNumber}, only {maxComponentCount} allowed."); - } - result.Add(new V2rayHostMatcherItem(matcher, segments[1], components)); - } - else - { - throw new FormatException($"Matcher {matcherName} is not allowed at line {lineNumber}."); - } - } - } - - if (!hasExplicitMatcher) - { - if (minComponentCount > 0 && segments.Count - 1 < minComponentCount) - { - throw new FormatException($"Too few components in line {lineNumber}, at least {minComponentCount} required."); - } - if (maxComponentCount >= 0 && segments.Count - 1 > maxComponentCount) - { - throw new FormatException($"Too many components in line {lineNumber}, only {maxComponentCount} allowed."); - } - result.Add(new V2rayHostMatcherItem(V2rayHostMatcherKind.DomainSuffix, segments[0], segments.Count == 1 ? [] : segments[1..])); - } - } - return result; - } - - public string ConfigString { get; } = configString; - public List<V2rayHostMatcherKind> AllowedMatchers { get; } = allowedMatchers; - public int MinComponentCount { get; } = minComponentCount; - public int MaxComponentCount { get; } = maxComponentCount; - public List<V2rayHostMatcherItem> Items { get; } = Parse(configString, allowedMatchers, minComponentCount, maxComponentCount); -} - -public class HostMatcherConfigFile -{ - public HostMatcherConfigFile(string path, List<V2rayHostMatcherKind> allowedMatchers, int minComponentCount = -1, int maxComponentCount = -1) - { - Path = path; - FileContent = File.ReadAllText(path); - MatcherConfig = new V2rayHostMatcherConfig(FileContent, allowedMatchers, minComponentCount, maxComponentCount); ; - } - - public string Path { get; } - public string FileContent { get; } - public V2rayHostMatcherConfig MatcherConfig { get; } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayHosts.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayHosts.cs deleted file mode 100644 index e9bf8cf..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayHosts.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace Crupest.V2ray; - -public record V2rayHostRule(V2rayHostMatcherKind MatcherKind, string MatcherString, List<string> ResolveResult) -{ - public string AddressString() - { - return MatcherKind switch - { - V2rayHostMatcherKind.DomainFull => MatcherString, - V2rayHostMatcherKind.DomainSuffix => $"domain:{MatcherString}", - V2rayHostMatcherKind.DomainKeyword => $"keyword:{MatcherString}", - V2rayHostMatcherKind.DomainRegex => $"regexp:{MatcherString}", - _ => throw new ArgumentOutOfRangeException($"Matcher {MatcherKind} is not allowed in host rule."), - }; - } - - public object ResolveResultToJsonObject() - { - return ResolveResult.Count == 1 ? ResolveResult[0] : ResolveResult; - } -} - -public class V2rayHosts(List<V2rayHostRule> rules) : IV2rayV4ConfigObject -{ - public List<V2rayHostRule> Rules { get; } = rules; - - public Dictionary<string, object> ToJsonObjectV4() => - Rules.ToDictionary(rule => rule.AddressString(), rule => rule.ResolveResultToJsonObject()); - - object IV2rayV4ConfigObject.ToJsonObjectV4() - { - return ToJsonObjectV4(); - } - - public static V2rayHosts CreateFromHostMatcherConfigString(string configString) - { - var matcherConfig = new V2rayHostMatcherConfig(configString, - [V2rayHostMatcherKind.DomainFull, V2rayHostMatcherKind.DomainKeyword, V2rayHostMatcherKind.DomainRegex, V2rayHostMatcherKind.DomainSuffix], minComponentCount: 1); - - return new V2rayHosts(matcherConfig.Items.Select(i => new V2rayHostRule(i.Kind, i.Matcher, [.. i.Values])).ToList()); - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayProxy.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayProxy.cs deleted file mode 100644 index bcb2b51..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayProxy.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace Crupest.V2ray; - -public abstract class V2rayProxy(string tag) : IV2rayV4ConfigObject -{ - public string Tag { get; set; } = tag; - - public abstract V2rayV4ConfigJsonObjects.Outbound ToJsonObjectV4(); - - object IV2rayV4ConfigObject.ToJsonObjectV4() - { - return ToJsonObjectV4(); - } -} - -public class V2rayHttpProxy(string host, int port, string tag) : V2rayProxy(tag) -{ - public string Host { get; set; } = host; - public int Port { get; set; } = port; - - public override V2rayV4ConfigJsonObjects.Outbound ToJsonObjectV4() - { - return new V2rayV4ConfigJsonObjects.Outbound(Tag, "http", - new V2rayV4ConfigJsonObjects.HttpOutboundSettings([new V2rayV4ConfigJsonObjects.HttpOutboundServer(Host, Port, [])]), - null - ); - } -} - - -public class V2rayVmessProxy(string host, int port, string userId, string path, string tag) : V2rayProxy(tag) -{ - public string Host { get; set; } = host; - public int Port { get; set; } = port; - public string Path { get; set; } = path; - public string UserId { get; set; } = userId; - - public override V2rayV4ConfigJsonObjects.Outbound ToJsonObjectV4() - { - return new V2rayV4ConfigJsonObjects.Outbound(Tag, "vmess", - new V2rayV4ConfigJsonObjects.VmessOutboundSettings( - [new V2rayV4ConfigJsonObjects.VnextServer(Host, Port, [new V2rayV4ConfigJsonObjects.VnextServerUser(UserId, 0, "auto", 0)])]), - new V2rayV4ConfigJsonObjects.WsStreamSettings("ws", "tls", new V2rayV4ConfigJsonObjects.WsSettings(Path, new() { ["Host"] = Host })) - ); - } - - public static V2rayVmessProxy CreateFromConfigString(string configString, string tag) - { - var config = new DictionaryConfig(configString, ["host", "port", "userid", "path"]); - var portString = config.GetItemCaseInsensitive("port").Value; - if (!int.TryParse(portString, out var port) || port <= 0) - { - throw new FormatException($"Invalid port number: {portString}: not an integer or is a invalid number."); - } - return new V2rayVmessProxy(config.GetItemCaseInsensitive("host").Value, port, - config.GetItemCaseInsensitive("userid").Value, config.GetItemCaseInsensitive("path").Value, tag - ); - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayRouting.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayRouting.cs deleted file mode 100644 index f385233..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayRouting.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace Crupest.V2ray; - -public record V2rayRoutingRule(V2rayHostMatcherKind MatcherKind, string MatcherString, string OutboundTag) : IV2rayV4ConfigObject -{ - public string ComposedMatcherString => MatcherKind switch - { - V2rayHostMatcherKind.DomainFull => $"full:{MatcherString}", - V2rayHostMatcherKind.DomainSuffix => $"domain:{MatcherString}", - V2rayHostMatcherKind.DomainKeyword => MatcherString, - V2rayHostMatcherKind.DomainRegex => $"regexp:{MatcherString}", - V2rayHostMatcherKind.Ip => MatcherString, - V2rayHostMatcherKind.GeoSite => $"geosite:{MatcherString}", - V2rayHostMatcherKind.GeoIp => $"geoip:{MatcherString}", - _ => throw new ArgumentException("Invalid matcher kind.") - }; - - public static Dictionary<string, List<V2rayRoutingRule>> GroupByOutboundTag(List<V2rayRoutingRule> rules) - => rules.GroupBy(r => r.OutboundTag).Select(g => (g.Key, g.ToList())).ToDictionary(); - - public static Dictionary<V2rayHostMatcherKind, List<V2rayRoutingRule>> GroupByMatcherKind(List<V2rayRoutingRule> rules) - => rules.GroupBy(r => r.MatcherKind).Select(g => (g.Key, g.ToList())).ToDictionary(); - - public static List<List<V2rayRoutingRule>> GroupByOutboundTagAndMatcherKind(List<V2rayRoutingRule> rules) - => GroupByOutboundTag(rules).Values.SelectMany((groupByTag) => GroupByMatcherKind(groupByTag).Values).ToList(); - - public static V2rayV4ConfigJsonObjects.RoutingRule ListToJsonObject(List<V2rayRoutingRule> rules) - { - if (rules.Count == 0) - { - throw new ArgumentException("Rule list is empty."); - } - - var matcherKind = rules[0].MatcherKind; - var outboundTag = rules[0].OutboundTag; - - if (rules.Any(r => r.OutboundTag != outboundTag) || rules.Any(r => r.MatcherKind != matcherKind)) - { - throw new ArgumentException("Rules must have the same matcher kind and outbound tag."); - } - - List<string> composedMatcherStringList = rules.Select(r => r.ComposedMatcherString).ToList(); - - return new V2rayV4ConfigJsonObjects.RoutingRule(OutboundTag: outboundTag, - Ip: (matcherKind is V2rayHostMatcherKind.Ip or V2rayHostMatcherKind.GeoIp) ? composedMatcherStringList : null, - Domains: (matcherKind is V2rayHostMatcherKind.DomainFull or V2rayHostMatcherKind.DomainSuffix or V2rayHostMatcherKind.DomainKeyword or V2rayHostMatcherKind.DomainRegex or V2rayHostMatcherKind.GeoSite) ? composedMatcherStringList : null - ); - } - - public V2rayRoutingRule CloneGeositeWithCnAttribute(string outboundTag) - { - if (MatcherKind is not V2rayHostMatcherKind.GeoSite) - { - throw new ArgumentException("Matcher kind must be GeoSite."); - } - - return new V2rayRoutingRule(V2rayHostMatcherKind.GeoSite, $"{MatcherString}@cn", outboundTag); - } - - public V2rayV4ConfigJsonObjects.RoutingRule ToJsonObjectV4() => ListToJsonObject([this]); - - object IV2rayV4ConfigObject.ToJsonObjectV4() => ToJsonObjectV4(); -} - -public record V2rayRouting(List<V2rayRoutingRule> Rules, bool DirectGeositeCn = true, string DomainStrategy = "IpOnDemand") : IV2rayV4ConfigObject -{ - public List<V2rayRoutingRule> CreateGeositeCnDirectRules() - { - return Rules.Where(r => r.MatcherKind is V2rayHostMatcherKind.GeoSite) - .Select(r => r.CloneGeositeWithCnAttribute("direct")).ToList(); - } - - public V2rayV4ConfigJsonObjects.Routing ToJsonObjectV4(bool directGeositeCn = true) - { - List<V2rayV4ConfigJsonObjects.RoutingRule> ruleJsonObjects = []; - - if (directGeositeCn) - { - ruleJsonObjects.Add(V2rayRoutingRule.ListToJsonObject(CreateGeositeCnDirectRules())); - } - - ruleJsonObjects.AddRange(V2rayRoutingRule.GroupByOutboundTagAndMatcherKind(Rules).Select(V2rayRoutingRule.ListToJsonObject)); - - return new V2rayV4ConfigJsonObjects.Routing(ruleJsonObjects); - } - - object IV2rayV4ConfigObject.ToJsonObjectV4() => ToJsonObjectV4(); -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5StaticHostRule.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5StaticHostRule.cs deleted file mode 100644 index cdead3c..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5StaticHostRule.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System.Net; - -namespace Crupest.V2ray; - -public interface IV2rayStaticHostResolveResult -{ - IDictionary<string, object> GetJsonProperties(); -} - -public class V2rayStaticHostDomainResolveResult : IV2rayStaticHostResolveResult -{ - public V2rayStaticHostDomainResolveResult(string domain) - { - Domain = domain; - } - - public string Domain { get; } - - public IDictionary<string, object> GetJsonProperties() - { - return new Dictionary<string, object> - { - - ["proxiedDomain"] = Domain - }; - } -} - -public class V2rayStaticHostIpResolveResult : IV2rayStaticHostResolveResult -{ - public V2rayStaticHostIpResolveResult(IEnumerable<string> ips) - { - Ips = ips.ToList(); - } - - public IReadOnlyList<string> Ips { get; } - - public IDictionary<string, object> GetJsonProperties() - { - return new Dictionary<string, object> - { - ["ip"] = Ips - }; - } -} - - -public class V2rayV5StaticHostRule(V2rayV5StaticHostRule.MatcherKind matcher, string domain, IV2rayStaticHostResolveResult resolveResult) -{ - public enum MatcherKind - { - Full, - Subdomain, - Keyword, - Regex - } - - public MatcherKind Matcher { get; } = matcher; - public string Domain { get; } = domain; - public IV2rayStaticHostResolveResult ResolveResult { get; } = resolveResult; - - public Dictionary<string, object> ToJsonObject() - { - var result = new Dictionary<string, object> - { - ["type"] = Enum.GetName(Matcher)!, - ["domain"] = Domain - }; - - foreach (var (key, value) in ResolveResult.GetJsonProperties()) - { - result.Add(key, value); - } - - return result; - } - - public static V2rayV5StaticHostRule IpRule(MatcherKind matcher, string domain, IEnumerable<string> ips) - { - return new V2rayV5StaticHostRule(matcher, domain, new V2rayStaticHostIpResolveResult(ips)); - } - - public static V2rayV5StaticHostRule DomainRule(MatcherKind matcher, string domain, string resolvedDomain) - { - return new V2rayV5StaticHostRule(matcher, domain, new V2rayStaticHostDomainResolveResult(resolvedDomain)); - } - - public static V2rayV5StaticHostRule Parse(string str) - { - var components = str.Trim().Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(); - - if (components.Count <= 1) - { - throw new FormatException("The str only has one or no component."); - } - - var matcher = MatcherKind.Subdomain; - - if (Enum.TryParse<MatcherKind>(components[0], out var m)) - { - matcher = m; - components.RemoveAt(0); - } - - if (components.Count <= 1) - { - throw new FormatException("The str only has one component after remove matcher."); - } - - var domain = components[0]; - components.RemoveAt(0); - - if (components.Count > 1 || IPAddress.TryParse(components[0], out var _)) - { - return new V2rayV5StaticHostRule(matcher, domain, new V2rayStaticHostIpResolveResult(components)); - } - else - { - return new V2rayV5StaticHostRule(matcher, domain, new V2rayStaticHostDomainResolveResult(domain)); - } - } -} |