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)); }
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()))); }
private string EncodeShadowsocksRServer(ShadowsocksRServer server, string groupName = null) { // base64(host:port:protocol:method:obfs:base64pass/?group=base64group) var serverInfo = $"{server.Host}:{server.Port}:{server.Protocol}:{server.Method}:{server.Obfuscation}"; var otherInfos = new List <string>() { "remarks=" + WebSafeBase64Encode(Encoding.UTF8.GetBytes(server.Name)) }; if (!string.IsNullOrEmpty(server.ObfuscationParameter)) { otherInfos.Add("obfsparam=" + WebSafeBase64Encode(Encoding.UTF8.GetBytes(server.ObfuscationParameter))); } if (!string.IsNullOrEmpty(server.ProtocolParameter)) { otherInfos.Add("protoparam=" + WebSafeBase64Encode(Encoding.UTF8.GetBytes(server.ProtocolParameter))); } if (server.UDPPort > 0) { otherInfos.Add($"udpport={server.UDPPort}"); } if (server.UDPOverTCP) { otherInfos.Add("uot=1"); } if (!string.IsNullOrEmpty(groupName)) { otherInfos.Add("group=" + WebSafeBase64Encode(Encoding.UTF8.GetBytes(groupName))); } return("ssr://" + WebSafeBase64Encode(Encoding.UTF8.GetBytes(serverInfo + "/?" + string.Join("&", otherInfos)))); }
public string EncodeShadowsocksRServer(ShadowsocksRServer server) { var properties = new List <string>(); properties.Add($"shadowsocks={server.Host}:{server.Port}"); properties.Add($"method={server.Method}"); properties.Add($"password={server.Password}"); properties.Add($"ssr-protocol={server.Protocol}"); properties.Add($"ssr-protocol-param={server.ProtocolParameter}"); properties.Add($"obfs={server.Obfuscation}"); properties.Add($"obfs-host={server.ObfuscationParameter}"); properties.Add($"tag={server.Name}"); return(string.Join(", ", properties)); }
/// <summary> /// Follow scheme https://github.com/shadowsocksr-backup/shadowsocks-rss/wiki/SSR-QRcode-scheme /// to parse a given shadowsocksR server uri. /// </summary> ShadowsocksRServer ParseShadowsocksRURI(string ssrURI) { ssrURI = ssrURI.Substring("ssr://".Length); var splitted = Encoding.UTF8.GetString(WebSafeBase64Decode(HttpUtility.UrlDecode(ssrURI))).Split("/?"); // host:port:protocol:method:obfs:base64pass var serverInfo = splitted[0].Split(":", 6); var server = new ShadowsocksRServer() { Host = serverInfo[0], Port = Convert.ToInt32(serverInfo.ElementAtOrDefault(1) ?? "0"), Protocol = serverInfo.ElementAtOrDefault(2), Method = serverInfo.ElementAtOrDefault(3), Obfuscation = serverInfo.ElementAtOrDefault(4), Password = Encoding.ASCII.GetString(Convert.FromBase64String(serverInfo.ElementAtOrDefault(5) ?? "")), }; // /?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0 var otherInfos = splitted.ElementAtOrDefault(1); if (!string.IsNullOrEmpty(otherInfos)) { var qs = HttpUtility.ParseQueryString(otherInfos); server.ProtocolParameter = qs["protoparam"] != null?Encoding.UTF8.GetString(WebSafeBase64Decode(qs["protoparam"])) : null; server.ObfuscationParameter = qs["obfsparam"] != null?Encoding.UTF8.GetString(WebSafeBase64Decode(qs["obfsparam"])) : null; server.Name = qs["remarks"] != null?Encoding.UTF8.GetString(WebSafeBase64Decode(qs["remarks"])) : null; server.UDPOverTCP = qs["uot"] != null && qs["uot"] != "0"; int UDPPort; if (Int32.TryParse(qs["udpport"], out UDPPort)) { server.UDPPort = UDPPort; } } 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); }