diff options
author | Yuqian Yang <crupest@crupest.life> | 2025-02-28 23:13:39 +0800 |
---|---|---|
committer | Yuqian Yang <crupest@crupest.life> | 2025-02-28 23:13:39 +0800 |
commit | dc1f0c4c0096013799416664894c5194dc7e1f52 (patch) | |
tree | 2f5d235f778cd720f4c39ec3e56b77ba6d99f375 /tools | |
parent | 7299d424d90b1effb6db69e3476ddd5af72eeba4 (diff) | |
download | crupest-dc1f0c4c0096013799416664894c5194dc7e1f52.tar.gz crupest-dc1f0c4c0096013799416664894c5194dc7e1f52.tar.bz2 crupest-dc1f0c4c0096013799416664894c5194dc7e1f52.zip |
chore(store): move everything to store.
Diffstat (limited to 'tools')
38 files changed, 0 insertions, 2339 deletions
diff --git a/tools/Crupest.SecretTool/.gitignore b/tools/Crupest.SecretTool/.gitignore deleted file mode 100644 index ac4d8a4..0000000 --- a/tools/Crupest.SecretTool/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.vs -bin -obj -*.pubxml.user -*.csproj.user - -publish diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool.sln b/tools/Crupest.SecretTool/Crupest.SecretTool.sln deleted file mode 100644 index fde4347..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool.sln +++ /dev/null @@ -1,30 +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("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Crupest.SecretTool", "Crupest.SecretTool\Crupest.SecretTool.csproj", "{D6335AE4-FD22-49CD-9624-37371F3B4F82}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {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(ExtensibilityGlobals) = postSolution - SolutionGuid = {B1E8FD9C-9157-4F4E-8265-4B37F30EEC5E} - EndGlobalSection -EndGlobal diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore b/tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore deleted file mode 100644 index c936492..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/.gitignore +++ /dev/null @@ -1 +0,0 @@ -vmess.txt diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs deleted file mode 100644 index ff58551..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Config.cs +++ /dev/null @@ -1,95 +0,0 @@ -namespace Crupest.SecretTool; - -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 l = line; - var beginOfComment = l.IndexOf('#'); - if (beginOfComment >= 0) - { - l = line[..beginOfComment]; - } - l = l.Trim(); - if (!string.IsNullOrEmpty(l)) - { - 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)); - } - - 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.SecretTool/Crupest.SecretTool/Controller.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs deleted file mode 100644 index 0803b01..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Controller.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Diagnostics; - -namespace Crupest.SecretTool; - -public class Controller(string executablePath, string configPath, string? assetPath) -{ - public const string ToolAssetEnvironmentVariableName = "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 localToolPath = Path.Combine(contentDir, executableName); - if (File.Exists(localToolPath)) - { - isLocal = true; - return localToolPath; - } - - var paths = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator); - if (paths is not null) - { - foreach (var p in paths) - { - var toolPath = Path.Combine(p, executableName); - if (File.Exists(toolPath)) - { - return toolPath; - } - } - } - - 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[ToolAssetEnvironmentVariableName] = 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.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj b/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj deleted file mode 100644 index 2502e74..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj +++ /dev/null @@ -1,34 +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> - <None Update="sing-config.json.template"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> - <None Update="sing-inbounds-mobile.json"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> - <None Update="sing-inbounds-pc.json"> - <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> - </None> - </ItemGroup> - -</Project> diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs deleted file mode 100644 index 26e9231..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/FileWatcher.cs +++ /dev/null @@ -1,26 +0,0 @@ -namespace Crupest.SecretTool; - -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 | WatcherChangeTypes.Created); - OnChanged?.Invoke(); - } - } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs deleted file mode 100644 index 8f4c171..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/GeoDataManager.cs +++ /dev/null @@ -1,324 +0,0 @@ -using System.IO.Compression; - -namespace Crupest.SecretTool; - -public interface IGeoSiteEntry -{ - bool IsInclude { get; } - string Value { get; } -} - -public record GeoSiteIncludeEntry(string Value, string ContainingSite) : IGeoSiteEntry -{ - public bool IsInclude => true; -} - -public record GeoSiteRuleEntry(HostMatchKind Kind, string Value, List<string> Attributes, string ContainingSite) : IGeoSiteEntry -{ - public bool IsInclude => false; - - public RoutingRuleMatcher GetRoutingRuleMatcher() => new(Kind, Value); -} - -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 ':'."); - } - - HostMatchKind kind; - if (segments.Length == 2) - { - kind = segments[0] switch - { - "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 = HostMatchKind.DomainSuffix; - } - - var domainSegments = segments[^1].Split('@', StringSplitOptions.TrimEntries); - var domain = domainSegments[0]; - if (kind != HostMatchKind.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<HostMatchKind>? onlyMatcherKinds = null, List<string>? onlyAttributes = null) - { - List<GeoSiteRuleEntry> entries = []; - HashSet<string> visited = []; - HashSet<HostMatchKind>? 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 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(); - - public record GeoDataAsset(string Name, string FileName, string GithubUser, string GithubRepo, string GithubReleaseFileName); - - public GeoDataManager() - { - Assets = - [ - new("geosite", GeoSiteFileName, ToolGithub.Organization, ToolGithub.GeoSiteRepository, ToolGithub.GeoSiteReleaseFilename), - new("geoip", GeoIpFileName, ToolGithub.Organization, ToolGithub.GeoIpRepository, ToolGithub.GeoIpReleaseFilename), - new("geoip-cn", GeoIpCnFileName, ToolGithub.Organization, ToolGithub.GeoIpRepository, ToolGithub.GeoIpCnReleaseFilename), - ]; - } - - 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, bool silent) - { - var url = GetReleaseFileUrl(user, repo, fileName); - if (!silent) Console.WriteLine($"Downloading {url} to {outputPath}"); - using var responseStream = httpClient.GetStreamAsync(url).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), silent); - if (!silent) - { - Console.WriteLine($"Downloaded {asset.Name}!"); - } - } - - if (!File.Exists(Program.RestartLabelFilePath)) - { - File.Create(Program.RestartLabelFilePath); - } - else - { - File.SetLastWriteTime(Program.RestartLabelFilePath, DateTime.Now); - } - } - - 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, ToolGithub.Organization, ToolGithub.GeoSiteRepository, 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.SecretTool/Crupest.SecretTool/HostMatchConfig.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs deleted file mode 100644 index 858333d..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/HostMatchConfig.cs +++ /dev/null @@ -1,123 +0,0 @@ -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 static List<HostMatchKind> SupportedInSingRouteMatchKinds { get; } = [..DomainMatchKinds, HostMatchKind.Ip]; - - public static bool IsSupportedInSingRoute(this HostMatchKind kind) => SupportedInSingRouteMatchKinds.Contains(kind); -} - -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."); - if (segments[0] == matchKindName) - { - hasExplicitMatchKind = true; - - 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; } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs deleted file mode 100644 index 18b1ac0..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System.Reflection; - -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.SecretTool."); - - 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.SecretTool.")); - - private const string ConfigOutputFileName = "config.json"; - private const string SurgeRuleSetChinaOutputFileName = "ChinaRuleSet.txt"; - private const string SurgeRuleSetGlobalOutputFileName = "GlobalRuleSet.txt"; - - public const string RestartLabelFileName = "restart.label"; - public static string RestartLabelFilePath { get; } = Path.Combine(CrupestSecretToolDirectory, RestartLabelFileName); - - public static void RunToolAndWatchConfigChange() - { - 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 = CrupestSecretToolDirectory; - var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestSecretToolDirectory, out var missing); - if (!assetsComplete) - { - throw new Exception($"Missing assets: {string.Join(", ", missing)} in {CrupestSecretToolDirectory}. This v2ray is local. So only use assets in Crupest.SecretTool directory."); - } - } - else - { - assetsPath = CrupestSecretToolDirectory; - var assetsComplete = GeoDataManager.Instance.HasAllAssets(CrupestSecretToolDirectory, out var missing); - if (!assetsComplete) - { - Console.WriteLine($"Missing assets: {string.Join(", ", missing)} in {CrupestSecretToolDirectory}. This v2ray is global. So fallback to its own assets."); - assetsPath = null; - } - } - - var controller = new Controller(executablePath, Path.Combine(CrupestSecretToolDirectory, ConfigOutputFileName), assetsPath); - var configFileWatcher = new FileWatcher(CrupestSecretToolDirectory, - [.. ToolConfig.ConfigFileNames, RestartLabelFileName]); - - ToolConfig.FromDirectoryAndWriteToFile(CrupestSecretToolDirectory, Path.Join(CrupestSecretToolDirectory, ConfigOutputFileName)); - controller.Start(); - - configFileWatcher.OnChanged += () => - { - ToolConfig.FromDirectoryAndWriteToFile(CrupestSecretToolDirectory, Path.Join(CrupestSecretToolDirectory, ConfigOutputFileName)); - controller.Restart(); - }; - - configFileWatcher.Run(); - } - - public static void Main(string[] args) - { - if (args.Length != 0) - { - var verb = args[0].ToLower(); - if (verb == "download-geodata" || verb == "dg") - { - if (args.Length != 1) - { - throw new Exception("Invalid command line arguments. download-geodata requires no arguments."); - } - GeoDataManager.Instance.Download(CrupestSecretToolDirectory, false); - return; - } - else if (verb == "generate-surge-rule-set" || verb == "gsr") - { - if (args.Length != 1) - { - throw new Exception("Invalid command line arguments. download-geodata requires no arguments."); - } - SurgeConfigGenerator.GenerateTo( - CrupestSecretToolDirectory, - Path.Join(CrupestSecretToolDirectory, SurgeRuleSetChinaOutputFileName), - Path.Join(CrupestSecretToolDirectory, SurgeRuleSetGlobalOutputFileName), - true, true - ); - return; - } - else if (verb == "generate-sing-config" || verb == "gs") - { - if (args.Length != 2 || args[1].ToLower() is not ("pc" or "mobile")) - { - throw new Exception("Invalid command line arguments. generate-sing-config requires 1 argument. The argument must be either 'pc' or 'mobile'."); - } - - var config = SingToolConfig.FromDirectory(CrupestSecretToolDirectory, args[1].ToLower() == "mobile", true, true); - Console.Out.WriteLine(config.ToSingConfigString()); - return; - } - else if (verb == "generate" || verb == "g") - { - var config = ToolConfig.FromDirectory(CrupestSecretToolDirectory); - Console.Out.WriteLine(config.ToJsonStringV4()); - return; - } - throw new Exception("Invalid command line arguments."); - } - - RunToolAndWatchConfigChange(); - } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml b/tools/Crupest.SecretTool/Crupest.SecretTool/Properties/PublishProfiles/FolderProfile.pubxml deleted file mode 100644 index 5fca454..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/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\net8.0\publish\</PublishDir> - <PublishProtocol>FileSystem</PublishProtocol> - <_TargetId>Folder</_TargetId> - </PropertyGroup> -</Project>
\ No newline at end of file diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs deleted file mode 100644 index d2703ba..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs +++ /dev/null @@ -1,76 +0,0 @@ -namespace Crupest.SecretTool; - -public abstract class Proxy(string tag) : IV4ConfigObject, ISingConfigObject -{ - public string Tag { get; set; } = tag; - - public abstract V4ConfigJsonObjects.Outbound ToJsonObjectV4(); - public abstract SingConfigJsonObjects.OutboundBase ToJsonObjectSing(); - - object IV4ConfigObject.ToJsonObjectV4() - { - return ToJsonObjectV4(); - } - - object ISingConfigObject.ToJsonObjectSing() - { - return ToJsonObjectSing(); - } -} - -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 SingConfigJsonObjects.OutboundBase ToJsonObjectSing() - { - throw new NotImplementedException("Http proxy is not supported in sing now."); - } - - 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 SingConfigJsonObjects.OutboundBase ToJsonObjectSing() - { - return new SingConfigJsonObjects.VmessOutbound(Tag, Host, Port, UserId, - Transport: new SingConfigJsonObjects.V2rayWebsocketTransport(Path, new Dictionary<string, string> { { "Host", Host } }), - Tls: new SingConfigJsonObjects.OutboundTls(true)); - } - - 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/ProxyFile.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/ProxyFile.cs deleted file mode 100644 index 81698a3..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/ProxyFile.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Crupest.SecretTool;
-
-public class ProxyFile : HostMatchConfigFile
-{
- public ProxyFile(string path) : base(path, [.. Enum.GetValues<HostMatchKind>()], maxComponentCount: 0)
- {
- RoutingRuleMatchers = Config.Items.Select(i => new RoutingRuleMatcher(i.Kind, i.MatchString)).ToList();
- }
-
- public List<RoutingRuleMatcher> RoutingRuleMatchers { get; }
-
- public List<RoutingRuleMatcher> GetChinaRulesByGeoSite(GeoSiteData geoSiteData)
- {
- var geoSites = RoutingRuleMatchers.Where(m => m.MatchKind == HostMatchKind.GeoSite).Select(i => i.MatchString).ToList();
- return geoSiteData.GetEntriesRecursive(geoSites, HostMatchKindExtensions.DomainMatchKinds, ["cn"]).Select(e => e.GetRoutingRuleMatcher()).ToList();
- }
-
- public List<RoutingRuleMatcher> GetRulesFlattenGeoSite(GeoSiteData geoSiteData, bool noCn = false)
- {
- var geoSites = RoutingRuleMatchers.Where(m => m.MatchKind == HostMatchKind.GeoSite).Select(i => i.MatchString).ToList();
- var flattenGeoSiteRules = geoSiteData.GetEntriesRecursive(geoSites, HostMatchKindExtensions.DomainMatchKinds)
- .Where(e => !noCn || !e.Attributes.Contains("cn"))
- .Select(e => e.GetRoutingRuleMatcher())
- .ToList();
- var otherRules = RoutingRuleMatchers.Where(m => m.MatchKind != HostMatchKind.GeoSite).ToList();
- return [
- ..flattenGeoSiteRules,
- ..otherRules
- ];
- }
-}
diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs deleted file mode 100644 index fdf1b93..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs +++ /dev/null @@ -1,155 +0,0 @@ -namespace Crupest.SecretTool; - -public record RoutingRuleMatcher(HostMatchKind MatchKind, string MatchString) -{ - public RoutingRule ToRoutingRule(string OutboundTag) => new(MatchKind, MatchString, OutboundTag); -} - -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 string ToolConfigStringSing => MatchKind.IsSupportedInSingRoute() ? MatchString : throw new ArgumentException("Unsupported matcher kind for sing."); - - 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 SingConfigJsonObjects.RouteRule ListToJsonObjectSing(List<RoutingRule> rules) - { - if (rules.Count == 0) - { - throw new ArgumentException("Rule list is empty."); - } - - var outboundTag = rules[0].OutboundTag; - - if (rules.Any(r => !r.MatchKind.IsSupportedInSingRoute())) - { - throw new ArgumentException("Rules must have matcher kinds supported in sing."); - } - - if (rules.Any(r => r.OutboundTag != outboundTag)) - { - throw new ArgumentException("Rules must have the same outbound tag."); - } - - return new SingConfigJsonObjects.RouteRule(Outbound: outboundTag, - Domain: rules.Where(r => r.MatchKind == HostMatchKind.DomainFull).Select(r => r.ToolConfigStringSing).ToList(), - DomainSuffix: rules.Where(r => r.MatchKind == HostMatchKind.DomainSuffix).Select(r => r.ToolConfigStringSing).ToList(), - DomainKeyword: rules.Where(r => r.MatchKind == HostMatchKind.DomainKeyword).Select(r => r.ToolConfigStringSing).ToList(), - DomainRegex: rules.Where(r => r.MatchKind == HostMatchKind.DomainRegex).Select(r => r.ToolConfigStringSing).ToList(), - IpCidr: rules.Where(r => r.MatchKind == HostMatchKind.Ip).Select(r => r.ToolConfigStringSing).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 RoutingRuleMatcher GetMatcher() => new(MatchKind, MatchString); - - public V4ConfigJsonObjects.RoutingRule ToJsonObjectV4() => ListToJsonObject([this]); - - object IV4ConfigObject.ToJsonObjectV4() => ToJsonObjectV4(); -} - -public record Routing(List<RoutingRule> Rules) : IV4ConfigObject, ISingConfigObject -{ - public List<RoutingRule> CreateGeositeCnDirectRules() - { - return Rules.Where(r => r.MatchKind is HostMatchKind.GeoSite) - .Select(r => r.CloneGeositeWithCnAttribute("direct")).ToList(); - } - - public SingConfigJsonObjects.Route ToJsonObjectSing() - { - List<SingConfigJsonObjects.RouteRule> ruleJsonObjects = [ new SingConfigJsonObjects.RouteRule(Outbound: "dns-out", Protocol: "dns")]; - ruleJsonObjects.AddRange(RoutingRule.GroupByOutboundTag(Rules).Values.Select(RoutingRule.ListToJsonObjectSing)); - return new SingConfigJsonObjects.Route(ruleJsonObjects); - } - - public V4ConfigJsonObjects.Routing ToJsonObjectV4(string domainStrategy = "IpOnDemand", 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, domainStrategy); - } - - object IV4ConfigObject.ToJsonObjectV4() => ToJsonObjectV4(); - - object ISingConfigObject.ToJsonObjectSing() => ToJsonObjectSing(); - - public static Routing FromProxyFile(ProxyFile proxyFile, string outboundTag) - { - return new Routing( - proxyFile.RoutingRuleMatchers.Select(m => m.ToRoutingRule(outboundTag)).ToList()); - } - - public static Routing FromProxyFileForSing(ProxyFile proxyFile, GeoSiteData geoSiteData, string outboundTag, string? directCnOutboundTag = null) - { - List<RoutingRule> rules = []; - - if (directCnOutboundTag is not null) - { - rules.AddRange(proxyFile.GetChinaRulesByGeoSite(geoSiteData).Select(m => m.ToRoutingRule(directCnOutboundTag)).ToList()); - } - - rules.AddRange(proxyFile.GetRulesFlattenGeoSite(geoSiteData).Where(m => m.MatchKind.IsSupportedInSingRoute()).Select(m => m.ToRoutingRule(outboundTag)).ToList()); - - return new Routing( - rules - ); - } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs deleted file mode 100644 index 56b5563..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Crupest.SecretTool;
-
-public static class SingConfigJsonObjects
-{
- public interface IObject;
-
- public record OutboundTls(bool Enabled);
- public record V2rayTransportBase(string Type);
- public record V2rayWebsocketTransport(string Path, Dictionary<string, string>? Headers = null) : V2rayTransportBase("ws");
- public record OutboundBase(string Tag, string Type) : IObject;
- public record VmessOutbound(string Tag, string Server, int ServerPort, string Uuid, string Security = "auto",
- V2rayTransportBase? Transport = null, OutboundTls? Tls = null): OutboundBase(Tag, "vmess");
-
- public record RouteRule(List<string>? Domain = null, List<string>? DomainSuffix = null, List<string>? DomainKeyword = null,
- List<string>? DomainRegex = null, List<string>? IpCidr = null, List<string>? SourceIpCidr = null, string? Protocol = null,
- List<int>? Port = null, List<int>? SourcePort = null, List<string>? PortRange = null, List<string>? SourcePortRange = null,
- string? Network = null, List<string>? Inbound = null, string? Outbound = null) : IObject;
-
- public record Route(List<RouteRule> Rules) : IObject;
-}
diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs deleted file mode 100644 index b112e1c..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/StaticHosts.cs +++ /dev/null @@ -1,40 +0,0 @@ -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.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs deleted file mode 100644 index 8a57c9f..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/SurgeConfigGenerator.cs +++ /dev/null @@ -1,56 +0,0 @@ -namespace Crupest.SecretTool; - -public class SurgeConfigGenerator(ProxyFile proxyFile, GeoSiteData geoData) -{ - public ProxyFile ProxyFile => proxyFile; - public GeoSiteData GeoData => geoData; - - private static string ToSurgeRuleString(HostMatchKind kind, string value) - { - var ruleType = kind switch - { - 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}"; - } - - public static string GenerateSurgeRuleSetString(List<RoutingRuleMatcher> rules) - { - return string.Join('\n', rules.Select(r => ToSurgeRuleString(r.MatchKind, r.MatchString))); - } - - public string GenerateChinaRuleSet() - { - return GenerateSurgeRuleSetString(proxyFile.GetChinaRulesByGeoSite(GeoData)); - } - - public string GenerateGlobalRuleSet() - { - return GenerateSurgeRuleSetString(proxyFile.GetRulesFlattenGeoSite(geoData, true)); - } - - public static void GenerateTo(ProxyFile proxyFile, GeoSiteData geoSiteData, string cnPath, string globalPath, bool silent) - { - var generator = new SurgeConfigGenerator(proxyFile, geoSiteData); - 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}."); - } - - public static void GenerateTo(string directory, string cnPath, string globalPath, bool clean, bool silent) - { - var geoSiteData = GeoDataManager.Instance.GetOrCreateGeoSiteData(clean, silent); - var proxyFile = new ProxyFile(Path.Combine(directory, ToolConfig.ProxyConfigFileName)); - var generator = new SurgeConfigGenerator(proxyFile, geoSiteData); - 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.SecretTool/Crupest.SecretTool/Template.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs deleted file mode 100644 index 1fe91b1..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Template.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text; - -namespace Crupest.SecretTool; - -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.SecretTool/Crupest.SecretTool/ToolConfig.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs deleted file mode 100644 index 809fba1..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs +++ /dev/null @@ -1,271 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Crupest.SecretTool; - -public interface IV4ConfigObject -{ - object ToJsonObjectV4(); -} - -public interface ISingConfigObject -{ - object ToJsonObjectSing(); -} - -public class ToolConfigBase(Template template, List<Proxy> proxies, Routing router) -{ - protected 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 VmessConfigFileName = "vmess.txt"; - public const string ProxyConfigFileName = "proxy.txt"; - - public Template Template { get; set; } = template; - public List<Proxy> Proxies { get; set; } = proxies; - public Routing Routing { get; set; } = router; -} - -public class ToolConfig(Template template, List<Proxy> proxies, Routing router, StaticHosts? hosts) : ToolConfigBase(template, proxies, router) -{ - public const string ConfigTemplateFileName = "config.json.template"; - 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_V2RAY_GEOSITE_USE_CN"; - - private static bool UseCnGeoSite => Environment.GetEnvironmentVariable(AddCnAttributeToGeositeEnvironmentVariable) switch - { - "0" or "false" or "off" or "disable" => false, - _ => true - }; - - public StaticHosts Hosts { get; set; } = hosts is null ? new StaticHosts([]) : hosts; - - public string ToJsonStringV4(string domainStrategy = "IpOnDemand", bool directGeositeCn = true, bool pretty = true) - { - var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - }); - // 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> - { - [ProxyAnchor] = string.Join(',', Proxies.Select(p => JsonSerializer.Serialize(p.ToJsonObjectV4(), jsonOptions))), - [RoutingAnchor] = JsonSerializer.Serialize(Routing.ToJsonObjectV4(domainStrategy, directGeositeCn), 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 ToolConfig 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 = VmessProxy.CreateFromConfigString(vmessString, "proxy"); - file = proxyPath; - var routing = Routing.FromProxyFile(proxyFile, "proxy"); - file = hostsPath ?? ""; - var hosts = hostsString is not null ? StaticHosts.CreateFromHostMatchConfigString(hostsString) : null; - return new ToolConfig(template, [vmess], routing, hosts); - } - catch (Exception e) - { - throw new Exception($"Error parsing config file {file}.", e); - } - } - - public static ToolConfig 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()); - } -} - -public class SingToolConfig(Template template, List<Proxy> proxies, Routing router, string inboundsString) : ToolConfigBase(template, proxies, router) -{ - - public const string ConfigTemplateFileName = "sing-config.json.template"; - public const string ConfigInboundsPcFileName = "sing-inbounds-pc.json"; - public const string ConfigInboundsMobileFileName = "sing-inbounds-mobile.json"; - - public static List<string> RequiredConfigFileNames { get; } = [ConfigTemplateFileName, VmessConfigFileName, ProxyConfigFileName, ConfigInboundsMobileFileName, ConfigInboundsPcFileName]; - - private const string ProxyAnchor = "PROXY_ANCHOR"; - private const string RouteAnchor = "ROUTE_ANCHOR"; - private const string InboundsAnchor = "INBOUNDS_ANCHOR"; - - public string InboundsString { get; } = inboundsString; - - public string ToSingConfigString(bool pretty = true) - { - var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions - { - PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower, - DictionaryKeyPolicy = JsonNamingPolicy.SnakeCaseLower, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - }); - // TODO: Make interface converter generic. - jsonOptions.Converters.Add(new JsonInterfaceConverter<SingConfigJsonObjects.OutboundBase>()); - jsonOptions.Converters.Add(new JsonInterfaceConverter<SingConfigJsonObjects.V2rayTransportBase>()); - - var templateValues = new Dictionary<string, string> - { - [ProxyAnchor] = string.Join(',', Proxies.Select(p => JsonSerializer.Serialize(p.ToJsonObjectSing(), jsonOptions))), - [RouteAnchor] = JsonSerializer.Serialize(Routing.ToJsonObjectSing(), jsonOptions), - [InboundsAnchor] = InboundsString - }; - - 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 SingToolConfig FromFiles(string templatePath, string vmessPath, string proxyPath, string inboundsPath, bool clean, bool silent) - { - foreach (var path in new List<string>([templatePath, vmessPath, proxyPath, inboundsPath])) - { - if (!File.Exists(path)) - { - throw new FileNotFoundException($"Required config file not found: {path}."); - } - } - - var geoSiteData = GeoDataManager.Instance.GetOrCreateGeoSiteData(clean, silent); - - ProxyFile proxyFile = new(proxyPath); - string templateString, vmessString, inboundsString; - - string file = ""; - try - { - file = templatePath; - templateString = File.ReadAllText(templatePath); - file = vmessPath; - vmessString = File.ReadAllText(vmessPath); - file = inboundsPath; - inboundsString = File.ReadAllText(inboundsPath); - } - catch (Exception e) - { - throw new Exception($"Error reading config file {file}.", e); - } - - try - { - file = templatePath; - var template = new Template(templateString); - file = vmessPath; - var vmess = VmessProxy.CreateFromConfigString(vmessString, "proxy-out"); - file = proxyPath; - var routing = Routing.FromProxyFileForSing(proxyFile, geoSiteData, "proxy-out", "direct-out"); - return new SingToolConfig(template, [vmess], routing, inboundsString); - } - catch (Exception e) - { - throw new Exception($"Error parsing config file {file}.", e); - } - } - - public static SingToolConfig FromDirectory(string directory, bool isMobile, bool clean, bool silent) - { - return FromFiles( - Path.Join(directory, ConfigTemplateFileName), - Path.Join(directory, VmessConfigFileName), - Path.Join(directory, ProxyConfigFileName), - isMobile ? Path.Join(directory, ConfigInboundsMobileFileName) : Path.Join(directory, ConfigInboundsPcFileName), - clean, silent - ); - } -} diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs deleted file mode 100644 index 3e81dbb..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/V4ConfigJsonObjects.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Crupest.SecretTool; - -public static class V4ConfigJsonObjects -{ - 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.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs deleted file mode 100644 index a50e9be..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/V5ConfigJsonObjects.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Crupest.SecretTool; - -public static class V5ConfigJsonObjects -{ - 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.SecretTool/Crupest.SecretTool/config.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template deleted file mode 100644 index 424e996..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/config.json.template +++ /dev/null @@ -1,63 +0,0 @@ -{ - "log": { - "loglevel": "warning" - }, - "inbounds": [ - { - "port": 2081, - "listen": "127.0.0.1", - "tag": "socks-inbound", - "protocol": "socks", - "settings": { - "auth": "noauth" - } - }, - { - "port": 2080, - "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.SecretTool/Crupest.SecretTool/config.v5.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/config.v5.json.template deleted file mode 100644 index 01ccf7a..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/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.SecretTool/Crupest.SecretTool/hosts.txt b/tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt deleted file mode 100644 index 88d5015..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/hosts.txt +++ /dev/null @@ -1,2 +0,0 @@ -cdn.jsdelivr.net cdn.jsdelivr.net.cdn.cloudflare.net - diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt b/tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt deleted file mode 100644 index 39800f9..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/proxy.txt +++ /dev/null @@ -1,50 +0,0 @@ -GeoSite microsoft -GeoSite google -GeoSite youtube -GeoSite x -GeoSite facebook -GeoSite discord -GeoSite reddit -GeoSite twitch -GeoSite quora -GeoSite telegram -GeoSite imgur -GeoSite stackexchange -GeoSite medium - -GeoSite duckduckgo -GeoSite wikimedia -GeoSite gitbook -GeoSite github -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 akamai -GeoSite aws -GeoSite jsdelivr -GeoSite fastly -GeoSite heroku -GeoSite bootstrap -GeoSite vercel - -GeoSite ieee -GeoSite sci-hub -GeoSite libgen -GeoSite z-library - -sagernet.org diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template deleted file mode 100644 index d7e55a0..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template +++ /dev/null @@ -1,45 +0,0 @@ -{
- "log": {
- "disabled": false,
- "level": "info",
- "timestamp": true
- },
- "dns": {
- "servers": [
- {
- "tag": "ali-doh",
- "address": "https://dns.alidns.com/dns-query",
- "address_resolver": "ali"
- },
- {
- "tag": "ali",
- "address": "223.5.5.5"
- },
- {
- "tag": "cloudflare",
- "address": "1.1.1.1"
- },
- {
- "tag": "google",
- "address": "8.8.8.8"
- }
- ]
- },
- "inbounds": ${INBOUNDS_ANCHOR},
- "outbounds": [
- {
- "type": "direct",
- "tag": "direct-out"
- },
- {
- "type": "block",
- "tag": "block-out"
- },
- {
- "tag": "dns-out",
- "type": "dns"
- },
- ${PROXY_ANCHOR}
- ],
- "route": ${ROUTE_ANCHOR}
-}
diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json deleted file mode 100644 index 5038c40..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json +++ /dev/null @@ -1,11 +0,0 @@ -[
- {
- "tag": "tun-in",
- "type": "tun",
- "auto_route": true,
- "strict_route": true,
- "address": [ "172.23.0.1/30", "fdfe:acbd:9876::1/126"],
- "sniff": true,
- "sniff_override_destination": true
- }
-]
diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json deleted file mode 100644 index 956d751..0000000 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json +++ /dev/null @@ -1,14 +0,0 @@ -[
- {
- "tag": "http-in",
- "type": "http",
- "listen": "127.0.0.1",
- "listen_port": 3080
- },
- {
- "tag": "socks-in",
- "type": "socks",
- "listen": "127.0.0.1",
- "listen_port": 3081
- }
-]
\ No newline at end of file diff --git a/tools/Crupest.SecretTool/build-secret.bash b/tools/Crupest.SecretTool/build-secret.bash deleted file mode 100755 index 8878049..0000000 --- a/tools/Crupest.SecretTool/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.SecretTool -c Release -o "$secret_dir/publish" --sc -r "$1" - -popd - -echo "Finish!" diff --git a/tools/Crupest.SecretTool/build-secret.ps1 b/tools/Crupest.SecretTool/build-secret.ps1 deleted file mode 100644 index 8aa7987..0000000 --- a/tools/Crupest.SecretTool/build-secret.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -if ($args.Count -ne 1 || $args[0] -notmatch "^win-x64|linux-x64|osx-x64$") -{ - Write-Error "You must specify exactly one argument, the build target (win-x64 | linux-x64 | osx-x64)." - exit 1 -} - -Write-Output "Secret dir: $PSScriptRoot" - -Write-Output "Check dotnet..." -dotnet --version -if ($LASTEXITCODE -ne 0) -{ - Write-Error "dotnet not found." - exit 2 -} - -Write-Output "Enter `"secret`" dir..." -Push-Location $PSScriptRoot - -Write-Output "Begin to build..." -dotnet publish Crupest.SecretTool -c Release -o "$secret_dir/publish" --sc -r $args[0] - -Pop-Location - -Write-Host "Finish!" -ForegroundColor Green diff --git a/tools/Crupest.SecretTool/tools/cru-proxy-edit b/tools/Crupest.SecretTool/tools/cru-proxy-edit deleted file mode 100755 index 51a33e1..0000000 --- a/tools/Crupest.SecretTool/tools/cru-proxy-edit +++ /dev/null @@ -1,12 +0,0 @@ -#! /usr/bin/env bash - -set -e - -p="$HOME/codes/crupest/tools/Crupest.SecretTool/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.SecretTool/tools/cru-proxy-log b/tools/Crupest.SecretTool/tools/cru-proxy-log deleted file mode 100755 index 6ec6ee1..0000000 --- a/tools/Crupest.SecretTool/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-secret-tool "$@" -elif [[ "$(uname)" == "Darwin" ]]; then - exec less "$HOME/.local/state/Crupest.SecretTool/log" -else - echo "Not supported on systems other than macOS and Linux now." >&2 - exit 1 -fi diff --git a/tools/Crupest.SecretTool/tools/crupest-secret-tool.service b/tools/Crupest.SecretTool/tools/crupest-secret-tool.service deleted file mode 100644 index df6d172..0000000 --- a/tools/Crupest.SecretTool/tools/crupest-secret-tool.service +++ /dev/null @@ -1,8 +0,0 @@ -[Unit] -Description=crupest v2ray service - -[Service] -ExecStart=%h/.local/bin/Crupest.SecretTool - -[Install] -WantedBy=default.target diff --git a/tools/Crupest.SecretTool/tools/crupest-secret-tool.xml b/tools/Crupest.SecretTool/tools/crupest-secret-tool.xml deleted file mode 100644 index 9b85f13..0000000 --- a/tools/Crupest.SecretTool/tools/crupest-secret-tool.xml +++ /dev/null @@ -1,49 +0,0 @@ -<!-- - MIT License - - Copyright (c) 2008-2020 Kohsuke Kawaguchi, Sun Microsystems, Inc., CloudBees, - Inc., Oleg Nenashev and other contributors - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all - copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. ---> - -<!-- - This is a sample configuration of the Windows Service Wrapper. - This configuration file should be placed near the WinSW executable, the name should be the same. - E.g. for myapp.exe the configuration file name should be myapp.xml - - You can find more information about configuration options here: -https://github.com/kohsuke/winsw/blob/master/doc/xmlConfigFile.md ---> -<service> - <id>crupest-secret-tool</id> - <name>Crupest Secret Tool</name> - <description>Crupest Secret Tool (powered by WinSW)</description> - - <!-- Path to the executable, which should be started --> - <executable>%BASE%\Crupest.SecretTool.exe</executable> - - <onfailure action="restart" delay="10 sec" /> - <onfailure action="restart" delay="30 sec" /> - <onfailure action="restart" delay="50 sec" /> - - <workingdirectory>%BASE%</workingdirectory> - - <startmode>Automatic</startmode> -</service>
\ No newline at end of file diff --git a/tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist b/tools/Crupest.SecretTool/tools/life.crupest.secret-tool.plist deleted file mode 100644 index bdfe490..0000000 --- a/tools/Crupest.SecretTool/tools/life.crupest.secret-tool.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.secret-tool</string> - <key>ProgramArguments</key> - <array> - <string>/Users/crupest/.local/bin/Crupest.SecretTool</string> - </array> - <key>KeepAlive</key> - <true/> - <key>StandardOutPath</key> - <string>/Users/crupest/.local/state/Crupest.SecretTool/log</string> - <key>StandardErrorPath</key> - <string>/Users/crupest/.local/state/Crupest.SecretTool/error</string> -</dict> -</plist> diff --git a/tools/scripts/neovide-listen b/tools/scripts/neovide-listen deleted file mode 100755 index 2591842..0000000 --- a/tools/scripts/neovide-listen +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -if [[ -z "$NVIM_SOCKET" ]]; then - NVIM_SOCKET="/tmp/nvimsocket" -fi - -args=() -MY_NEOVIM_PATH="$HOME/codes/neovim/build/bin/nvim" -if [[ -e "$MY_NEOVIM_PATH" ]]; then - echo "Found my neovim at $MY_NEOVIM_PATH" - export VIMRUNTIME="$HOME/codes/neovim/runtime" - args=("${args[@]}" "--neovim-bin" "$MY_NEOVIM_PATH") -fi - -listen_added=0 -for arg in "$@"; do - args=("${args[@]}" "$arg") - if [ "$arg" = '--' ]; then - args=("${args[@]}" "--listen" "$NVIM_SOCKET") - listen_added=1 - fi -done - -if [[ $listen_added = 0 ]]; then - args=("${args[@]}" "--" "--listen" "$NVIM_SOCKET") -fi - -NEOVIDE_BIN=neovide -MY_NEOVIDE_PATH="$HOME/codes/neovide/target/release/neovide" -if [ -e "$MY_NEOVIDE_PATH" ]; then - echo "Found my neovide at $MY_NEOVIDE_PATH" - NEOVIDE_BIN="$MY_NEOVIDE_PATH" -fi - -if which nvr > /dev/null; then - echo "Detected nvr, set git editor env" - export GIT_EDITOR='nvr -cc split --remote-wait' -fi - -args=("$NEOVIDE_BIN" "${args[@]}") -echo "Command is ${args[@]}" -exec "${args[@]}" - diff --git a/tools/scripts/neovide-listen.ps1 b/tools/scripts/neovide-listen.ps1 deleted file mode 100644 index e84f3a2..0000000 --- a/tools/scripts/neovide-listen.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -$env:NVIM_LISTEN_ADDRESS ??= "\\.\pipe\nvimsocket" - -$neovide_args = @() - -$MY_NEOVIM_PATH="$HOME/codes/neovim/build/bin/nvim.exe" -if (Get-Item $MY_NEOVIM_PATH -ErrorAction Ignore) { - Write-Output "Found my neovim at $MY_NEOVIM_PATH." - $env:VIMRUNTIME="$HOME/codes/neovim/runtime" - $neovide_args += "--neovim-bin", "$MY_NEOVIM_PATH" -} - -$listen_added = $false -foreach ($arg in $args) { - $neovide_args += $arg - if ( $arg -eq '--') { - $neovide_args += "--listen", $env:NVIM_LISTEN_ADDRESS - $listen_added=$true - } -} - -if (-not $listen_added) { - $neovide_args += "--", "--listen", $env:NVIM_LISTEN_ADDRESS -} - -$neovide_bin = "neovide" -$my_neovide_path = "$HOME/codes/neovide/target/release/neovide.exe" -if (Get-Item $my_neovide_path -ErrorAction Ignore) { - Write-Output "Found my neovide at $my_neovide_path." - $neovide_bin = "$my_neovide_path" -} - -if (Get-Command nvr -ErrorAction Ignore) { - Write-Output "Detected nvr, set git editor env." - $env:GIT_EDITOR = "nvr -cc split --remote-wait" -} - -Write-Output "Command is $($neovide_args -join ' ')." -Start-Process $neovide_bin -ArgumentList $neovide_args -Wait diff --git a/tools/utility/rename-tree.py b/tools/utility/rename-tree.py deleted file mode 100755 index c177eb6..0000000 --- a/tools/utility/rename-tree.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import os -import os.path -import re - -parser = argparse.ArgumentParser( - prog='rename-tree', - description='Recursively rename directories and files') - -parser.add_argument('old') -parser.add_argument('new') -parser.add_argument('dirs', nargs="+") - -args = parser.parse_args() - -old_regex = re.compile(args.old) -new = args.new - -def rename(path, isdir): - dirname = os.path.dirname(path) - filename = os.path.basename(path) - new_filename = re.sub(old_regex, new, filename) - dir_str = "/" if isdir else "" - if new_filename != filename: - os.rename(path, os.path.join(dirname, new_filename)) - print(f"{path}{dir_str} -> {new_filename}{dir_str}") - -for i, d in enumerate(args.dirs): - print(f"[{i + 1}/{len(args.dirs)}] Run for {d}:") - for dirpath, dirnames, filenames in os.walk(d, topdown=False): - for filename in filenames: - rename(os.path.join(dirpath, filename), False) - rename(dirpath, True) - -print("Done!") |