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 | ddb23b83bd3beda11389455ba6926ac9ce3d481e (patch) | |
tree | 300745f607c2e40aa7525408c9025c6a1e768110 /tools/Crupest.V2ray | |
parent | a178b04f3c2168203e90fa9cd7f92513f8a12bcb (diff) | |
download | crupest-ddb23b83bd3beda11389455ba6926ac9ce3d481e.tar.gz crupest-ddb23b83bd3beda11389455ba6926ac9ce3d481e.tar.bz2 crupest-ddb23b83bd3beda11389455ba6926ac9ce3d481e.zip |
Rename secret tool.
Diffstat (limited to 'tools/Crupest.V2ray')
30 files changed, 0 insertions, 1899 deletions
diff --git a/tools/Crupest.V2ray/.gitignore b/tools/Crupest.V2ray/.gitignore deleted file mode 100644 index ac4d8a4..0000000 --- a/tools/Crupest.V2ray/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.vs -bin -obj -*.pubxml.user -*.csproj.user - -publish diff --git a/tools/Crupest.V2ray/Crupest.V2ray/.gitignore b/tools/Crupest.V2ray/Crupest.V2ray/.gitignore deleted file mode 100644 index c936492..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vmess.txt diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Config.cs b/tools/Crupest.V2ray/Crupest.V2ray/Config.cs deleted file mode 100644 index 2ef18f0..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/Config.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace Crupest.V2ray; - -public record ConfigItem(string Value, int LineNumber); - -public class DictionaryConfig(string configString, List<string>? requiredKeys = null) -{ - private static Dictionary<string, ConfigItem> Parse(string configString, List<string>? requiredKeys = null) - { - var config = new Dictionary<string, ConfigItem>(); - var lines = configString.Split('\n'); - int lineNumber = 1; - - foreach (var line in lines) - { - var trimmedLine = line.Trim(); - if (string.IsNullOrEmpty(trimmedLine) || trimmedLine.StartsWith('#')) - { - lineNumber++; - continue; - } - - var equalIndex = trimmedLine.IndexOf('='); - if (equalIndex == -1) - { - throw new FormatException($"No '=' found in line {lineNumber}."); - } - - config.Add(trimmedLine[..equalIndex].Trim(), new ConfigItem(trimmedLine[(equalIndex + 1)..].Trim(), lineNumber)); - lineNumber++; - } - - if (requiredKeys is not null) - { - foreach (var key in requiredKeys) - { - if (!config.ContainsKey(key)) - { - throw new FormatException($"Required key '{key}' not found in config."); - } - } - } - - return config; - } - - public string ConfigString { get; } = configString; - public List<string>? RequiredKeys { get; } = requiredKeys; - public Dictionary<string, ConfigItem> Config { get; } = Parse(configString); - public ConfigItem GetItemCaseInsensitive(string key) - { - foreach (var (originalKey, value) in Config) - { - if (string.Equals(originalKey, key, StringComparison.OrdinalIgnoreCase)) - { - return value; - } - } - throw new KeyNotFoundException($"Key '{key}' not found in config case-insensitively."); - } -} - -public class ListConfig(string configString) -{ - private static List<ConfigItem> Parse(string configString) - { - var config = new List<ConfigItem>(); - var lines = configString.Split('\n'); - int lineNumber = 1; - - foreach (var line in lines) - { - var l = line; - var beginOfComment = l.IndexOf('#'); - if (beginOfComment >= 0) - { - l = line[..beginOfComment]; - } - l = l.Trim(); - if (!string.IsNullOrEmpty(l)) - { - config.Add(new ConfigItem(l, lineNumber)); - } - lineNumber++; - } - - return config; - } - - public string ConfigString { get; } = configString; - public List<ConfigItem> Config { get; } = Parse(configString); -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Crupest.V2ray.csproj b/tools/Crupest.V2ray/Crupest.V2ray/Crupest.V2ray.csproj deleted file mode 100644 index 0812e4c..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/Crupest.V2ray.csproj +++ /dev/null @@ -1,25 +0,0 @@ -<Project Sdk="Microsoft.NET.Sdk"> - - <PropertyGroup> - <OutputType>Exe</OutputType> - <TargetFramework>net8.0</TargetFramework> - <ImplicitUsings>enable</ImplicitUsings> - <Nullable>enable</Nullable> - </PropertyGroup> - - <ItemGroup> - <None Update="config.json.template"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> - <None Update="proxy.txt"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> - <None Update="vmess.txt"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> - <None Update="hosts.txt"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> - </ItemGroup> - -</Project> diff --git a/tools/Crupest.V2ray/Crupest.V2ray/FileWatcher.cs b/tools/Crupest.V2ray/Crupest.V2ray/FileWatcher.cs deleted file mode 100644 index 547adeb..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/FileWatcher.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Crupest.V2ray; - -public class FileWatcher(string directory, List<string> fileNames) -{ - public string Directory { get; set; } = directory; - public List<string> FileNames { get; set; } = fileNames; - - public delegate void OnChangedHandler(); - public event OnChangedHandler? OnChanged; - - public void Run() - { - var sourceWatcher = new FileSystemWatcher(Directory); - foreach (var fileName in FileNames) - { - sourceWatcher.Filters.Add(fileName); - } - sourceWatcher.NotifyFilter = NotifyFilters.LastWrite; - - while (true) - { - var result = sourceWatcher.WaitForChanged(WatcherChangeTypes.Changed); - OnChanged?.Invoke(); - } - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/GeoDataManager.cs b/tools/Crupest.V2ray/Crupest.V2ray/GeoDataManager.cs deleted file mode 100644 index 3dce3f6..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/GeoDataManager.cs +++ /dev/null @@ -1,307 +0,0 @@ -using System.IO.Compression; - -namespace Crupest.V2ray; - -public interface IGeoSiteEntry -{ - bool IsInclude { get; } - string Value { get; } -} - -public record GeoSiteIncludeEntry(string Value, string ContainingSite) : IGeoSiteEntry -{ - public bool IsInclude => true; -} - -public record GeoSiteRuleEntry(V2rayHostMatcherKind Kind, string Value, List<string> Attributes, string ContainingSite) : IGeoSiteEntry -{ - public bool IsInclude => false; -} - -public record GeoSite(string Name, List<IGeoSiteEntry> Entries) -{ - public static GeoSite Parse(string name, string str) - { - List<IGeoSiteEntry> entries = []; - var listConfig = new ListConfig(str); - foreach (var item in listConfig.Config) - { - var (value, line) = item; - - if (value.StartsWith("include:")) - { - var include = value["include:".Length..].Trim(); - if (include.Length == 0 || include.Contains(' ')) - { - throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Invalid include value."); - } - entries.Add(new GeoSiteIncludeEntry(include, name)); - continue; - } - - var segments = value.Split(':', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); - if (segments.Length > 2) - { - throw new FormatException($"Invalid geo site rule '{name}' in line {line}. More than one ':'."); - } - - V2rayHostMatcherKind 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, - _ => throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Unknown matcher.") - }; - } - else - { - kind = V2rayHostMatcherKind.DomainSuffix; - } - - var domainSegments = segments[^1].Split('@', StringSplitOptions.TrimEntries); - var domain = domainSegments[0]; - if (kind != V2rayHostMatcherKind.DomainRegex && Uri.CheckHostName(domain) != UriHostNameType.Dns) - { - throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Invalid domain."); - } - - List<string> attributes = []; - foreach (var s in domainSegments[1..]) - { - if (s.Length == 0) - { - throw new FormatException($"Invalid geo site rule '{name}' in line {line}. Empty attribute value."); - } - attributes.Add(s); - } - - entries.Add(new GeoSiteRuleEntry(kind, domain, attributes, name)); - } - return new GeoSite(name, entries); - } -} - -public class GeoSiteData(string directory) -{ - private static List<GeoSite> Parse(string directory) - { - var sites = new List<GeoSite>(); - foreach (var path in Directory.GetFileSystemEntries(directory)) - { - var content = File.ReadAllText(path); - sites.Add(GeoSite.Parse(Path.GetFileName(path), content)); - } - return sites; - } - - public string DataDirectory { get; } = directory; - - public List<GeoSite> Sites { get; } = Parse(directory); - - public GeoSite? GetSite(string name) - { - return Sites.Where(s => s.Name == name).FirstOrDefault(); - } - - public List<GeoSiteRuleEntry> GetEntriesRecursive(List<string> sites, - List<V2rayHostMatcherKind>? onlyMatcherKinds = null, List<string>? onlyAttributes = null) - { - List<GeoSiteRuleEntry> entries = []; - HashSet<string> visited = []; - HashSet<V2rayHostMatcherKind>? kinds = onlyMatcherKinds?.ToHashSet(); - - void Visit(string site) - { - if (visited.Contains(site)) - { - return; - } - - visited.Add(site); - var siteData = GetSite(site); - if (siteData == null) - { - return; - } - foreach (var entry in siteData.Entries) - { - if (entry is GeoSiteIncludeEntry includeEntry) - { - Visit(includeEntry.Value); - } - else if (entry is GeoSiteRuleEntry geoSiteRuleEntry) - { - if (kinds != null && !kinds.Contains(geoSiteRuleEntry.Kind)) - { - continue; - } - - if (onlyAttributes != null && !geoSiteRuleEntry.Attributes.Intersect(onlyAttributes).Any()) - { - continue; - } - - entries.Add(geoSiteRuleEntry); - } - } - } - - foreach (var s in sites) - { - Visit(s); - } - - return entries; - } -} - -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 GeoDataManager Instance { get; } = new GeoDataManager(); - - public record GeoDataAsset(string Name, string FileName, string GithubUser, string GithubRepo, string GithubReleaseFileName); - - public GeoDataManager() - { - Assets = - [ - new("geosite", GeoSiteFileName, V2rayGithubOrganization, V2rayGeoSiteGithubRepository, V2rayGeoSiteGithubRepository), - new("geoip", GeoIpFileName, V2rayGithubOrganization, V2rayGeoIpGithubRepository, V2rayGeoIpGithubReleaseFilename), - new("geoip-cn", GeoIpCnFileName, V2rayGithubOrganization, V2rayGeoIpGithubRepository, V2rayGeoIpCnGithubReleaseFilename), - ]; - } - - public List<GeoDataAsset> Assets { get; set; } - - public GeoSiteData? GeoSiteData { get; set; } - - public GeoSiteData GetOrCreateGeoSiteData(bool clean, bool silent) - { - if (GeoSiteData is not null) { return GeoSiteData; } - GeoSiteData = DownloadAndGenerateGeoSiteData(clean, silent); - return GeoSiteData; - } - - private static string GetReleaseFileUrl(string user, string repo, string fileName) - { - return $"https://github.com/{user}/{repo}/releases/latest/download/{fileName}"; - } - - private static void GithubDownloadRelease(HttpClient httpClient, string user, string repo, string fileName, string outputPath) - { - using var responseStream = httpClient.GetStreamAsync(GetReleaseFileUrl(user, repo, fileName)).Result; - using var outputFileStream = File.OpenWrite(outputPath); - responseStream.CopyTo(outputFileStream); - } - - public bool HasAllAssets(string directory, out List<string> missing) - { - missing = []; - foreach (var asset in Assets) - { - var assetPath = Path.Combine(directory, asset.FileName); - if (!File.Exists(assetPath)) - { - missing.Add(asset.Name); - } - } - return missing.Count == 0; - } - - public void Download(string outputDir, bool silent) - { - using var httpClient = new HttpClient(); - - foreach (var asset in Assets) - { - if (!silent) - { - Console.WriteLine($"Downloading {asset.Name}..."); - } - GithubDownloadRelease(httpClient, asset.GithubUser, asset.GithubRepo, asset.GithubReleaseFileName, Path.Combine(outputDir, asset.FileName)); - if (!silent) - { - Console.WriteLine($"Downloaded {asset.Name}!"); - } - } - } - - private static string GetGithubRepositoryArchiveUrl(string user, string repo) - { - return $"https://github.com/{user}/{repo}/archive/refs/heads/master.zip"; - } - - private static void GithubDownloadRepository(HttpClient httpClient, string user, string repo, string outputPath, bool silent) - { - var url = GetGithubRepositoryArchiveUrl(user, repo); - if (!silent) { Console.WriteLine($"Begin to download data from {url} to {outputPath}."); } - using var responseStream = httpClient.GetStreamAsync(url).Result; - using var outputFileStream = File.OpenWrite(outputPath); - responseStream.CopyTo(outputFileStream); - if (!silent) { Console.WriteLine("Succeeded to download."); } - } - - private static void Unzip(string zipPath, string outputPath) - { - using var zip = ZipFile.OpenRead(zipPath) ?? throw new Exception($"Failed to open zip file {zipPath}"); - zip.ExtractToDirectory(outputPath); - } - - private static string DownloadAndExtractGeoDataRepository(bool cleanTempDirIfFailed, bool silent, out string tempDirectoryPath) - { - tempDirectoryPath = ""; - const string zipFileName = "v2ray-geosite-master.zip"; - using var httpClient = new HttpClient(); - var tempDirectory = Directory.CreateTempSubdirectory(Program.Name); - tempDirectoryPath = tempDirectory.FullName; - try - { - var archivePath = Path.Combine(tempDirectoryPath, zipFileName); - var extractPath = Path.Combine(tempDirectoryPath, "repo"); - GithubDownloadRepository(httpClient, V2rayGithubOrganization, V2rayGeoSiteGithubRepository, archivePath, silent); - if (!silent) { Console.WriteLine($"Extract geo data to {extractPath}."); } - Directory.CreateDirectory(extractPath); - Unzip(archivePath, extractPath); - if (!silent) { Console.WriteLine($"Extraction done."); } - return Path.Join(extractPath, "domain-list-community-master"); - } - catch (Exception) - { - if (cleanTempDirIfFailed) - { - Directory.Delete(tempDirectoryPath, true); - } - throw; - } - } - - private static GeoSiteData DownloadAndGenerateGeoSiteData(bool clean, bool silent) - { - var repoDirectory = DownloadAndExtractGeoDataRepository(clean, silent, out var tempDirectoryPath); - try - { - return new GeoSiteData(Path.Join(repoDirectory, "data")); - } - finally - { - if (clean) - { - Directory.Delete(tempDirectoryPath, true); - } - } - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Program.cs b/tools/Crupest.V2ray/Crupest.V2ray/Program.cs deleted file mode 100644 index 0e98861..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/Program.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Reflection; - -namespace Crupest.V2ray; - -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 CrupestV2rayDirectory { get; } = - Environment.GetEnvironmentVariable("CRUPEST_V2RAY_DIR") ?? - Path.GetFullPath(Path.GetDirectoryName( - Assembly.GetExecutingAssembly().Location) ?? throw new Exception("Can't get the path of Crupest.V2ray.")); - - private const string ConfigOutputFileName = "config.json"; - private const string SurgeRuleSetChinaOutputFileName = "ChinaRuleSet.txt"; - private const string SurgeRuleSetGlobalOutputFileName = "GlobalRuleSet.txt"; - - public static void RunV2rayAndWatchConfigChange() - { - var v2rayPath = V2rayController.FindExecutable(CrupestV2rayDirectory, out var isLocal) ?? - throw new Exception("Can't find v2ray executable either in Crupest.V2ray directory or in PATH."); - - string? assetsPath; - if (isLocal) - { - assetsPath = CrupestV2rayDirectory; - var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestV2rayDirectory, 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."); - } - } - else - { - assetsPath = CrupestV2rayDirectory; - var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestV2rayDirectory, out var missing); - if (!assetsComplete) - { - Console.WriteLine($"Missing assets: {string.Join(", ", missing)} in {CrupestV2rayDirectory}. 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); - - V2rayConfig.FromDirectoryAndWriteToFile(CrupestV2rayDirectory, Path.Join(CrupestV2rayDirectory, ConfigOutputFileName)); - v2rayController.Start(); - - configFileWatcher.OnChanged += () => - { - V2rayConfig.FromDirectoryAndWriteToFile(CrupestV2rayDirectory, Path.Join(CrupestV2rayDirectory, ConfigOutputFileName)); - v2rayController.Restart(); - }; - - configFileWatcher.Run(); - } - - public static void Main(string[] args) - { - if (args.Length != 0) - { - if (args.Length != 1) - { - throw new Exception("Invalid command line arguments."); - } - var verb = args[0].ToLower(); - if (verb == "download-geodata" || verb == "dg") - { - GeoDataManager.Instance.Download(CrupestV2rayDirectory, 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), - true, false - ); - return; - } - else if (verb == "generate" || verb == "g") - { - var config = V2rayConfig.FromDirectory(CrupestV2rayDirectory); - Console.Out.WriteLine(config.ToJsonStringV4()); - return; - } - throw new Exception("Invalid command line arguments."); - } - - RunV2rayAndWatchConfigChange(); - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Properties/PublishProfiles/FolderProfile.pubxml b/tools/Crupest.V2ray/Crupest.V2ray/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index bbdd2ad..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/Properties/PublishProfiles/FolderProfile.pubxml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -https://go.microsoft.com/fwlink/?LinkID=208121. ---> -<Project> - <PropertyGroup> - <Configuration>Release</Configuration> - <Platform>Any CPU</Platform> - <PublishDir>bin\Release\net7.0\publish\</PublishDir> - <PublishProtocol>FileSystem</PublishProtocol> - <_TargetId>Folder</_TargetId> - </PropertyGroup> -</Project>
\ No newline at end of file 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/SurgeConfigGenerator.cs b/tools/Crupest.V2ray/Crupest.V2ray/SurgeConfigGenerator.cs deleted file mode 100644 index bd52234..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/SurgeConfigGenerator.cs +++ /dev/null @@ -1,60 +0,0 @@ -namespace Crupest.V2ray; - -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) - { - var ruleType = kind switch - { - V2rayHostMatcherKind.DomainFull => "DOMAIN", - V2rayHostMatcherKind.DomainSuffix => "DOMAIN-SUFFIX", - V2rayHostMatcherKind.DomainKeyword => "DOMAIN-KEYWORD", - V2rayHostMatcherKind.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, - ]; - - public string GenerateChinaRuleSet() - { - var geoSites = ProxyFile.MatcherConfig.Items.Where(i => i.Kind == V2rayHostMatcherKind.GeoSite).Select(i => i.Matcher).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 nonCnRules = GeoSiteData.GetEntriesRecursive(geoSites, DomainMatcherKinds).Where(e => !e.Attributes.Contains("cn")).ToList(); - var domainRules = ProxyFile.MatcherConfig.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)) - ]); - } - - public static SurgeConfigGenerator Create(string proxyFilePath, bool clean, bool silent) - { - var proxyFile = new ProxyFile(proxyFilePath); - var geoSiteData = GeoDataManager.Instance.GetOrCreateGeoSiteData(clean, silent); - return new SurgeConfigGenerator(proxyFile, geoSiteData); - } - - public static void GenerateTo(string proxyFilePath, string cnPath, string globalPath, bool clean, bool silent) - { - var generator = Create(proxyFilePath, clean, silent); - File.WriteAllText(cnPath, generator.GenerateChinaRuleSet()); - if (!silent) Console.WriteLine($"China rule set written to {cnPath}."); - File.WriteAllText(globalPath, generator.GenerateGlobalRuleSet()); - if (!silent) Console.WriteLine($"Global rule set written to {globalPath}."); - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Template.cs b/tools/Crupest.V2ray/Crupest.V2ray/Template.cs deleted file mode 100644 index 9c137b0..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/Template.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text; - -namespace Crupest.V2ray; - -public class Template -{ - private enum ParseState - { - Text, - Dollar, - LeftBracket, - VariableName, - VariableNameFinish, - } - - private interface ITemplateNode - { - string Render(Dictionary<string, string> values); - } - - private class TextNode(string text) : ITemplateNode - { - - public string Text { get; } = text; - - public string Render(Dictionary<string, string> values) - { - return Text; - } - } - - private class VariableNode(string variableName) : ITemplateNode - { - public string VariableName { get; } = variableName; - - public string Render(Dictionary<string, string> values) - { - return values.GetValueOrDefault(VariableName) ?? ""; - } - } - - public Template(string templateString) - { - TemplateString = templateString; - Nodes = Parse(templateString); - VariableNames = Nodes.OfType<VariableNode>().Select(node => node.VariableName).ToList(); - } - - private static List<ITemplateNode> Parse(string templateString) - { - int lineNumber = 1; - int columnNumber = 0; - List<ITemplateNode> nodes = []; - ParseState state = ParseState.Text; - StringBuilder stringBuilder = new(); - - string GetPosition() => $"line {lineNumber} column{columnNumber}"; - - [DoesNotReturn] - void ReportInvalidState(string message) - { - throw new Exception($"Invalid state at {GetPosition()}: {message}"); - } - - [DoesNotReturn] - void ReportInvalidCharacter(char c) - { - throw new FormatException($"Unexpected '{c}' at {GetPosition()}."); - } - - void FinishText() - { - if (state != ParseState.Text) - { - ReportInvalidState($"Can't call FinishText here."); - } - - if (stringBuilder.Length > 0) - { - nodes.Add(new TextNode(stringBuilder.ToString())); - stringBuilder.Clear(); - } - } - - foreach (var c in templateString) - { - if (c == '\n') - { - lineNumber++; - columnNumber = 0; - } - - columnNumber++; - - switch (c) - { - case '$': - if (state == ParseState.Text) - { - FinishText(); - state = ParseState.Dollar; - } - else if (state == ParseState.Dollar) - { - if (stringBuilder.Length > 0) - { - throw new Exception($"Invalid state at {GetPosition()}: when we meet the second '$', text builder should be empty."); - } - stringBuilder.Append(c); - state = ParseState.Text; - } - else - { - throw new FormatException($"Unexpected '$' at {GetPosition()}."); - } - break; - case '{': - if (state == ParseState.Text) - { - stringBuilder.Append(c); - } - else if (state == ParseState.Dollar) - { - state = ParseState.LeftBracket; - } - else - { - throw new Exception($"Unexpected '{{' at {GetPosition()}."); - } - break; - case '}': - if (state == ParseState.Text) - { - stringBuilder.Append(c); - state = ParseState.Text; - } - else if (state == ParseState.VariableName || state == ParseState.VariableNameFinish) - { - nodes.Add(new VariableNode(stringBuilder.ToString())); - stringBuilder.Clear(); - state = ParseState.Text; - } - else - { - ReportInvalidCharacter(c); - } - break; - default: - if (state == ParseState.Dollar) - { - ReportInvalidCharacter(c); - } - - if (char.IsWhiteSpace(c)) - { - if (state == ParseState.LeftBracket || state == ParseState.VariableNameFinish) - { - continue; - } - else if (state == ParseState.Text) - { - stringBuilder.Append(c); - } - else if (state == ParseState.VariableName) - { - state = ParseState.VariableNameFinish; - } - else - { - ReportInvalidCharacter(c); - } - } - else - { - if (state == ParseState.Text) - { - stringBuilder.Append(c); - } - else if (state == ParseState.LeftBracket || state == ParseState.VariableName) - { - stringBuilder.Append(c); - state = ParseState.VariableName; - } - else - { - ReportInvalidCharacter(c); - } - } - break; - } - } - - if (state == ParseState.Text) - { - FinishText(); - } - else - { - throw new FormatException("Unexpected end of template string."); - } - - return nodes; - } - - public string TemplateString { get; } - private List<ITemplateNode> Nodes { get; set; } - public List<string> VariableNames { get; } - - public string Generate(Dictionary<string, string> values, bool allowMissingVariable = false) - { - StringBuilder stringBuilder = new(); - foreach (var node in Nodes) - { - if (node is TextNode textNode) - { - stringBuilder.Append(textNode.Text); - } - else if (node is VariableNode variableNode) - { - var hasValue = values.TryGetValue(variableNode.VariableName, out var value); - if (!hasValue && !allowMissingVariable) - { - throw new Exception($"Variable '{variableNode.VariableName}' is not set."); - } - stringBuilder.Append(hasValue ? value : string.Empty); - } - } - return stringBuilder.ToString(); - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs deleted file mode 100644 index e81a6cb..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Crupest.V2ray; - -public interface IV2rayV4ConfigObject -{ - object ToJsonObjectV4(); -} - -public class V2rayConfig(Template template, List<V2rayProxy> proxies, V2rayRouting router, V2rayHosts? hosts) -{ - private class JsonInterfaceConverter<Interface> : JsonConverter<Interface> - { - public override Interface Read( - ref Utf8JsonReader reader, - Type typeToConvert, - JsonSerializerOptions options) - { - throw new NotImplementedException(); - } - - public override void Write( - Utf8JsonWriter writer, - Interface value, - JsonSerializerOptions options) - { - JsonSerializer.Serialize(writer, value, typeof(object), options); - } - } - - - public const string ConfigTemplateFileName = "config.json.template"; - public const string VmessConfigFileName = "vmess.txt"; - public const string ProxyConfigFileName = "proxy.txt"; - public const string HostsConfigFileName = "hosts.txt"; - - public static List<string> RequiredConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName]; - public static List<string> ConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName, HostsConfigFileName]; - - private const string ProxyAnchor = "PROXY_ANCHOR"; - private const string RoutingAnchor = "ROUTING_ANCHOR"; - private const string HostsAnchor = "HOSTS_ANCHOR"; - - public const string AddCnAttributeToGeositeEnvironmentVariable = "CRUPEST_V@RAY_GEOSITE_USE_CN"; - - private static bool UseCnGeoSite => Environment.GetEnvironmentVariable(AddCnAttributeToGeositeEnvironmentVariable) switch - { - "0" or "false" or "off" or "disable" => false, - _ => true - }; - - 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 string ToJsonStringV4(bool pretty = true) - { - var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - }); - jsonOptions.Converters.Add(new JsonInterfaceConverter<V2rayV4ConfigJsonObjects.IOutboundSettings>()); - jsonOptions.Converters.Add(new JsonInterfaceConverter<V2rayV4ConfigJsonObjects.IOutboundStreamSettings>()); - - var templateValues = new Dictionary<string, string> - { - [ProxyAnchor] = string.Join(',', Proxies.Select(p => JsonSerializer.Serialize(p.ToJsonObjectV4(), jsonOptions))), - [RoutingAnchor] = JsonSerializer.Serialize(Routing.ToJsonObjectV4(), jsonOptions), - [HostsAnchor] = JsonSerializer.Serialize(Hosts.ToJsonObjectV4(), jsonOptions), - }; - - var configString = Template.Generate(templateValues); - - if (pretty) - { - var jsonOptionsPretty = new JsonSerializerOptions(jsonOptions) - { - WriteIndented = true, - }; - return JsonSerializer.Serialize(JsonSerializer.Deserialize<object>(configString, jsonOptionsPretty), jsonOptionsPretty); - } - else - { - return configString; - } - } - - public static V2rayConfig FromFiles(string templatePath, string vmessPath, string proxyPath, string? hostsPath) - { - foreach (var path in new List<string>([templatePath, vmessPath, proxyPath])) - { - if (!File.Exists(path)) - { - throw new FileNotFoundException($"Required config file not found: {path}."); - } - } - - ProxyFile proxyFile = new(proxyPath); - string templateString, vmessString; - string? hostsString; - - string file = ""; - try - { - file = templatePath; - templateString = File.ReadAllText(templatePath); - file = vmessPath; - vmessString = File.ReadAllText(vmessPath); - hostsString = hostsPath is not null ? File.ReadAllText(hostsPath) : null; - } - catch (Exception e) - { - throw new Exception($"Error reading config file {file}.", e); - } - - try - { - file = templatePath; - var template = new Template(templateString); - file = vmessPath; - var vmess = V2rayVmessProxy.CreateFromConfigString(vmessString, "proxy"); - file = proxyPath; - var routing = proxyFile.ToV2rayRouting("proxy", UseCnGeoSite); - file = hostsPath ?? ""; - var hosts = hostsString is not null ? V2rayHosts.CreateFromHostMatcherConfigString(hostsString) : null; - return new V2rayConfig(template, [vmess], routing, hosts); - } - catch (Exception e) - { - throw new Exception($"Error parsing config file {file}.", e); - } - } - - public static V2rayConfig FromDirectory(string directory) - { - return FromFiles( - Path.Join(directory, ConfigTemplateFileName), - Path.Join(directory, VmessConfigFileName), - Path.Join(directory, ProxyConfigFileName), - Path.Join(directory, HostsConfigFileName) - ); - } - - public static void FromDirectoryAndWriteToFile(string directory, string outputPath) - { - var config = FromDirectory(directory); - File.WriteAllText(outputPath, config.ToJsonStringV4()); - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs deleted file mode 100644 index 4656216..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Diagnostics; - -namespace Crupest.V2ray; - -public class V2rayController(string executablePath, string configPath, string? assetPath) -{ - public const string V2rayAssetEnvironmentVariableName = "v2ray.location.asset"; - - public static string? FindExecutable(string contentDir, out bool isLocal, string? executableName = null) - { - isLocal = false; - executableName ??= "v2ray"; - - if (OperatingSystem.IsWindows()) - { - executableName += ".exe"; - } - - var localV2rayPath = Path.Combine(contentDir, executableName); - if (File.Exists(localV2rayPath)) - { - isLocal = true; - return localV2rayPath; - } - - var paths = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator); - if (paths is not null) - { - foreach (var p in paths) - { - var v2rayPath = Path.Combine(p, executableName); - if (File.Exists(v2rayPath)) - { - return v2rayPath; - } - } - } - - return null; - } - - public string ExecutablePath { get; } = executablePath; - public string ConfigPath { get; } = configPath; - public string? AssetPath { get; } = assetPath; - public Process? CurrentProcess { get; private set; } - - private Process CreateProcess() - { - var process = new Process(); - - var startInfo = new ProcessStartInfo - { - FileName = ExecutablePath, - }; - startInfo.ArgumentList.Add("run"); - startInfo.ArgumentList.Add("-c"); - startInfo.ArgumentList.Add(ConfigPath); - if (AssetPath is not null) - { - startInfo.EnvironmentVariables[V2rayAssetEnvironmentVariableName] = AssetPath; - } - - process.StartInfo = startInfo; - process.OutputDataReceived += (_, args) => - { - Console.Out.Write(args.Data); - }; - process.ErrorDataReceived += (_, args) => - { - Console.Error.WriteLine(args.Data); - }; - - return process; - } - - public void Stop() - { - if (CurrentProcess is not null) - { - CurrentProcess.Kill(); - CurrentProcess.Dispose(); - CurrentProcess = null; - Console.WriteLine("V2ray stopped."); - } - } - - public void Start(bool stopOld = false) - { - if (stopOld) Stop(); - - if (CurrentProcess is null) - { - CurrentProcess = CreateProcess(); - CurrentProcess.EnableRaisingEvents = true; - CurrentProcess.Exited += (_, _) => - { - if (CurrentProcess.ExitCode != 0) - { - const string message = "V2ray exited with error."; - Console.Error.WriteLine(message); - throw new Exception(message); - } - }; - CurrentProcess.Start(); - Console.WriteLine("V2ray started."); - } - } - - public void Restart() - { - Start(true); - } -} 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/V2rayV4ConfigJsonObjects.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayV4ConfigJsonObjects.cs deleted file mode 100644 index 672af71..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV4ConfigJsonObjects.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Crupest.V2ray; - -public static class V2rayV4ConfigJsonObjects -{ - public interface IObject; - public interface IOutboundSettings : IObject; - public interface IOutboundStreamSettings : IObject; - - public record WsSettings(string Path, Dictionary<string, string> Headers) : IObject; - public record WsStreamSettings(string Network, string Security, WsSettings WsSettings) : IOutboundStreamSettings; - public record VnextServerUser(string Id, int AlterId, string Security, int Level) : IObject; - public record VnextServer(string Address, int Port, List<VnextServerUser> Users) : IObject; - public record VmessOutboundSettings(List<VnextServer> Vnext) : IOutboundSettings; - public record HttpOutboundUser(string User, string Pass) : IObject; - public record HttpOutboundServer(string Address, int Port, List<HttpOutboundUser> Users) : IObject; - public record HttpOutboundSettings(List<HttpOutboundServer> Servers) : IOutboundSettings; - public record Outbound(string Tag, string Protocol, IOutboundSettings Settings, - IOutboundStreamSettings? StreamSettings) : IObject; - - public record RoutingRule(string DomainMatcher = "mph", string Type = "field", List<string>? Domains = null, List<string>? Ip = null, - string? Port = null, string? SourcePort = null, string? Network = null, List<string>? Source = null, - List<string>? User = null, List<string>? InboundTag = null, List<string>? Protocol = null, string? Attrs = null, - string? OutboundTag = null, string? BalancerTag = null) : IObject; - public record Routing(List<RoutingRule> Rules, string DomainStrategy = "IpOnDemand", string DomainMatcher = "mph") : IObject; -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigJsonObjects.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigJsonObjects.cs deleted file mode 100644 index 56d64ca..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigJsonObjects.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Crupest.V2ray; - -public static class V2rayV5ConfigJsonObjects -{ - public record OutboundObject(string Protocol, object Settings, string Tag, object? StreamSettings) - { - public static OutboundObject VmessViaWs(string tag, string address, int port, string uuid, string path) - { - return new OutboundObject("vmess", new VmessSettings(address, port, uuid), tag, StreamSettingsObject.Ws(path)); - } - - public static OutboundObject Http(string tag, string address, int port) - { - return new OutboundObject("http", new HttpSettingsObject(address, port), tag, null); - } - } - - public record WsSettingsObject(string Path, Dictionary<string, string> Headers); - - public record StreamSettingsObject(string Transport, object TransportSettings, string Security, object SecuritySettings) - { - public static StreamSettingsObject Ws(string path) - { - return new StreamSettingsObject("ws", new WsSettingsObject(path, new()), "tls", new()); - } - } - - public record VmessSettings(string Address, int Port, string Uuid); - - public record HttpSettingsObject(string Address, int Port); -} 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)); - } - } -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/config.json.template b/tools/Crupest.V2ray/Crupest.V2ray/config.json.template deleted file mode 100644 index 686006c..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/config.json.template +++ /dev/null @@ -1,63 +0,0 @@ -{ - "log": { - "loglevel": "warning" - }, - "inbounds": [ - { - "port": 3081, - "listen": "127.0.0.1", - "tag": "socks-inbound", - "protocol": "socks", - "settings": { - "auth": "noauth" - } - }, - { - "port": 3080, - "listen": "127.0.0.1", - "tag": "http-inbound", - "protocol": "http", - "settings": { - "auth": "noauth" - } - } - ], - "outbounds": [ - { - "protocol": "freedom", - "settings": {}, - "tag": "direct" - }, - { - "protocol": "blackhole", - "settings": {}, - "tag": "blocked" - }, - ${PROXY_ANCHOR} - ], - "routing": ${ROUTING_ANCHOR}, - "dns": { - "hosts": ${HOSTS_ANCHOR}, - "servers": [ - "https://doh.pub/dns-query", - "1.1.1.1", - "8.8.8.8", - "localhost" - ] - }, - "policy": { - "levels": { - "0": { - "uplinkOnly": 0, - "downlinkOnly": 0 - } - }, - "system": { - "statsInboundUplink": false, - "statsInboundDownlink": false, - "statsOutboundUplink": false, - "statsOutboundDownlink": false - } - }, - "other": {} -} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template b/tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template deleted file mode 100644 index 01ccf7a..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template +++ /dev/null @@ -1,55 +0,0 @@ -{ - "log": { - "access": { - "type": "Console", - "level": "Info" - } - }, - "dns": { - "nameServer": [{ - "address": "https://doh.pub/dns-query" - }, { - "address": "1.1.1.1" - }, { - "address": "8.8.8.8" - }, { - "address": "localhost" - }], - "staticHosts": ${HOSTS_ANCHOR} - }, - "inbounds": [{ - { - "protocol": "socks", - "port": 2081, - "listen": "127.0.0.1", - "tag": "socks-inbound", - "settings": { - "auth": "noauth" - } - }, - { - "protocol": "http", - "port": 2080, - "listen": "127.0.0.1", - "tag": "http-inbound", - "settings": { - "auth": "noauth" - } - } - }], - "outbounds": [ - { - "protocol": "freedom", - "settings": {}, - "tag": "direct" - }, - { - "protocol": "blackhole", - "settings": {}, - "tag": "blocked" - }, - ${PROXY_ANCHOR} - ], - "router": ${ROUTER_ANCHOR} -} - diff --git a/tools/Crupest.V2ray/Crupest.V2ray/hosts.txt b/tools/Crupest.V2ray/Crupest.V2ray/hosts.txt deleted file mode 100644 index 88d5015..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/hosts.txt +++ /dev/null @@ -1,2 +0,0 @@ -cdn.jsdelivr.net cdn.jsdelivr.net.cdn.cloudflare.net - diff --git a/tools/Crupest.V2ray/Crupest.V2ray/proxy.txt b/tools/Crupest.V2ray/Crupest.V2ray/proxy.txt deleted file mode 100644 index 6273e35..0000000 --- a/tools/Crupest.V2ray/Crupest.V2ray/proxy.txt +++ /dev/null @@ -1,45 +0,0 @@ -GeoSite github -GeoSite google -GeoSite youtube -GeoSite twitter -GeoSite facebook -GeoSite discord -GeoSite reddit -GeoSite twitch -GeoSite onedrive -GeoSite quora -GeoSite telegram -GeoSite imgur -GeoSite stackexchange - -GeoSite duckduckgo -GeoSite wikimedia -GeoSite gitbook -GeoSite gitlab -GeoSite sourceforge -GeoSite creativecommons -GeoSite archive -GeoSite matrix -GeoSite tor - -GeoSite python -GeoSite ruby -GeoSite rust -GeoSite nodejs -GeoSite npmjs -GeoSite qt -GeoSite docker -GeoSite v2ray -GeoSite homebrew - -GeoSite azure -GeoSite jsdelivr -GeoSite fastly -GeoSite heroku -GeoSite bootstrap -GeoSite vercel - -GeoSite ieee -GeoSite sci-hub -GeoSite libgen -GeoSite z-library diff --git a/tools/Crupest.V2ray/CrupestV2ray.sln b/tools/Crupest.V2ray/CrupestV2ray.sln deleted file mode 100644 index 3045b4e..0000000 --- a/tools/Crupest.V2ray/CrupestV2ray.sln +++ /dev/null @@ -1,33 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.7.34024.191 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F4C2CE80-CDF8-4B08-8912-D1F0F14196AD}" - ProjectSection(SolutionItems) = preProject - .gitignore = .gitignore - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Crupest.V2ray", "Crupest.V2ray\Crupest.V2ray.csproj", "{154D49F2-242E-4384-8D34-73774231AA75}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - 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 - 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 -EndGlobal diff --git a/tools/Crupest.V2ray/build-secret.bash b/tools/Crupest.V2ray/build-secret.bash deleted file mode 100755 index bc5c7ee..0000000 --- a/tools/Crupest.V2ray/build-secret.bash +++ /dev/null @@ -1,41 +0,0 @@ -#! /usr/bin/env bash - -set -e - -function print_argument_error_message_and_exit() { - argument_error_message="You must specify exactly one argument, the build target (win-x64 | linux-x64 | osx-x64)." - echo "$argument_error_message" - exit 1 -} - - - -if [[ $# != 1 ]]; then - print_argument_error_message_and_exit -fi - -case "$1" in - win-x64 | linux-x64 | osx-x64) - echo "Build target: $1" - ;; - *) - print_argument_error_message_and_exit - ;; -esac - -secret_dir=$(realpath "$(dirname "$0")") - -echo "Secret dir: ${secret_dir}" - -echo "Check dotnet..." -dotnet --version - -echo "Enter \"secret\" dir..." -pushd "$secret_dir" - -echo "Begin to build..." -dotnet publish Crupest.V2ray -c Release -o "$secret_dir/publish" --sc -r "$1" - -popd - -echo "Finish!" diff --git a/tools/Crupest.V2ray/tools/cru-proxy-edit b/tools/Crupest.V2ray/tools/cru-proxy-edit deleted file mode 100755 index 9ba6cbc..0000000 --- a/tools/Crupest.V2ray/tools/cru-proxy-edit +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env bash - -set -e - -p="$HOME/codes/crupest/tools/Crupest.V2ray/publish/proxy.txt" - -if [[ ! -f "$p" ]]; then - echo "File $p does not exist!" >&2 - exit 1 -fi - -exec vim "$p" diff --git a/tools/Crupest.V2ray/tools/cru-proxy-log b/tools/Crupest.V2ray/tools/cru-proxy-log deleted file mode 100755 index 0ac800c..0000000 --- a/tools/Crupest.V2ray/tools/cru-proxy-log +++ /dev/null @@ -1,13 +0,0 @@ -#! /usr/bin/env bash - -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 "$@" -elif [[ "$(uname)" == "Darwin" ]]; then - exec less "$HOME/.local/state/Crupest.V2ray/log" -else - echo "Not supported on systems other than macOS and Linux now." >&2 - exit 1 -fi diff --git a/tools/Crupest.V2ray/tools/crupest-v2ray.service b/tools/Crupest.V2ray/tools/crupest-v2ray.service deleted file mode 100644 index afe840f..0000000 --- a/tools/Crupest.V2ray/tools/crupest-v2ray.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=crupest v2ray service - -[Service] -ExecStart=%h/.local/bin/Crupest.V2ray - -[Install] -WantedBy=default.target diff --git a/tools/Crupest.V2ray/tools/life.crupest.v2ray.plist b/tools/Crupest.V2ray/tools/life.crupest.v2ray.plist deleted file mode 100644 index 4569ae2..0000000 --- a/tools/Crupest.V2ray/tools/life.crupest.v2ray.plist +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> -<plist version="1.0"> -<dict> - <key>Label</key> - <string>life.crupest.v2ray</string> - <key>ProgramArguments</key> - <array> - <string>/Users/crupest/.local/bin/Crupest.V2ray</string> - </array> - <key>KeepAlive</key> - <true/> - <key>StandardOutPath</key> - <string>/Users/crupest/.local/state/Crupest.V2ray/log</string> - <key>StandardErrorPath</key> - <string>/Users/crupest/.local/state/Crupest.V2ray/error</string> -</dict> -</plist> |