/** * start with "ss://". * Reference code: * https://github.com/shadowsocks/shadowsocks-windows/raw/master/shadowsocks-csharp/Model/Server.cs */ public static ServerProfile ParseLegacyServer(string ssUrl) { var match = UrlFinder.Match(ssUrl); if (!match.Success) { return(null); } ServerProfile serverProfile = new ServerProfile(); var base64 = match.Groups["base64"].Value.TrimEnd('/'); var tag = match.Groups["tag"].Value; if (!string.IsNullOrEmpty(tag)) { serverProfile.vRemarks = HttpUtility.UrlDecode(tag, Encoding.UTF8); } Match details = null; try { details = DetailsParser.Match( Encoding.UTF8.GetString(Convert.FromBase64String(base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=')))); } catch (FormatException) { return(null); } if (!details.Success) { return(null); } serverProfile.vEncrypt = details.Groups["method"].Value; serverProfile.vPassword = details.Groups["password"].Value; serverProfile.vHostIP = details.Groups["hostname"].Value; serverProfile.vPort = int.Parse(details.Groups["port"].Value); serverProfile.SetFriendlyNameDefault(); return(serverProfile); }
public Server(string ssUrl) { if (!ssUrl.StartsWith("ss://", StringComparison.OrdinalIgnoreCase)) { throw new Exception("Invalid"); } if (ssUrl.EndsWith("/?outline=1", StringComparison.OrdinalIgnoreCase)) { var outline = OutlineFinder.Match(ssUrl); if (outline.Success) { var base64 = outline.Groups["base64"].Value.TrimEnd('/'); var spl = base64.DeBase64().Split(':'); if (spl.Length == 2) { method = spl[0]; password = spl[1]; server = outline.Groups["hostname"].Value; server_port = int.Parse(outline.Groups["port"].Value); timeout = DefaultServerTimeoutSec; Logging.Info("Parse Outline Server:" + FriendlyName()); return; } } } if (UriFinder.IsMatch(ssUrl)) {//new format Uri parsedUrl; try { parsedUrl = new Uri(ssUrl); } catch (UriFormatException) { throw new Exception("Invalid"); } remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped); server = parsedUrl.GetComponents(UriComponents.Host, UriFormat.Unescaped); server_port = parsedUrl.Port; var rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped); var base64 = rawUserInfo.Replace('-', '+').Replace('_', '/'); // Web-safe base64 to normal base64 string userInfo; try { userInfo = base64.DeBase64(); } catch (FormatException) { throw new Exception("Invalid"); } var userInfoParts = userInfo.Split(new[] { ':' }, 2); if (userInfoParts.Length != 2) { throw new Exception("Invalid"); } method = userInfoParts[0]; password = userInfoParts[1]; var queryParameters = new NameValueCollection(); if (parsedUrl.Query.StartsWith("?")) { var queryString = parsedUrl.Query.Substring(1, parsedUrl.Query.Length - 1); foreach (string element in queryString.Split('&')) { string[] pair = element.Split('='); if (pair.Length == 2) { string name = Uri.UnescapeDataString(pair[0]); string value = Uri.UnescapeDataString(pair[1]); queryParameters.Add(name, value); } } } var pluginParts = (queryParameters["plugin"] ?? "").Split(new[] { ';' }, 2); if (pluginParts.Length > 0) { plugin = pluginParts[0] ?? ""; } if (pluginParts.Length > 1) { plugin_opts = pluginParts[1] ?? ""; } timeout = DefaultServerTimeoutSec; Logging.Info("Parse Server:" + FriendlyName()); } else { var matchOld = UrlFinder.Match(ssUrl); if (matchOld.Success) { var base64 = matchOld.Groups["base64"].Value.TrimEnd('/'); var tag = matchOld.Groups["tag"].Value; if (!string.IsNullOrEmpty(tag)) { remarks = Uri.UnescapeDataString(tag); } Match details; try { details = DetailsParser.Match(base64.DeBase64()); } catch (FormatException) { throw new Exception("Invalid"); } if (!details.Success) { throw new Exception("Invalid"); } method = details.Groups["method"].Value; password = details.Groups["password"].Value; server = details.Groups["hostname"].Value; server_port = int.Parse(details.Groups["port"].Value); timeout = DefaultServerTimeoutSec; Logging.Info("Parse Classic Server:" + FriendlyName()); } else { throw new FormatException("ParseFail:" + ssUrl); } } }