using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using System.Web; using Netch.Models; using Netch.ServerEx.Shadowsocks.Form; using Netch.ServerEx.Shadowsocks.Models.SSD; using Netch.Utils; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace Netch.ServerEx.Shadowsocks { public class SSUtil : IServerUtil { public ushort Priority { get; } = 1; public string TypeName { get; } = "SS"; public string FullName { get; } = "Shadowsocks"; public string[] UriScheme { get; } = {"ss","ssd"}; public Server ParseJObject(JObject j) { return j.ToObject(); } public void Edit(Server s) { new ShadowsocksForm((Shadowsocks) s).ShowDialog(); } public void Create() { new ShadowsocksForm().ShowDialog(); } public string GetShareLink(Server s) { var server = (Shadowsocks) s; // ss://method:password@server:port#Remark return "ss://" + ShareLink.URLSafeBase64Encode($"{server.EncryptMethod}:{server.Password}@{server.Hostname}:{server.Port}") + "#" + HttpUtility.UrlEncode(server.Remark); } public ServerController GetController() { return new SSController(); } public IEnumerable ParseUri(string text) { if (text.StartsWith("ss://")) { return new[] {ParseSsUri(text)}; } if (text.StartsWith("ssd://")) { return ParseSsdUri(text); } return null; } public IEnumerable ParseSsdUri(string s) { var json = JsonConvert.DeserializeObject
(ShareLink.URLSafeBase64Decode(s.Substring(6))); return json.servers.Select(server => new Shadowsocks { Remark = server.remarks, Hostname = server.server, Port = server.port != 0 ? server.port : json.port, Password = server.password ?? json.password, EncryptMethod = server.encryption ?? json.encryption, Plugin = string.IsNullOrEmpty(json.plugin) ? string.IsNullOrEmpty(server.plugin) ? null : server.plugin : json.plugin, PluginOption = string.IsNullOrEmpty(json.plugin_options) ? string.IsNullOrEmpty(server.plugin_options) ? null : server.plugin_options : json.plugin_options }) .Where(CheckServer); } public Shadowsocks ParseSsUri(string text) { var data = new Shadowsocks(); text = text.Replace("/?", "?"); try { if (text.Contains("#")) { data.Remark = HttpUtility.UrlDecode(text.Split('#')[1]); text = text.Split('#')[0]; } if (text.Contains("?")) { var finder = new Regex(@"^(?.+?)\?(.+)$"); var match = finder.Match(text); if (match.Success) { 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; } else { throw new FormatException(); } } if (text.Contains("@")) { var finder = new Regex(@"^ss://(?.+?)@(?.+):(?\d+)"); var parser = new Regex(@"^(?.+?):(?.+)$"); var match = finder.Match(text); if (!match.Success) throw new FormatException(); data.Hostname = match.Groups["server"].Value; data.Port = int.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(@"^((?.+?):(?.+)@(?.+):(?\d+))"); var match = parser.Match(ShareLink.URLSafeBase64Decode(text.Replace("ss://", ""))); if (!match.Success) throw new FormatException(); data.Hostname = match.Groups["server"].Value; data.Port = int.Parse(match.Groups["port"].Value); data.EncryptMethod = match.Groups["method"].Value; data.Password = match.Groups["password"].Value; } return CheckServer(data) ? data : null; } catch (FormatException) { return null; } } public bool CheckServer(Server s) { var server = (Shadowsocks) s; if (!SSGlobal.EncryptMethods.Contains(server.EncryptMethod)) { Logging.Error($"不支持的 SS 加密方式:{server.EncryptMethod}"); { return false; } } return true; } } }