mirror of
https://github.com/netchx/netch.git
synced 2026-05-11 23:45:06 +08:00
Feat #517 new VMess/VLESS ShareLink format get and parse
This commit is contained in:
@@ -3,106 +3,16 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Netch.Models;
|
||||
using Netch.Servers.Shadowsocks;
|
||||
using Netch.Servers.Shadowsocks.Models;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Server = Netch.Models.Server;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class ShareLink
|
||||
{
|
||||
#region Utils
|
||||
|
||||
/// <summary>
|
||||
/// URL 传输安全的 Base64 解码
|
||||
/// </summary>
|
||||
/// <param name="text">需要解码的字符串</param>
|
||||
/// <returns>解码后的字符串</returns>
|
||||
public static string URLSafeBase64Decode(string text)
|
||||
{
|
||||
return Encoding.UTF8.GetString(Convert.FromBase64String(text.Replace("-", "+").Replace("_", "/").PadRight(text.Length + (4 - text.Length % 4) % 4, '=')));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URL 传输安全的 Base64 加密
|
||||
/// </summary>
|
||||
/// <param name="text">需要加密的字符串</param>
|
||||
/// <returns>加密后的字符串</returns>
|
||||
public static string URLSafeBase64Encode(string text)
|
||||
{
|
||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(text)).Replace("+", "-").Replace("/", "_").Replace("=", "");
|
||||
}
|
||||
|
||||
private static string RemoveEmoji(string text)
|
||||
{
|
||||
byte[] emojiBytes = {240, 159};
|
||||
var remark = Encoding.UTF8.GetBytes(text);
|
||||
var startIndex = 0;
|
||||
while (remark.Length > startIndex + 1 && remark[startIndex] == emojiBytes[0] && remark[startIndex + 1] == emojiBytes[1])
|
||||
startIndex += 4;
|
||||
return Encoding.UTF8.GetString(remark.Skip(startIndex).ToArray()).Trim();
|
||||
}
|
||||
|
||||
|
||||
public static string UnBase64String(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var bytes = Convert.FromBase64String(value);
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
public static string ToBase64String(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(value);
|
||||
return Convert.ToBase64String(bytes);
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> ParseParam(string paramStr)
|
||||
{
|
||||
var paramsDict = new Dictionary<string, string>();
|
||||
var obfsParams = paramStr.Split('&');
|
||||
foreach (var p in obfsParams)
|
||||
{
|
||||
if (p.IndexOf('=') > 0)
|
||||
{
|
||||
var index = p.IndexOf('=');
|
||||
var key = p.Substring(0, index);
|
||||
var val = p.Substring(index + 1);
|
||||
paramsDict[key] = val;
|
||||
}
|
||||
}
|
||||
|
||||
return paramsDict;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetLines(this string str, bool removeEmptyLines = true)
|
||||
{
|
||||
using var sr = new StringReader(str);
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
if (removeEmptyLines && string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static string GetShareLink(Server server)
|
||||
{
|
||||
return ServerHelper.GetUtilByTypeName(server.Type).GetShareLink(server);
|
||||
@@ -137,9 +47,7 @@ namespace Netch.Utils
|
||||
catch (JsonReaderException)
|
||||
{
|
||||
foreach (var line in text.GetLines())
|
||||
{
|
||||
list.AddRange(ParseUri(line));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -168,13 +76,9 @@ namespace Netch.Utils
|
||||
var scheme = GetUriScheme(text);
|
||||
var util = ServerHelper.GetUtilByUriScheme(scheme);
|
||||
if (util != null)
|
||||
{
|
||||
list.AddRange(util.ParseUri(text));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Warning($"无法处理 {scheme} 协议订阅链接");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -183,14 +87,13 @@ namespace Netch.Utils
|
||||
}
|
||||
|
||||
foreach (var node in list)
|
||||
{
|
||||
node.Remark = RemoveEmoji(node.Remark);
|
||||
}
|
||||
if (!node.Remark.IsNullOrWhiteSpace())
|
||||
node.Remark = RemoveEmoji(node.Remark);
|
||||
|
||||
return list.Where(s => s != null);
|
||||
}
|
||||
|
||||
private static string GetUriScheme(string text)
|
||||
public static string GetUriScheme(string text)
|
||||
{
|
||||
var endIndex = text.IndexOf("://", StringComparison.Ordinal);
|
||||
if (endIndex == -1)
|
||||
@@ -206,19 +109,13 @@ namespace Netch.Utils
|
||||
|
||||
var NetchLink = (JObject) JsonConvert.DeserializeObject(URLSafeBase64Decode(text));
|
||||
if (NetchLink == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty((string) NetchLink["Hostname"]))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ushort.TryParse((string) NetchLink["Port"], out _))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var type = (string) NetchLink["Type"];
|
||||
var s = ServerHelper.GetUtilByTypeName(type).ParseJObject(NetchLink);
|
||||
@@ -236,5 +133,86 @@ namespace Netch.Utils
|
||||
var server = (Server) s.Clone();
|
||||
return "Netch://" + URLSafeBase64Encode(JsonConvert.SerializeObject(server, new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}));
|
||||
}
|
||||
|
||||
#region Utils
|
||||
|
||||
/// <summary>
|
||||
/// URL 传输安全的 Base64 解码
|
||||
/// </summary>
|
||||
/// <param name="text">需要解码的字符串</param>
|
||||
/// <returns>解码后的字符串</returns>
|
||||
public static string URLSafeBase64Decode(string text)
|
||||
{
|
||||
return Encoding.UTF8.GetString(Convert.FromBase64String(text.Replace("-", "+").Replace("_", "/").PadRight(text.Length + (4 - text.Length % 4) % 4, '=')));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// URL 传输安全的 Base64 加密
|
||||
/// </summary>
|
||||
/// <param name="text">需要加密的字符串</param>
|
||||
/// <returns>加密后的字符串</returns>
|
||||
public static string URLSafeBase64Encode(string text)
|
||||
{
|
||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(text)).Replace("+", "-").Replace("/", "_").Replace("=", "");
|
||||
}
|
||||
|
||||
private static string RemoveEmoji(string text)
|
||||
{
|
||||
byte[] emojiBytes = {240, 159};
|
||||
var remark = Encoding.UTF8.GetBytes(text);
|
||||
var startIndex = 0;
|
||||
while (remark.Length > startIndex + 1 && remark[startIndex] == emojiBytes[0] && remark[startIndex + 1] == emojiBytes[1])
|
||||
startIndex += 4;
|
||||
return Encoding.UTF8.GetString(remark.Skip(startIndex).ToArray()).Trim();
|
||||
}
|
||||
|
||||
public static string UnBase64String(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return "";
|
||||
|
||||
var bytes = Convert.FromBase64String(value);
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
public static string ToBase64String(string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return "";
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(value);
|
||||
return Convert.ToBase64String(bytes);
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> ParseParam(string paramStr)
|
||||
{
|
||||
var paramsDict = new Dictionary<string, string>();
|
||||
var obfsParams = paramStr.Split('&');
|
||||
foreach (var p in obfsParams)
|
||||
if (p.IndexOf('=') > 0)
|
||||
{
|
||||
var index = p.IndexOf('=');
|
||||
var key = p.Substring(0, index);
|
||||
var val = p.Substring(index + 1);
|
||||
paramsDict[key] = val;
|
||||
}
|
||||
|
||||
return paramsDict;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetLines(this string str, bool removeEmptyLines = true)
|
||||
{
|
||||
using var sr = new StringReader(str);
|
||||
string line;
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
if (removeEmptyLines && string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
yield return line;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user