/// <summary>
        /// Follow scheme https://shadowsocks.org/en/spec/SIP002-URI-Scheme.html
        /// to parse a given shadowsocks server uri.
        /// </summary>
        internal ShadowsocksServer ParseShadowsocksURI(string ssURI)
        {
            var u      = new Uri(ssURI);
            var server = new ShadowsocksServer();

            // UserInfo: WebSafe Base64(method:password)
            var plainUserInfo = Encoding.UTF8.GetString(WebSafeBase64Decode(HttpUtility.UrlDecode(u.UserInfo))).Split(':', 2);

            server.Method   = plainUserInfo[0];
            server.Password = plainUserInfo.ElementAtOrDefault(1);

            // Host & Port
            server.Host = u.Host;
            server.Port = u.Port;

            // Name
            if (u.Fragment.StartsWith("#"))
            {
                server.Name = HttpUtility.UrlDecode(u.Fragment.Substring(1));
            }

            // Plugin
            var otherParams = HttpUtility.ParseQueryString(u.Query);
            var plugin      = (otherParams.Get("plugin") ?? "").Trim();
            var pluginInfos = plugin.Split(";");

            switch (pluginInfos[0].Trim())
            {
            case "simple-obfs":
            case "obfs-local":
                var options = new SimpleObfsPluginOptions();
                server.PluginOptions = options;

                foreach (var info in pluginInfos)
                {
                    var trimedInfo = info.Trim();
                    if (trimedInfo.StartsWith("obfs="))
                    {
                        if (SimpleObfsPluginOptions.TryParseMode(trimedInfo.Substring("obfs=".Length).Trim(), out SimpleObfsPluginMode mode))
                        {
                            options.Mode = mode;
                        }
                    }
                    else if (string.IsNullOrEmpty(options.Host) && trimedInfo.StartsWith("obfs-host="))
                    {
                        options.Host = trimedInfo.Substring("obfs-host=".Length);
                    }
                    else
                    {
                        // TODO: log
                    }
                }
                break;
            }



            return(server);
        }
示例#2
0
        public Server[] ParseProxyList(string profile)
        {
            var servers      = new List <Server>();
            var plainProxies = profile.Trim().Split("\n");

            foreach (var proxy in plainProxies)
            {
                var trimed = proxy.Trim();
                if (trimed.StartsWith("#"))
                {
                    continue;
                }

                var match = proxyRegex.Match(proxy);
                if (!match.Success)
                {
                    continue;
                }

                var server = new ShadowsocksServer();
                server.Name = match.Groups[1].Value;

                var serverInfos = match.Groups[2].Value.Trim().Split(",");
                switch (serverInfos[0].Trim().ToLower())
                {
                case "ss":
                case "custom":
                    break;

                default:
                    continue;
                }

                int port = 0;
                if (Int32.TryParse((serverInfos[2] ?? ""), out port))
                {
                    server.Port = port;
                }
                server.Host     = (serverInfos[1] ?? "").Trim();
                server.Method   = TrimPrefix((serverInfos[3] ?? "").Trim(), "encrypt-method=");
                server.Password = TrimPrefix((serverInfos[4] ?? "").Trim(), "password="******"obfs="))
                    {
                        pluginOptions.Mode = trimedInfo.Substring("obfs=".Length).TrimStart();
                    }

                    else if (String.IsNullOrEmpty(pluginOptions.Host) && trimedInfo.StartsWith("obfs-host="))
                    {
                        pluginOptions.Host = trimedInfo.Substring("obfs-host=".Length).TrimStart();
                    }

                    else if (!server.UDPRelay && trimedInfo.StartsWith("udp-relay="))
                    {
                        server.UDPRelay = trimedInfo.Substring("udp-relay=".Length).TrimStart() == "true";
                    }

                    // module is no more needed for newer version of surge
                    else if (trimedInfo.StartsWith("http://") || trimedInfo.StartsWith("https://"))
                    {
                        continue;
                    }

                    else
                    {
                        logger.LogWarning($"Unsupported surge proxy parameter found: {trimedInfo}");
                    }
                }

                if (!String.IsNullOrEmpty(pluginOptions.Mode))
                {
                    server.PluginType    = PluginType.SimpleObfs;
                    server.PluginOptions = pluginOptions;
                }

                servers.Add(server);
            }

            return(servers.ToArray());
        }
示例#3
0
        /// <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);
        }
示例#4
0
        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);
        }