diff options
18 files changed, 466 insertions, 16 deletions
diff --git a/crupest-words.txt b/crupest-words.txt index 1efd12f..af85fa2 100644 --- a/crupest-words.txt +++ b/crupest-words.txt @@ -1,5 +1,10 @@ crupest +# secret +vmess +confdir +geodata + # university ustc diff --git a/tools/Crupest.V2ray/.gitignore b/tools/Crupest.V2ray/.gitignore index 15db15d..ac4d8a4 100644 --- a/tools/Crupest.V2ray/.gitignore +++ b/tools/Crupest.V2ray/.gitignore @@ -3,3 +3,5 @@ bin obj *.pubxml.user *.csproj.user + +publish diff --git a/tools/Crupest.V2ray/Crupest.V2ray/IV2rayProxy.cs b/tools/Crupest.V2ray/Crupest.V2ray/IV2rayProxy.cs new file mode 100644 index 0000000..7643a01 --- /dev/null +++ b/tools/Crupest.V2ray/Crupest.V2ray/IV2rayProxy.cs @@ -0,0 +1,7 @@ +namespace Crupest.V2ray; + +public interface IV2rayProxy +{ + object ToOutboundJsonObject(); +} + diff --git a/tools/Crupest.V2ray/Crupest.V2ray/IV2rayStaticHostResolveResult.cs b/tools/Crupest.V2ray/Crupest.V2ray/IV2rayStaticHostResolveResult.cs new file mode 100644 index 0000000..ae5fe1c --- /dev/null +++ b/tools/Crupest.V2ray/Crupest.V2ray/IV2rayStaticHostResolveResult.cs @@ -0,0 +1,7 @@ +namespace Crupest.V2ray; + +public interface IV2rayStaticHostResolveResult +{ + IDictionary<string, object> GetJsonProperties(); +} + diff --git a/tools/Crupest.V2ray/Crupest.V2ray/Program.cs b/tools/Crupest.V2ray/Crupest.V2ray/Program.cs index e0c0d6d..e623a88 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/Program.cs +++ b/tools/Crupest.V2ray/Crupest.V2ray/Program.cs @@ -24,8 +24,8 @@ public static class Program var verb = args[0].ToLower(); if (verb == "download-geodata" || verb == "dg") { - var geoDataDonwloader = new GeoDataDownloader(); - geoDataDonwloader.Download(ExeDir); + var geoDataDownloader = new GeoDataDownloader(); + geoDataDownloader.Download(ExeDir); return; } throw new Exception("Invalid command line arguments."); diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs index 1ae9683..5623a20 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayConfig.cs @@ -4,20 +4,21 @@ namespace Crupest.V2ray; public class V2rayConfig { + private const string ProxyAnchor = "PROXY_ANCHOR"; private const string VmessAnchor = "VMESS_PROXY_ANCHOR"; private const string RoutingAnchor = "ROUTING_ANCHOR"; private const string HostsAnchor = "HOSTS_ANCHOR"; - public V2rayConfig(string template, V2rayVmessProxy vmess, V2rayRouting router, V2rayHosts hosts) + public V2rayConfig(string template, IV2rayProxy proxy, V2rayRouting router, V2rayHosts hosts) { Template = template; - Vmess = vmess; + Proxy = proxy; Routing = router; Hosts = hosts; } public string Template { get; set; } - public V2rayVmessProxy Vmess { get; set; } + public IV2rayProxy Proxy { get; set; } public V2rayRouting Routing { get; set; } public V2rayHosts Hosts { get; set; } @@ -31,7 +32,7 @@ public class V2rayConfig var templateValues = new Dictionary<string, string> { - [VmessAnchor] = JsonSerializer.Serialize(Vmess.ToOutboundJsonObject(), jsonOptions), + [VmessAnchor] = JsonSerializer.Serialize(Proxy.ToOutboundJsonObject(), jsonOptions), [RoutingAnchor] = JsonSerializer.Serialize(Routing.ToJsonObject(), jsonOptions), [HostsAnchor] = JsonSerializer.Serialize(Hosts.ToJsonObject(), jsonOptions), }; diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs index 5b8fcac..201dcf4 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayController.cs @@ -4,12 +4,19 @@ namespace Crupest.V2ray; public class V2rayController { - public const string V2RayAssetLocationEnvironmentVariableName = "V2RAY_LOCATION_ASSET"; - public const string V2RayConfigLocationEnvironmentVariableName = "V2RAY_LOCATION_CONFIG"; + public static string V2rayExecutableName { get; } = OperatingSystem.IsWindows() ? "v2ray.exe" : "v2ray"; + public const string V2rayExecutableLocationEnvironmentVariableName = "V2RAY_LOCATION_EXE"; + public const string V2rayAssetLocationEnvironmentVariableName = "V2RAY_LOCATION_ASSET"; + public const string V2rayConfigLocationEnvironmentVariableName = "V2RAY_LOCATION_CONFIG"; + public const string V2rayV5ConfdirEnvironmentVariableName = "v2ray.location.confdir"; - public V2rayController(string v2rayExePath = "v2ray") : this(v2rayExePath, Program.ExeDir, Program.ExeDir) + public V2rayController() : this(V2rayExecutableName, Program.ExeDir, Program.ExeDir) { - + var localV2ray = Path.Combine(Program.ExeDir, V2rayExecutableName); + if (Path.Exists(localV2ray)) + { + V2rayExePath = localV2ray; + } } public V2rayController(string v2rayExePath, string configDirPath, string assetDirPath) @@ -32,8 +39,32 @@ public class V2rayController { FileName = V2rayExePath, }; - startInfo.EnvironmentVariables[V2RayConfigLocationEnvironmentVariableName] = ConfigDirPath; - startInfo.EnvironmentVariables[V2RayAssetLocationEnvironmentVariableName] = AssetDirPath; + startInfo.EnvironmentVariables[V2rayConfigLocationEnvironmentVariableName] = ConfigDirPath; + startInfo.EnvironmentVariables[V2rayAssetLocationEnvironmentVariableName] = AssetDirPath; + + process.StartInfo = startInfo; + process.OutputDataReceived += (_, args) => + { + Console.Out.Write(args.Data); + }; + process.ErrorDataReceived += (_, args) => + { + Console.Error.WriteLine(args.Data); + }; + + return process; + } + + private Process V5CreateProcess() + { + var process = new Process(); + + var startInfo = new ProcessStartInfo + { + FileName = V2rayExePath, + }; + startInfo.ArgumentList.Add("run"); + startInfo.EnvironmentVariables[V2rayV5ConfdirEnvironmentVariableName] = ConfigDirPath; process.StartInfo = startInfo; process.OutputDataReceived += (_, args) => @@ -65,7 +96,7 @@ public class V2rayController if (CurrentProcess is null) { - CurrentProcess = CreateProcess(); + CurrentProcess = V5CreateProcess(); CurrentProcess.EnableRaisingEvents = true; CurrentProcess.Exited += (_, _) => { diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayHttpProxy.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayHttpProxy.cs new file mode 100644 index 0000000..c641b4b --- /dev/null +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayHttpProxy.cs @@ -0,0 +1,41 @@ +namespace Crupest.V2ray; + +public class V2rayHttpProxy : IV2rayProxy +{ + public record HttpOutboundJsonObject(string Protocol, SettingsJsonObject Settings, string Tag) + { + public static HttpOutboundJsonObject Create(string address, int port, string tag) + { + return new HttpOutboundJsonObject("http", new SettingsJsonObject( + new List<ServerJsonObject> { new ServerJsonObject(address, port) } + ), tag); + } + } + + public record ServerJsonObject(string Address, int Port); + public record SettingsJsonObject(List<ServerJsonObject> Servers); + + public string Host { get; set; } + public int Port { get; set; } + + public V2rayHttpProxy(string host, int port) + { + Host = host; + Port = port; + } + + public HttpOutboundJsonObject ToOutboundJsonObject(string tag = "proxy") + { + return HttpOutboundJsonObject.Create(Host, Port, tag); + } + + object IV2rayProxy.ToOutboundJsonObject() + { + return ToOutboundJsonObject(); + } + + public static V2rayHttpProxy FromDictionary(Dictionary<string, string> dict) + { + return new V2rayHttpProxy(dict["host"], int.Parse(dict["port"])); + } +} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayRouting.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayRouting.cs index 7265b09..d5ad0da 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayRouting.cs +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayRouting.cs @@ -6,8 +6,16 @@ public record V2rayRouting(List<V2rayRoutingRule> Rules, string DomainStrategy = public record IpRuleJsonObject(List<string> Ip, string OutboundTag, string Type = "field"); + public record V5DomainRuleJsonObject(List<V2rayRoutingRuleMatcher.V5DomainObject> Domain, string OutboundTag); + + public record V5GeoDomainRuleJsonObject(List<V2rayRoutingRuleMatcher.V5GeoDomainObject> GeoDomain, string OutboundTag); + + public record V5GeoIpRuleJsonObject(List<V2rayRoutingRuleMatcher.V5GeoIpObject> Geoip, string OutboundTag); + public record RoutingJsonObject(string DomainStrategy, List<object> Rules); + public record V5RouterJsonObject(string DomainStrategy, List<object> Rule); + public V2rayRouting() : this(new List<V2rayRoutingRule>()) { @@ -35,6 +43,11 @@ public record V2rayRouting(List<V2rayRoutingRule> Rules, string DomainStrategy = return new RoutingJsonObject(DomainStrategy, ruleJsonObjects); } + public V5RouterJsonObject V5ToJsonObject() + { + throw new NotImplementedException(); + } + public static V2rayRouting FromStringList(List<string> list, string outboundTag = "proxy") { var router = new V2rayRouting(); diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayRoutingRule.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayRoutingRule.cs index 0218183..1928de0 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayRoutingRule.cs +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayRoutingRule.cs @@ -21,5 +21,15 @@ public record V2rayRoutingRule(V2rayRoutingRuleMatcher Matcher, string OutboundT } return result; } + + public static Dictionary<V2rayRoutingRuleMatcher.V5MatchByKind, List<V2rayRoutingRule>> V5GroupByMatchByKind(List<V2rayRoutingRule> rules) + { + var result = new Dictionary<V2rayRoutingRuleMatcher.V5MatchByKind, List<V2rayRoutingRule>>(); + foreach (var group in rules.GroupBy(r => r.Matcher.V5MatchBy)) + { + result[group.Key] = group.ToList(); + } + return result; + } } diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayRoutingRuleMatcher.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayRoutingRuleMatcher.cs index a13237c..7c853c4 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayRoutingRuleMatcher.cs +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayRoutingRuleMatcher.cs @@ -8,6 +8,14 @@ public record V2rayRoutingRuleMatcher(V2rayRoutingRuleMatcher.MatchKind Kind, st Ip } + public enum V5MatchByKind + { + Domain, + // Ip, + GeoIp, + GeoSite, + } + public enum MatchKind { GeoIp, @@ -30,6 +38,19 @@ public record V2rayRoutingRuleMatcher(V2rayRoutingRuleMatcher.MatchKind Kind, st } } + public V5MatchByKind V5MatchBy + { + get + { + return Kind switch + { + MatchKind.GeoIp => V5MatchByKind.GeoIp, + MatchKind.GeoSite => V5MatchByKind.GeoSite, + _ => V5MatchByKind.Domain, + }; + } + } + public static V2rayRoutingRuleMatcher? Parse(string line) { if (line.IndexOf('#') != -1) @@ -71,4 +92,51 @@ public record V2rayRoutingRuleMatcher(V2rayRoutingRuleMatcher.MatchKind Kind, st _ => throw new Exception("Unknown matcher kind."), }; } + + public enum V5DomainObjectType + { + Plain, + Regex, + RootDomain, + Full, + } + + public record V5DomainObject(V5DomainObjectType Type, string Value); + + public V5DomainObject ToDomainObject() + { + return new V5DomainObject(Kind switch + { + MatchKind.DomainFull => V5DomainObjectType.Full, + MatchKind.DomainPlain => V5DomainObjectType.Plain, + MatchKind.DomainRegex => V5DomainObjectType.Regex, + MatchKind.DomainSuffix => V5DomainObjectType.RootDomain, + _ => throw new Exception("Not a domain matcher."), + }, Value); + } + + public record V5GeoDomainObject(string Code); + + public V5GeoDomainObject ToGeoDomainObject() + { + if (Kind != MatchKind.GeoSite) + { + throw new Exception("Not a geo-domain matcher."); + } + + return new V5GeoDomainObject(Value); + } + + public record V5GeoIpObject(string Code); + + public V5GeoIpObject ToGeoIpObject() + { + if (Kind != MatchKind.GeoIp) + { + throw new Exception("Not a geo-ip matcher."); + } + + return new V5GeoIpObject(Value); + } } + diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayStaticHostDomainResolveResult.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayStaticHostDomainResolveResult.cs new file mode 100644 index 0000000..88ea5ba --- /dev/null +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayStaticHostDomainResolveResult.cs @@ -0,0 +1,21 @@ +namespace Crupest.V2ray; + +public class V2rayStaticHostDomainResolveResult : IV2rayStaticHostResolveResult +{ + public V2rayStaticHostDomainResolveResult(string domain) + { + Domain = domain; + } + + public string Domain { get; } + + public IDictionary<string, object> GetJsonProperties() + { + return new Dictionary<string, object> + { + + ["proxiedDomain"] = Domain + }; + } +} + diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayStaticHostIpResolveResult.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayStaticHostIpResolveResult.cs new file mode 100644 index 0000000..2829152 --- /dev/null +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayStaticHostIpResolveResult.cs @@ -0,0 +1,20 @@ +namespace Crupest.V2ray; + +public class V2rayStaticHostIpResolveResult : IV2rayStaticHostResolveResult +{ + public V2rayStaticHostIpResolveResult(IEnumerable<string> ips) + { + Ips = ips.ToList(); + } + + public IReadOnlyList<string> Ips { get; } + + public IDictionary<string, object> GetJsonProperties() + { + return new Dictionary<string, object> + { + ["ip"] = Ips + }; + } +} + diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigObjects.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigObjects.cs new file mode 100644 index 0000000..f4001c1 --- /dev/null +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5ConfigObjects.cs @@ -0,0 +1,31 @@ +namespace Crupest.V2ray; + +public static class V2rayV5ConfigObjects +{ + public record OutboundObject(string Protocol, object Settings, string Tag, object? StreamSettings) + { + public static OutboundObject VmessViaWs(string tag, string address, int port, string uuid, string path) + { + return new OutboundObject("vmess", new VmessSettings(address, port, uuid), tag, StreamSettingsObject.Ws(path)); + } + + public static OutboundObject Http(string tag, string address, int port) + { + return new OutboundObject("http", new HttpSettingsObject(address, port), tag, null); + } + } + + public record WsSettingsObject(string Path, Dictionary<string, string> Headers); + + public record StreamSettingsObject(string Transport, object TransportSettings, string Security, object SecuritySettings) + { + public static StreamSettingsObject Ws(string path) + { + return new StreamSettingsObject("ws", new WsSettingsObject(path, new()), "tls", new()); + } + } + + public record VmessSettings(string Address, int Port, string Uuid); + + public record HttpSettingsObject(string Address, int Port); +} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5StaticHostRule.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5StaticHostRule.cs new file mode 100644 index 0000000..4a89134 --- /dev/null +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayV5StaticHostRule.cs @@ -0,0 +1,86 @@ +using System.Net; + +namespace Crupest.V2ray; + +public class V2rayV5StaticHostRule +{ + public enum MatcherKind + { + Full, + Subdomain, + Keyword, + Regex + } + + public V2rayV5StaticHostRule(MatcherKind matcher, string domain, IV2rayStaticHostResolveResult resolveResult) + { + Matcher = matcher; + Domain = domain; + ResolveResult = resolveResult; + } + + public MatcherKind Matcher { get; } + public string Domain { get; } + public IV2rayStaticHostResolveResult ResolveResult { get; } + + public Dictionary<string, object> ToJsonObject() + { + var result = new Dictionary<string, object> + { + ["type"] = Enum.GetName(Matcher)!, + ["domain"] = Domain + }; + + foreach (var (key, value) in ResolveResult.GetJsonProperties()) + { + result.Add(key, value); + } + + return result; + } + + public static V2rayV5StaticHostRule IpRule(MatcherKind matcher, string domain, IEnumerable<string> ips) + { + return new V2rayV5StaticHostRule(matcher, domain, new V2rayStaticHostIpResolveResult(ips)); + } + + public static V2rayV5StaticHostRule DomainRule(MatcherKind matcher, string domain, string resolvedDomain) + { + return new V2rayV5StaticHostRule(matcher, domain, new V2rayStaticHostDomainResolveResult(resolvedDomain)); + } + + public static V2rayV5StaticHostRule Parse(string str) + { + var components = str.Trim().Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries).ToList(); + + if (components.Count <= 1) + { + throw new FormatException("The str only has one or no component."); + } + + var matcher = MatcherKind.Subdomain; + + if (Enum.TryParse<MatcherKind>(components[0], out var m)) + { + matcher = m; + components.RemoveAt(0); + } + + if (components.Count <= 1) + { + throw new FormatException("The str only has one component after remove matcher."); + } + + var domain = components[0]; + components.RemoveAt(0); + + if (components.Count > 1 || IPAddress.TryParse(components[0], out var _)) + { + return new V2rayV5StaticHostRule(matcher, domain, new V2rayStaticHostIpResolveResult(components)); + } + else + { + return new V2rayV5StaticHostRule(matcher, domain, new V2rayStaticHostDomainResolveResult(domain)); + } + } +} diff --git a/tools/Crupest.V2ray/Crupest.V2ray/V2rayVmessProxy.cs b/tools/Crupest.V2ray/Crupest.V2ray/V2rayVmessProxy.cs index e701463..495961c 100644 --- a/tools/Crupest.V2ray/Crupest.V2ray/V2rayVmessProxy.cs +++ b/tools/Crupest.V2ray/Crupest.V2ray/V2rayVmessProxy.cs @@ -1,6 +1,6 @@ namespace Crupest.V2ray; -public class V2rayVmessProxy +public class V2rayVmessProxy : IV2rayProxy { public record VmessOutboundJsonObject(string Protocol, SettingsJsonObject Settings, string Tag, StreamSettingsJsonObject StreamSettings) { @@ -18,6 +18,8 @@ public class V2rayVmessProxy public record VnextUserJsonObject(string Id, int AlterId = 0, string Security = "auto", int Level = 0); + public record WsSettingsJsonObject(string Path, Dictionary<string, string> Headers); + public record StreamSettingsJsonObject(string Network, string Security, WsSettingsJsonObject WsSettings) { public static StreamSettingsJsonObject Ws(string path) @@ -26,8 +28,6 @@ public class V2rayVmessProxy } } - public record WsSettingsJsonObject(string Path, Dictionary<string, string> Headers); - public string Host { get; set; } public int Port { get; set; } public string Path { get; set; } @@ -47,6 +47,16 @@ public class V2rayVmessProxy return VmessOutboundJsonObject.ByWs(Host, Port, UserId, tag, Path); } + public V2rayV5ConfigObjects.OutboundObject ToOutboundJsonObjectV5(string tag = "proxy") + { + return V2rayV5ConfigObjects.OutboundObject.VmessViaWs(tag, Host, Port, UserId, Path); + } + + object IV2rayProxy.ToOutboundJsonObject() + { + return ToOutboundJsonObject(); + } + public static V2rayVmessProxy FromDictionary(Dictionary<string, string> dict) { return new V2rayVmessProxy(dict["host"], int.Parse(dict["port"]), dict["userid"], dict["path"]); diff --git a/tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template b/tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template new file mode 100644 index 0000000..49d97c2 --- /dev/null +++ b/tools/Crupest.V2ray/Crupest.V2ray/config.v5.json.template @@ -0,0 +1,55 @@ +{ + "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": ${STATIC_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_OUTBOUND_ANCHOR} + ], + "router": ${ROUTER_ANCHOR} +} + diff --git a/tools/build-secret.bash b/tools/build-secret.bash new file mode 100755 index 0000000..7f3ac29 --- /dev/null +++ b/tools/build-secret.bash @@ -0,0 +1,42 @@ +#! /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 + +tools_dir=$(realpath "$(dirname "$0")") +secret_dir="$tools_dir/Crupest.V2ray" + +echo "Tools dir: ${tools_dir}" + +echo "Check dotnet..." +dotnet --version + +echo "Enter \"secret\" dir..." +pushd "$secret_dir" + +echo "Begin to build..." +dotnet publish Crupest.V2ray -c Release -o "$secret_dir/publish" --sc -r "$1" + +popd + +echo "Finish!" |