Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support new vmess url, fix ss url #921

Merged
merged 3 commits into from Jul 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
242 changes: 185 additions & 57 deletions v2rayN/v2rayN/Handler/V2rayConfigHandler.cs
@@ -1,8 +1,12 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using v2rayN.Base;
using v2rayN.Mode;

Expand Down Expand Up @@ -392,7 +396,7 @@ private static int outbound(Config config, ref V2rayConfig v2rayConfig)

outbound.mux.enabled = false;
outbound.mux.concurrency = -1;


outbound.protocol = "shadowsocks";
outbound.settings.vnext = null;
Expand Down Expand Up @@ -454,7 +458,7 @@ private static int boundStreamSettings(Config config, string iobound, ref Stream
{
//远程服务器底层传输配置
streamSettings.network = config.network();
string host = config.requestHost();
string host = config.requestHost();
//if tls
if (config.streamSecurity() == Global.StreamSecurity)
{
Expand Down Expand Up @@ -1195,7 +1199,7 @@ public static VmessItem ImportFromClipboardConfig(string clipboardData, out stri
int indexSplit = result.IndexOf("?");
if (indexSplit > 0)
{
vmessItem = ResolveVmess4Kitsunebi(result);
vmessItem = ResolveStdVmess(result) ?? ResolveVmess4Kitsunebi(result);
}
else
{
Expand Down Expand Up @@ -1242,68 +1246,21 @@ public static VmessItem ImportFromClipboardConfig(string clipboardData, out stri
{
msg = UIRes.I18N("ConfigurationFormatIncorrect");

vmessItem.configType = (int)EConfigType.Shadowsocks;
result = result.Substring(Global.ssProtocol.Length);
//remark
int indexRemark = result.IndexOf("#");
if (indexRemark > 0)
vmessItem = ResolveSSLegacy(result);
if (vmessItem == null)
{
try
{
vmessItem.remarks = WebUtility.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
}
catch { }
result = result.Substring(0, indexRemark);
vmessItem = ResolveSip002(result);
}
//part decode
int indexS = result.IndexOf("@");
if (indexS > 0)
{
result = Utils.Base64Decode(result.Substring(0, indexS)) + result.Substring(indexS, result.Length - indexS);
}
else
{
result = Utils.Base64Decode(result);
}

//密码中可能包含“@”,所以从后往前搜索
int indexAddressAndPort = result.LastIndexOf("@");
if (indexAddressAndPort < 0)
if (vmessItem == null)
{
return null;
}
string addressAndPort = result.Substring(indexAddressAndPort + 1);
string securityAndId = result.Substring(0, indexAddressAndPort);

//IPv6地址中包含“:”,所以从后往前搜索
int indexPort = addressAndPort.LastIndexOf(":");
if (indexPort < 0)
if (vmessItem.address.Length == 0 || vmessItem.port == 0 || vmessItem.security.Length == 0 || vmessItem.id.Length == 0)
{
return null;
}

//加密方式中不包含“:”,所以从前往后搜索
int indexId = securityAndId.IndexOf(":");
if (indexId < 0)
{
return null;
}

string address = addressAndPort.Substring(0, indexPort);
string port = addressAndPort.Substring(indexPort + 1);
string security = securityAndId.Substring(0, indexId);
string id = securityAndId.Substring(indexId + 1);

//所有字段均不能为空
if (address.Length == 0 || port.Length == 0 || security.Length == 0 || id.Length == 0)
{
return null;
}

vmessItem.address = address;
vmessItem.port = Utils.ToInt(port);
vmessItem.security = security;
vmessItem.id = id;
vmessItem.configType = (int)EConfigType.Shadowsocks;
}
else if (result.StartsWith(Global.socksProtocol))
{
Expand Down Expand Up @@ -1428,6 +1385,177 @@ private static VmessItem ResolveVmess4Kitsunebi(string result)
return vmessItem;
}

private static VmessItem ResolveSip002(string result)
{
Uri parsedUrl;
try
{
parsedUrl = new Uri(result);
}
catch (UriFormatException)
{
return null;
}
VmessItem server = new VmessItem
{
remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
address = parsedUrl.IdnHost,
port = parsedUrl.Port,
};

// parse base64 UserInfo
string rawUserInfo = parsedUrl.GetComponents(UriComponents.UserInfo, UriFormat.Unescaped);
string base64 = rawUserInfo.Replace('-', '+').Replace('_', '/'); // Web-safe base64 to normal base64
string userInfo;
try
{
userInfo = Encoding.UTF8.GetString(Convert.FromBase64String(
base64.PadRight(base64.Length + (4 - base64.Length % 4) % 4, '=')));
}
catch (FormatException)
{
return null;
}
string[] userInfoParts = userInfo.Split(new char[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
server.security = userInfoParts[0];
server.id = userInfoParts[1];

NameValueCollection queryParameters = HttpUtility.ParseQueryString(parsedUrl.Query);
if (queryParameters["plugin"] != null)
{
return null;
}

return server;
}

private static readonly Regex UrlFinder = new Regex(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase);
private static readonly Regex DetailsParser = new Regex(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase);

private static VmessItem ResolveSSLegacy(string result)
{
var match = UrlFinder.Match(result);
if (!match.Success)
return null;

VmessItem server = new VmessItem();
var base64 = match.Groups["base64"].Value.TrimEnd('/');
var tag = match.Groups["tag"].Value;
if (!tag.IsNullOrEmpty())
{
server.remarks = HttpUtility.UrlDecode(tag, Encoding.UTF8);
}
Match details;
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;
server.security = details.Groups["method"].Value;
server.id = details.Groups["password"].Value;
server.address = details.Groups["hostname"].Value;
server.port = int.Parse(details.Groups["port"].Value);
return server;
}


private static readonly Regex StdVmessUserInfo = new Regex(
@"^(?<network>[a-z]+)(\+(?<streamSecurity>[a-z]+))?:(?<id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})-(?<alterId>[0-9]+)$");

private static VmessItem ResolveStdVmess(string result)
{
VmessItem i = new VmessItem
{
configType = (int)EConfigType.Vmess,
security = "auto"
};

Uri u = new Uri(result);

i.address = u.IdnHost;
i.port = u.Port;
i.remarks = u.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
var q = HttpUtility.ParseQueryString(u.Query);

var m = StdVmessUserInfo.Match(u.UserInfo);
if (!m.Success) return null;

i.id = m.Groups["id"].Value;
if (!int.TryParse(m.Groups["alterId"].Value, out int aid))
{
return null;
}
i.alterId = aid;

if (m.Groups["streamSecurity"].Success)
{
i.streamSecurity = m.Groups["streamSecurity"].Value;
}
switch (i.streamSecurity)
{
case "tls":
// TODO tls config
break;
default:
if (!string.IsNullOrWhiteSpace(i.streamSecurity))
return null;
break;
}

i.network = m.Groups["network"].Value;
switch (i.network)
{
case "tcp":
string t1 = q["type"] ?? "none";
i.headerType = t1;
// TODO http option

break;
case "kcp":
i.headerType = q["type"] ?? "none";
// TODO kcp seed
break;

case "ws":
string p1 = q["path"] ?? "/";
string h1 = q["host"] ?? "";
i.requestHost = h1;
i.path = p1;
break;

case "http":
i.network = "h2";
string p2 = q["path"] ?? "/";
string h2 = q["host"] ?? "";
i.requestHost = h2;
i.path = p2;
break;

case "quic":
string s = q["security"] ?? "none";
string k = q["key"] ?? "";
string t3 = q["type"] ?? "none";
i.headerType = t3;
i.requestHost = s;
i.path = k;
break;

default:
return null;
}

return i;
}
#endregion

#region Gen speedtest config
Expand All @@ -1449,7 +1577,7 @@ public static string GenerateClientSpeedtestConfigString(Config config, List<int

msg = UIRes.I18N("InitialConfiguration");

Config configCopy = Utils.DeepCopy(config);
Config configCopy = Utils.DeepCopy(config);

string result = Utils.GetEmbedText(SampleClient);
if (Utils.IsNullOrEmpty(result))
Expand Down
1 change: 1 addition & 0 deletions v2rayN/v2rayN/v2rayN.csproj
Expand Up @@ -93,6 +93,7 @@
<Reference Include="System.Net" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Web" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
Expand Down