예제 #1
0
        public string Encode(EncodeOptions options, Server[] servers, out Server[] encodedServers)
        {
            var encodedServerList = new List <Server>();
            var sb = new StringBuilder();

            sb.AppendLine($"REMARKS={options.ProfileName}");
            sb.AppendLine("");

            foreach (var server in servers)
            {
                var serverLine = server switch
                {
                    ShadowsocksServer ss => EncodeShadowsocksServer(ss),
                    ShadowsocksRServer ssr => EncodeShadowsocksRServer(ssr),
                    _ => null,
                };

                if (string.IsNullOrWhiteSpace(serverLine))
                {
                    this.logger.LogInformation($"Server {server} is ignored.");
                }
                else
                {
                    encodedServerList.Add(server);
                    sb.AppendLine(serverLine);
                }
            }

            encodedServers = encodedServerList.ToArray();
            return(Convert.ToBase64String(Encoding.ASCII.GetBytes(sb.ToString())));
        }
예제 #2
0
        public string EncodeProxyList(Server[] servers, out Server[] encodedServers)
        {
            var encodedServersList = new List <Server>();
            var serverLines        = servers
                                     .Select(server =>
            {
                string serverLine = server switch
                {
                    ShadowsocksServer ssServer => this.EncodeShadowsocksServer(ssServer),
                    ShadowsocksRServer ssrServer => this.EncodeShadowsocksRServer(ssrServer),
                    _ => null,
                };
                if (!string.IsNullOrWhiteSpace(serverLine))
                {
                    encodedServersList.Add(server);
                }
                else
                {
                    this.logger.LogInformation($"Server {server} is ignored.");
                }
                return(serverLine);
            })
                                     .Where(line => !string.IsNullOrWhiteSpace(line))
                                     .ToArray();

            encodedServers = encodedServersList.ToArray();
            return(string.Join('\n', serverLines));
        }
예제 #3
0
        private string EncodeShadowsocksServer(ShadowsocksServer server, string groupName = null)
        {
            var userInfo     = WebSafeBase64Encode(Encoding.UTF8.GetBytes($"{server.Method}:{server.Password}"));
            var uri          = new UriBuilder();
            var queryBuilder = new QueryBuilder();

            uri.UserName = userInfo;
            uri.Host     = server.Host;
            uri.Port     = server.Port;
            uri.Path     = "/";
            uri.Fragment = HttpUtility.UrlEncode(server.Name).Replace("+", "%20");
            uri.Scheme   = "ss";

            if (server.PluginOptions is SimpleObfsPluginOptions options)
            {
                var obfsHost = string.IsNullOrEmpty(options.Host) ? Constant.ObfsucationHost : options.Host;
                queryBuilder.Add("plugin", $"obfs-local;obfs={options.Mode.ToString().ToLower()};obfs-host={obfsHost}");
            }

            if (!string.IsNullOrEmpty(groupName))
            {
                queryBuilder.Add("name", WebSafeBase64Encode(Encoding.UTF8.GetBytes(groupName)));
            }

            uri.Query = queryBuilder.ToString().Replace(";", "%3B");

            return(uri.ToString());
        }
예제 #4
0
        /// <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);
        }
예제 #5
0
        public string EncodeShadowsocksServer(ShadowsocksServer server)
        {
            var properties = new List <string>
            {
                $"shadowsocks={server.Host}:{server.Port}",
                $"method={server.Method}",
                $"password={server.Password}",
            };

            switch (server.PluginOptions)
            {
            case V2RayPluginOptions options:
                if (options.Mode == V2RayPluginMode.QUIC)
                {
                    // v2ray-plugin with QUIC is not supported
                    return(null);
                }
                else if (options.Mode == V2RayPluginMode.WebSocket)
                {
                    properties.Add("obfs=" + (options.EnableTLS ? "wss" : "ws"));
                    properties.Add($"obfs-host=" + (string.IsNullOrWhiteSpace(options.Host) ? Constant.ObfsucationHost : options.Host));

                    if (!string.IsNullOrWhiteSpace(options.Path) || options.Path == "/")
                    {
                        properties.Add($"obfs-uri={options.Path}");
                    }
                }
                break;

            case SimpleObfsPluginOptions options:
                properties.Add($"obfs={options.Mode.ToString().ToLower()}");
                properties.Add($"obfs-host=" + (string.IsNullOrWhiteSpace(options.Host) ? Constant.ObfsucationHost : options.Host));

                if (options.Mode == SimpleObfsPluginMode.HTTP)
                {
                    properties.Add($"obfs-uri={options.Uri}");
                }
                break;
            }

            properties.Add($"fast-open={server.FastOpen.ToString().ToLower()}");
            properties.Add($"udp-relay={server.UDPRelay.ToString().ToLower()}");
            properties.Add($"tag={server.Name}");
            return(string.Join(", ", properties));
        }
예제 #6
0
        private string EncodeShadowsocksServer(ShadowsocksServer server)
        {
            var userInfo = WebSafeBase64Encode(Encoding.UTF8.GetBytes($"{server.Method}:{server.Password}"));
            var uri      = new UriBuilder();

            uri.UserName = userInfo;
            uri.Host     = server.Host;
            uri.Port     = server.Port;
            uri.Path     = "/";
            uri.Fragment = HttpUtility.UrlEncode(server.Name).Replace("+", "%20");
            uri.Scheme   = "ss";
            if (server.PluginType == PluginType.SimpleObfs)
            {
                var options  = server.PluginOptions as SimpleObfsPluginOptions;
                var obfsHost = String.IsNullOrEmpty(options.Host) ? Constant.ObfsucationHost : options.Host;
                uri.Query = "plugin=obfs-local" + HttpUtility.UrlEncode($";obfs={options.Mode};obfs-host={obfsHost}");
            }

            return(uri.ToString());
        }
