diff options
10 files changed, 168 insertions, 92 deletions
diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj b/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj index 1e011b1..2502e74 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Crupest.SecretTool.csproj @@ -23,6 +23,12 @@ <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/Program.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs index 310143d..18b1ac0 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Program.cs @@ -64,18 +64,22 @@ public static class Program { 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") { + 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), @@ -86,7 +90,12 @@ public static class Program } else if (verb == "generate-sing-config" || verb == "gs") { - var config = ToolConfig.FromDirectoryForSing(CrupestSecretToolDirectory, true, true); + 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; } diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs index ddbbde8..d2703ba 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Proxy.cs @@ -48,7 +48,7 @@ public class VmessProxy(string host, int port, string userId, string path, strin public override SingConfigJsonObjects.OutboundBase ToJsonObjectSing() { return new SingConfigJsonObjects.VmessOutbound(Tag, Host, Port, UserId, - Transport: new SingConfigJsonObjects.V2rayWebsocketTransport(Path), + Transport: new SingConfigJsonObjects.V2rayWebsocketTransport(Path, new Dictionary<string, string> { { "Host", Host } }), Tls: new SingConfigJsonObjects.OutboundTls(true)); } diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs index 9c247a2..fdf1b93 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/Routing.cs @@ -108,7 +108,7 @@ public record Routing(List<RoutingRule> Rules) : IV4ConfigObject, ISingConfigObj public SingConfigJsonObjects.Route ToJsonObjectSing() { - List<SingConfigJsonObjects.RouteRule> ruleJsonObjects = []; + 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); } diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs index 6af0cd1..56b5563 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/SingConfigJsonObjects.cs @@ -12,7 +12,7 @@ public static class SingConfigJsonObjects 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,
+ 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;
diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs b/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs index 534bd65..809fba1 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/ToolConfig.cs @@ -13,9 +13,9 @@ public interface ISingConfigObject object ToJsonObjectSing(); } -public class ToolConfig(Template template, List<Proxy> proxies, Routing router, StaticHosts? hosts) +public class ToolConfigBase(Template template, List<Proxy> proxies, Routing router) { - private class JsonInterfaceConverter<Interface> : JsonConverter<Interface> + protected class JsonInterfaceConverter<Interface> : JsonConverter<Interface> { public override Interface Read( ref Utf8JsonReader reader, @@ -34,20 +34,24 @@ public class ToolConfig(Template template, List<Proxy> proxies, Routing router, } } - - 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 const string SingConfigTemplateFileName = "sing-config.json.template"; + 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 SingRouteAnchor = "ROUTE_ANCHOR"; private const string HostsAnchor = "HOSTS_ANCHOR"; public const string AddCnAttributeToGeositeEnvironmentVariable = "CRUPEST_V2RAY_GEOSITE_USE_CN"; @@ -58,45 +62,8 @@ public class ToolConfig(Template template, List<Proxy> proxies, Routing router, _ => true }; - public Template Template { get; set; } = template; - public List<Proxy> Proxies { get; set; } = proxies; - public Routing Routing { get; set; } = router; public StaticHosts Hosts { get; set; } = hosts is null ? new StaticHosts([]) : hosts; - public string 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))), - [SingRouteAnchor] = JsonSerializer.Serialize(Routing.ToJsonObjectSing(), 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 string ToJsonStringV4(string domainStrategy = "IpOnDemand", bool directGeositeCn = true, bool pretty = true) { var jsonOptions = new JsonSerializerOptions(new JsonSerializerOptions @@ -178,9 +145,76 @@ public class ToolConfig(Template template, List<Proxy> proxies, Routing router, } } - public static ToolConfig FromFilesForSing(string templatePath, string vmessPath, string proxyPath, bool clean, bool silent) + public static ToolConfig FromDirectory(string directory) { - foreach (var path in new List<string>([templatePath, vmessPath, proxyPath])) + 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)) { @@ -191,7 +225,7 @@ public class ToolConfig(Template template, List<Proxy> proxies, Routing router, var geoSiteData = GeoDataManager.Instance.GetOrCreateGeoSiteData(clean, silent); ProxyFile proxyFile = new(proxyPath); - string templateString, vmessString; + string templateString, vmessString, inboundsString; string file = ""; try @@ -200,6 +234,8 @@ public class ToolConfig(Template template, List<Proxy> proxies, Routing router, templateString = File.ReadAllText(templatePath); file = vmessPath; vmessString = File.ReadAllText(vmessPath); + file = inboundsPath; + inboundsString = File.ReadAllText(inboundsPath); } catch (Exception e) { @@ -211,10 +247,10 @@ public class ToolConfig(Template template, List<Proxy> proxies, Routing router, file = templatePath; var template = new Template(templateString); file = vmessPath; - var vmess = VmessProxy.CreateFromConfigString(vmessString, "proxy"); + var vmess = VmessProxy.CreateFromConfigString(vmessString, "proxy-out"); file = proxyPath; - var routing = Routing.FromProxyFileForSing(proxyFile, geoSiteData, "proxy", "direct"); - return new ToolConfig(template, [vmess], routing, null); + var routing = Routing.FromProxyFileForSing(proxyFile, geoSiteData, "proxy-out", "direct-out"); + return new SingToolConfig(template, [vmess], routing, inboundsString); } catch (Exception e) { @@ -222,29 +258,14 @@ public class ToolConfig(Template template, List<Proxy> proxies, Routing router, } } - public static ToolConfig FromDirectory(string directory) + 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), - Path.Join(directory, HostsConfigFileName) - ); - } - - public static ToolConfig FromDirectoryForSing(string directory, bool clean, bool silent) - { - return FromFilesForSing( - Path.Join(directory, SingConfigTemplateFileName), - Path.Join(directory, VmessConfigFileName), - Path.Join(directory, ProxyConfigFileName), + isMobile ? Path.Join(directory, ConfigInboundsMobileFileName) : Path.Join(directory, ConfigInboundsPcFileName), clean, silent ); } - - public static void FromDirectoryAndWriteToFile(string directory, string outputPath) - { - var config = FromDirectory(directory); - File.WriteAllText(outputPath, config.ToJsonStringV4()); - } } diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template index 429bd1d..d7e55a0 100644 --- a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-config.json.template @@ -23,31 +23,21 @@ "tag": "google",
"address": "8.8.8.8"
}
-
]
},
- "inbounds": [
- {
- "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
- }
- ],
+ "inbounds": ${INBOUNDS_ANCHOR},
"outbounds": [
{
"type": "direct",
- "tag": "direct"
+ "tag": "direct-out"
},
{
"type": "block",
- "tag": "block"
+ "tag": "block-out"
+ },
+ {
+ "tag": "dns-out",
+ "type": "dns"
},
${PROXY_ANCHOR}
],
diff --git a/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json new file mode 100644 index 0000000..5038c40 --- /dev/null +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-mobile.json @@ -0,0 +1,11 @@ +[
+ {
+ "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 new file mode 100644 index 0000000..956d751 --- /dev/null +++ b/tools/Crupest.SecretTool/Crupest.SecretTool/sing-inbounds-pc.json @@ -0,0 +1,14 @@ +[
+ {
+ "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.ps1 b/tools/Crupest.SecretTool/build-secret.ps1 new file mode 100644 index 0000000..8aa7987 --- /dev/null +++ b/tools/Crupest.SecretTool/build-secret.ps1 @@ -0,0 +1,25 @@ +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 |