public Server ParseShadowsocksServer(YamlMappingNode proxy) { string portString = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "port", "0"); int port; if (!int.TryParse(portString, out port)) { this.logger.LogError($"Invalid port: {port}."); return(null); } var server = new ShadowsocksServer() { Port = port, Name = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "name"), Host = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "server"), Password = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "password"), Method = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "cipher"), UDPRelay = Yaml.GetTruthFromYamlChildrenNode(proxy, "udp"), }; YamlNode pluginOptionsNode; // refer to offical clash to parse plugin options // https://github.com/Dreamacro/clash/blob/34338e7107c1868124f8aab2446f6b71c9b0640f/adapters/outbound/shadowsocks.go#L135 if (proxy.Children.TryGetValue("plugin-opts", out pluginOptionsNode) && pluginOptionsNode.NodeType == YamlNodeType.Mapping) { switch (Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "plugin").ToLower()) { case "obfs": var simpleObfsModeString = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "mode"); var simpleObfsOptions = new SimpleObfsPluginOptions(); if (SimpleObfsPluginOptions.TryParseMode(simpleObfsModeString, out SimpleObfsPluginMode simpleObfsMode)) { simpleObfsOptions.Mode = simpleObfsMode; } else if (!string.IsNullOrWhiteSpace(simpleObfsModeString)) { this.logger.LogError($"Unsupported simple-obfs mode: {simpleObfsModeString}. This server will be ignored."); return(null); } simpleObfsOptions.Host = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "host"); server.PluginOptions = simpleObfsOptions; break; case "v2ray-plugin": // also refer to official v2ray-plugin to parse v2ray-plugin options // https://github.com/shadowsocks/v2ray-plugin/blob/c7017f45bb1e12cf1e4b739bcb8f42f3eb8b22cd/main.go#L126 var v2rayModeString = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "mode"); var options = new V2RayPluginOptions(); server.PluginOptions = options; if (V2RayPluginOptions.TryParseMode(v2rayModeString, out V2RayPluginMode v2rayMode)) { options.Mode = v2rayMode; } else { this.logger.LogError($"Unsupported v2ray-plugin mode: {v2rayModeString}. This server will be ignored."); return(null); } options.Host = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "host"); options.Path = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "path"); options.EnableTLS = Yaml.GetTruthFromYamlChildrenNode(pluginOptionsNode, "tls"); options.SkipCertVerification = Yaml.GetTruthFromYamlChildrenNode(pluginOptionsNode, "skip-cert-verify"); options.Headers = new Dictionary <string, string>(); YamlNode headersNode; if (!(pluginOptionsNode as YamlMappingNode).Children.TryGetValue("headers", out headersNode)) { break; } if (headersNode.NodeType != YamlNodeType.Mapping) { break; } foreach (var header in (headersNode as YamlMappingNode)) { if (header.Value.NodeType != YamlNodeType.Scalar) { continue; } options.Headers.Add((header.Key as YamlScalarNode).Value, (header.Value as YamlScalarNode).Value); } break; } } if (server.PluginOptions == null) { var simpleObfsModeString = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "obfs"); if (SimpleObfsPluginOptions.TryParseMode(simpleObfsModeString, out SimpleObfsPluginMode simpleObfsMode)) { server.PluginOptions = new SimpleObfsPluginOptions() { Mode = simpleObfsMode, Host = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "obfs-host") }; } else if (!string.IsNullOrWhiteSpace(simpleObfsModeString)) { this.logger.LogError($"Unsupported simple-obfs mode: {simpleObfsModeString}"); return(null); } } return(server); }
/// <summary> /// Parse QuantumultX shadowsocks proxy setting /// Sample: /// shadowsocks=example.com:80, method=chacha20, password=pwd, obfs=http, obfs-host=bing.com, obfs-uri=/resource/file, fast-open=false, udp-relay=false, server_check_url=http://www.apple.com/generate_204, tag=ss-01 /// shadowsocks=example.com:443, method=chacha20, password=pwd, ssr-protocol=auth_chain_b, ssr-protocol-param=def, obfs=tls1.2_ticket_fastauth, obfs-host=bing.com, tag=ssr /// </summary> /// <param name="line">A string starts with `shadowsocks=`</param> /// <returns>Could be an instance of <see cref="ShadowsocksRServer"> or <see cref="ShadowsocksServer"></returns> public Server ParseShadowsocksLine(string line) { var properties = new Dictionary <string, string>(); foreach (var kvPair in line.Split(",")) { var parts = kvPair.Split("=", 2); if (parts.Length != 2) { this.logger.LogWarning($"[{{ComponentName}}] Invaild shadowsocks setting: {line}", this.GetType()); return(null); } properties.TryAdd(parts[0].Trim().ToLower(), parts[1].Trim()); } var host = default(string); var port = default(int); var method = properties.GetValueOrDefault("method"); var password = properties.GetValueOrDefault("password"); if (!Misc.SplitHostAndPort(properties.GetValueOrDefault("shadowsocks"), out host, out port)) { this.logger.LogWarning($"[{{ComponentName}}] Host and port are invalid and this proxy setting is ignored. Invaild shadowsocks setting : {line}", this.GetType()); return(null); } if (string.IsNullOrEmpty(method)) { this.logger.LogWarning($"[{{ComponentName}}] Encryption method is missing and this proxy setting is ignored. Invaild shadowsocks setting : {line}", this.GetType()); return(null); } if (string.IsNullOrEmpty(password)) { this.logger.LogWarning($"[{{ComponentName}}] Password is missing and this proxy setting is ignored. Invaild shadowsocks setting : {line}", this.GetType()); return(null); } Server server; if (this.IsShadowsocksRServer(properties)) { var ssrServer = new ShadowsocksRServer { Method = method, }; ssrServer.Obfuscation = properties.GetValueOrDefault("obfs"); ssrServer.ObfuscationParameter = properties.GetValueOrDefault("obfs-host"); ssrServer.Protocol = properties.GetValueOrDefault("ssr-protocol"); ssrServer.ProtocolParameter = properties.GetValueOrDefault("ssr-protocol-param"); // QuantumultX doesn't support following parameter yet // * UDP Port // * UDP over TCP server = ssrServer; } else { var ssServer = new ShadowsocksServer { Method = method, }; if (bool.TryParse(properties.GetValueOrDefault("udp-relay", "false"), out bool udpRelay)) { ssServer.UDPRelay = udpRelay; } if (bool.TryParse(properties.GetValueOrDefault("fast-open", "false"), out bool fastOpen)) { ssServer.FastOpen = fastOpen; } var obfs = properties.GetValueOrDefault("obfs", "").ToLower(); switch (obfs) { case "tls": case "http": { if (!SimpleObfsPluginOptions.TryParseMode(obfs, out SimpleObfsPluginMode mode)) { this.logger.LogWarning($"[{{ComponentName}}] Simple-obfs mode `{obfs}` is not supported and this proxy setting is ignored. Invaild shadowsocks setting : {line}", this.GetType()); return(null); } var options = new SimpleObfsPluginOptions() { Host = properties.GetValueOrDefault("obfs-host"), Mode = mode, }; if (obfs != "tls") { options.Uri = properties.GetValueOrDefault("obfs-uri"); } ssServer.PluginOptions = options; } break; case "wss": case "ws": { if (!V2RayPluginOptions.TryParseMode(obfs, out V2RayPluginMode mode)) { this.logger.LogWarning($"[{{ComponentName}}] Simple-obfs mode `{obfs}` is not supported and this proxy setting is ignored. Invaild shadowsocks setting : {line}", this.GetType()); return(null); } var options = new V2RayPluginOptions() { Mode = mode, Host = properties.GetValueOrDefault("obfs-host"), Path = properties.GetValueOrDefault("obfs-uri"), EnableTLS = obfs == "wss", }; ssServer.PluginOptions = options; } break; default: this.logger.LogWarning($"[{{ComponentName}}] Obfuscation `{obfs}` is not supported and this proxy setting is ignored. Invaild shadowsocks setting : {line}", this.GetType()); return(null); } server = ssServer; } server.Host = host; server.Port = port; server.Name = properties.GetValueOrDefault("tag", $"{server.Host}:{server.Port}"); return(server); }
public static Server ParseShadowsocksServer(YamlMappingNode proxy) { int port; if (!Int32.TryParse(Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "port", "0"), out port)) { return(null); } var server = new ShadowsocksServer() { Port = port, Name = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "name"), Host = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "server"), Password = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "password"), Method = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "cipher"), UDPRelay = Yaml.GetTruthFromYamlChildrenNode(proxy, "udp"), }; YamlNode pluginOptionsNode; // refer to offical clash to parse plugin options // https://github.com/Dreamacro/clash/blob/34338e7107c1868124f8aab2446f6b71c9b0640f/adapters/outbound/shadowsocks.go#L135 if (proxy.Children.TryGetValue("plugin-opts", out pluginOptionsNode) && pluginOptionsNode.NodeType == YamlNodeType.Mapping) { switch (Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "plugin")) { case "obfs": server.PluginType = PluginType.SimpleObfs; server.PluginOptions = new SimpleObfsPluginOptions() { Mode = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "mode"), Host = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "host"), }; break; case "v2ray-plugin": // also refer to official v2ray-plugin to parse v2ray-plugin options // https://github.com/shadowsocks/v2ray-plugin/blob/c7017f45bb1e12cf1e4b739bcb8f42f3eb8b22cd/main.go#L126 var options = new V2RayPluginOptions(); server.PluginType = PluginType.V2Ray; server.PluginOptions = options; options.Host = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "host"); options.Mode = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "mode"); options.Path = Yaml.GetStringOrDefaultFromYamlChildrenNode(pluginOptionsNode, "path"); options.EnableTLS = Yaml.GetTruthFromYamlChildrenNode(pluginOptionsNode, "tls"); options.SkipCertVerification = Yaml.GetTruthFromYamlChildrenNode(pluginOptionsNode, "skip-cert-verify"); options.Headers = new Dictionary <string, string>(); YamlNode headersNode; if (!(pluginOptionsNode as YamlMappingNode).Children.TryGetValue("headers", out headersNode)) { break; } if (headersNode.NodeType != YamlNodeType.Mapping) { break; } foreach (var header in (headersNode as YamlMappingNode)) { if (header.Value.NodeType != YamlNodeType.Scalar) { continue; } options.Headers.Add((header.Key as YamlScalarNode).Value, (header.Value as YamlScalarNode).Value); } break; } } if (server.PluginType == PluginType.None) { var obfs = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "obfs"); var obfsHost = Yaml.GetStringOrDefaultFromYamlChildrenNode(proxy, "obfs-host"); if (!String.IsNullOrEmpty(obfs)) { server.PluginType = PluginType.SimpleObfs; server.PluginOptions = new SimpleObfsPluginOptions() { Mode = obfs, Host = obfsHost }; } } return(server); }