Skip to content

Commit

Permalink
Merge pull request #921 from studentmain/stdurl
Browse files Browse the repository at this point in the history
support new vmess url, fix ss url
  • Loading branch information
2dust authored Jul 28, 2020
2 parents 5c9c35c + 4e8ea37 commit 153c8a0
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 57 deletions.
242 changes: 185 additions & 57 deletions v2rayN/v2rayN/Handler/V2rayConfigHandler.cs
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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

0 comments on commit 153c8a0

Please sign in to comment.