PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/shadowsocks-csharp/Model/Server.cs

https://gitlab.com/Mirros/shadowsocks-windows
C# | 194 lines | 173 code | 19 blank | 2 comment | 16 complexity | 000878b8078265b26eca5119b7319027 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.Specialized;
  4. using System.Text;
  5. using System.Web;
  6. using Shadowsocks.Controller;
  7. using System.Text.RegularExpressions;
  8. namespace Shadowsocks.Model
  9. {
  10. [Serializable]
  11. public class Server
  12. {
  13. #region ParseLegacyURL
  14. public static readonly Regex
  15. UrlFinder = new Regex(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase),
  16. DetailsParser = new Regex(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase);
  17. #endregion ParseLegacyURL
  18. private const int DefaultServerTimeoutSec = 5;
  19. public const int MaxServerTimeoutSec = 20;
  20. public string server;
  21. public int server_port;
  22. public string password;
  23. public string method;
  24. public string plugin;
  25. public string plugin_opts;
  26. public string plugin_args;
  27. public string remarks;
  28. public int timeout;
  29. public override int GetHashCode()
  30. {
  31. return server.GetHashCode() ^ server_port;
  32. }
  33. public override bool Equals(object obj)
  34. {
  35. Server o2 = (Server)obj;
  36. return server == o2.server && server_port == o2.server_port;
  37. }
  38. public string FriendlyName()
  39. {
  40. if (server.IsNullOrEmpty())
  41. {
  42. return I18N.GetString("New server");
  43. }
  44. string serverStr = $"{FormatHostName(server)}:{server_port}";
  45. return remarks.IsNullOrEmpty()
  46. ? serverStr
  47. : $"{remarks} ({serverStr})";
  48. }
  49. public string FormatHostName(string hostName)
  50. {
  51. // CheckHostName() won't do a real DNS lookup
  52. switch (Uri.CheckHostName(hostName))
  53. {
  54. case UriHostNameType.IPv6: // Add square bracket when IPv6 (RFC3986)
  55. return $"[{hostName}]";
  56. default: // IPv4 or domain name
  57. return hostName;
  58. }
  59. }
  60. public Server()
  61. {
  62. server = "";
  63. server_port = 8388;
  64. method = "aes-256-cfb";
  65. plugin = "";
  66. plugin_opts = "";
  67. plugin_args = "";
  68. password = "";
  69. remarks = "";
  70. timeout = DefaultServerTimeoutSec;
  71. }
  72. private static Server ParseLegacyURL(string ssURL)
  73. {
  74. var match = UrlFinder.Match(ssURL);
  75. if (!match.Success)
  76. return null;
  77. Server server = new Server();
  78. var base64 = match.Groups["base64"].Value.TrimEnd('/');
  79. var tag = match.Groups["tag"].Value;
  80. if (!tag.IsNullOrEmpty())
  81. {
  82. server.remarks = HttpUtility.UrlDecode(tag, Encoding.UTF8);
  83. }
  84. Match details = null;
  85. try
  86. {
  87. details = DetailsParser.Match(Encoding.UTF8.GetString(Convert.FromBase64String(
  88. base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '='))));
  89. }
  90. catch (FormatException)
  91. {
  92. return null;
  93. }
  94. if (!details.Success)
  95. return null;
  96. server.method = details.Groups["method"].Value;
  97. server.password = details.Groups["password"].Value;
  98. server.server = details.Groups["hostname"].Value;
  99. server.server_port = int.Parse(details.Groups["port"].Value);
  100. return server;
  101. }
  102. public static List<Server> GetServers(string ssURL)
  103. {
  104. var serverUrls = ssURL.Split('\r', '\n', ' ');
  105. List<Server> servers = new List<Server>();
  106. foreach (string serverUrl in serverUrls)
  107. {
  108. string _serverUrl = serverUrl.Trim();
  109. if (!_serverUrl.BeginWith("ss://", StringComparison.InvariantCultureIgnoreCase))
  110. {
  111. continue;
  112. }
  113. Server legacyServer = ParseLegacyURL(serverUrl);
  114. if (legacyServer != null) //legacy
  115. {
  116. servers.Add(legacyServer);
  117. }
  118. else //SIP002
  119. {
  120. Uri parsedUrl;
  121. try
  122. {
  123. parsedUrl = new Uri(serverUrl);
  124. }
  125. catch (UriFormatException)
  126. {
  127. continue;
  128. }
  129. Server server = new Server
  130. {
  131. remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
  132. server = parsedUrl.IdnHost,
  133. server_port = parsedUrl.Port,
  134. };
  135. // parse base64 UserInfo
  136. string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
  137. string base64 = rawUserInfo.Replace('-', '+').Replace('_', '/'); // Web-safe base64 to normal base64
  138. string userInfo = "";
  139. try
  140. {
  141. userInfo = Encoding.UTF8.GetString(Convert.FromBase64String(
  142. base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=')));
  143. }
  144. catch (FormatException)
  145. {
  146. continue;
  147. }
  148. string[] userInfoParts = userInfo.Split(new char[] { ':' }, 2);
  149. if (userInfoParts.Length != 2)
  150. {
  151. continue;
  152. }
  153. server.method = userInfoParts[0];
  154. server.password = userInfoParts[1];
  155. NameValueCollection queryParameters = HttpUtility.ParseQueryString(parsedUrl.Query);
  156. string[] pluginParts = (queryParameters["plugin"] ?? "").Split(new[] { ';' }, 2);
  157. if (pluginParts.Length > 0)
  158. {
  159. server.plugin = pluginParts[0] ?? "";
  160. }
  161. if (pluginParts.Length > 1)
  162. {
  163. server.plugin_opts = pluginParts[1] ?? "";
  164. }
  165. servers.Add(server);
  166. }
  167. }
  168. return servers;
  169. }
  170. public string Identifier()
  171. {
  172. return server + ':' + server_port;
  173. }
  174. }
  175. }