예제 #7
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());
        }
예제 #8
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);
        }
예제 #9
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);
        }
예제 #10
0
        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);
        }
예제 #11
0
        public Server ParseShadowsocksServer(string[] properties)
        {
            if (properties.Length < 3)
            {
                this.logger.LogError("Invaild surge shadowsocks proxy: " + string.Join(", ", properties));
                return(null);
            }

            if (!int.TryParse(properties[2].Trim(), out int port))
            {
                this.logger.LogError("Invalid shadowsocks port: ", properties[2].Trim());
                return(null);
            }
            ;


            var server = new ShadowsocksServer()
            {
                Host = properties[1].Trim(),
                Port = port,
            };

            var keyValues = properties
                            .Skip(3)
                            .Select(p => p.Trim().Split("=", 2))
                            .Where(p => p.Length == 2)
                            .Select(p => new string[] { p[0].TrimEnd(), p[1].TrimStart() });

            string obfs = null, obfsHost = null;

            foreach (var keyValue in keyValues)
            {
                var key = keyValue[0];
                if (key == "psk" || key == "password")
                {
                    server.Password = keyValue[1];
                }
                else if (key == "encrypt-method")
                {
                    server.Method = keyValue[1];
                }
                else if (key == "obfs")
                {
                    obfs = keyValue[1];
                }
                else if (key == "obfs-host")
                {
                    obfsHost = keyValue[1];
                }
                else if (key == "tfo")
                {
                    server.FastOpen = keyValue[1] == "true";
                }
                else if (key == "udp-relay")
                {
                    server.UDPRelay = keyValue[1] == "true";
                }
                else if (key != "interface" && key != "allow-other-interface")
                {
                    this.logger.LogWarning($"Unrecognized shadowsocks options {key}={keyValue[1]}");
                }
            }

            if (server.Password == null)
            {
                this.logger.LogError("Shadowsocks password is missing!");
                return(null);
            }

            if (server.Method == null)
            {
                this.logger.LogError("Shadowsocks method is missing!");
                return(null);
            }

            switch (obfs)
            {
            case "http":
                server.PluginOptions = new SimpleObfsPluginOptions()
                {
                    Mode = SimpleObfsPluginMode.HTTP,
                    Host = obfsHost,
                };
                break;

            case "tls":
                server.PluginOptions = new SimpleObfsPluginOptions()
                {
                    Mode = SimpleObfsPluginMode.TLS,
                    Host = obfsHost,
                };
                break;

            case null:
                break;

            default:
                this.logger.LogError("Unknown shadowsocks obfusaction mode: " + obfs);
                return(null);
            }


            return(server);
        }
예제 #12
0
    public ShadowsocksServer ParseSsUri(string text)
    {
        var data = new ShadowsocksServer();

        text = text.Replace("/?", "?");
        if (text.Contains("#"))
        {
            data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]);
            text        = text.Split('#')[0];
        }

        if (text.Contains("?"))
        {
            var finder = new Regex(@"^(?<data>.+?)\?(.+)$");
            var match  = finder.Match(text);

            if (!match.Success)
            {
                throw new FormatException();
            }

            var plugins = HttpUtility.UrlDecode(HttpUtility.ParseQueryString(new Uri(text).Query).Get("plugin"));
            if (plugins != null)
            {
                var plugin     = plugins.Substring(0, plugins.IndexOf(";", StringComparison.Ordinal));
                var pluginopts = plugins.Substring(plugins.IndexOf(";", StringComparison.Ordinal) + 1);
                switch (plugin)
                {
                case "obfs-local":
                case "simple-obfs":
                    plugin = "simple-obfs";
                    if (!pluginopts.Contains("obfs="))
                    {
                        pluginopts = "obfs=http;obfs-host=" + pluginopts;
                    }

                    break;

                case "simple-obfs-tls":
                    plugin = "simple-obfs";
                    if (!pluginopts.Contains("obfs="))
                    {
                        pluginopts = "obfs=tls;obfs-host=" + pluginopts;
                    }

                    break;
                }

                data.Plugin       = plugin;
                data.PluginOption = pluginopts;
            }

            text = match.Groups["data"].Value;
        }

        if (text.Contains("@"))
        {
            var finder = new Regex(@"^ss://(?<base64>.+?)@(?<server>.+):(?<port>\d+)");
            var parser = new Regex(@"^(?<method>.+?):(?<password>.+)$");
            var match  = finder.Match(text);
            if (!match.Success)
            {
                throw new FormatException();
            }

            data.Hostname = match.Groups["server"].Value;
            data.Port     = ushort.Parse(match.Groups["port"].Value);

            var base64 = ShareLink.URLSafeBase64Decode(match.Groups["base64"].Value);
            match = parser.Match(base64);
            if (!match.Success)
            {
                throw new FormatException();
            }

            data.EncryptMethod = match.Groups["method"].Value;
            data.Password      = match.Groups["password"].Value;
        }
        else
        {
            var parser = new Regex(@"^((?<method>.+?):(?<password>.+)@(?<server>.+):(?<port>\d+))");
            var match  = parser.Match(ShareLink.URLSafeBase64Decode(text.Replace("ss://", "")));
            if (!match.Success)
            {
                throw new FormatException();
            }

            data.Hostname      = match.Groups["server"].Value;
            data.Port          = ushort.Parse(match.Groups["port"].Value);
            data.EncryptMethod = match.Groups["method"].Value;
            data.Password      = match.Groups["password"].Value;
        }

        return(CheckServer(data) ? data : throw new FormatException());
    }