subconverter/speedtestutil.cpp
Tindy X abada4ec8b
Enhancements
Fix compatibility with some non-standard ShadowsocksR subscription.
Add local rulesets from ConnersHua, lhie1 and NobyDa.
Add support for conversion of some nodes between Shadowsocks and ShadowsocksR format.
Add some build scripts.
Optimize default groups and rulesets.
Optimize Gist uploader.
Optimize codes and performance.
2019-11-19 21:37:18 +08:00

1585 lines
62 KiB
C++

#include <fstream>
#include <algorithm>
#include <rapidjson/document.h>
#include "misc.h"
#include "printout.h"
#include "logger.h"
#include "speedtestutil.h"
#include "webget.h"
#include "rapidjson_extra.h"
#include "ini_reader.h"
using namespace std::__cxx11;
using namespace rapidjson;
using namespace YAML;
string_array exclude_remarks;
string_array include_remarks;
std::string base_ss_win = "{\"version\":\"4.1.6\",\"configs\":[?config?],\"strategy\":null,\"index\":0,\"global\":false,\"enabled\":false,\"shareOverLan\":true,\"isDefault\":false,\"localPort\":?localport?,\"portableMode\":true,\"pacUrl\":null,\"useOnlinePac\":false,\"secureLocalPac\":true,\"availabilityStatistics\":false,\"autoCheckUpdate\":true,\"checkPreRelease\":false,\"isVerboseLogging\":false,\"logViewer\":{\"topMost\":false,\"wrapText\":false,\"toolbarShown\":false,\"Font\":\"Consolas, 8pt\",\"BackgroundColor\":\"Black\",\"TextColor\":\"White\"},\"proxy\":{\"useProxy\":false,\"proxyType\":0,\"proxyServer\":\"\",\"proxyPort\":0,\"proxyTimeout\":3,\"useAuth\":false,\"authUser\":\"\",\"authPwd\":\"\"},\"hotkey\":{\"SwitchSystemProxy\":\"\",\"SwitchSystemProxyMode\":\"\",\"SwitchAllowLan\":\"\",\"ShowLogs\":\"\",\"ServerMoveUp\":\"\",\"ServerMoveDown\":\"\",\"RegHotkeysAtStartup\":false}}";
std::string config_ss_win = "{\"server\":\"?server?\",\"server_port\":?port?,\"password\":\"?password?\",\"method\":\"?method?\",\"plugin\":\"?plugin?\",\"plugin_opts\":\"?plugin_opts?\",\"plugin_args\":\"\",\"remarks\":\"?remarks?\",\"timeout\":5}";
std::string config_ss_libev = "{\"server\":\"?server?\",\"server_port\":?port?,\"password\":\"?password?\",\"method\":\"?method?\",\"plugin\":\"?plugin?\",\"plugin_opts\":\"?plugin_opts?\",\"plugin_args\":\"\",\"local_address\":\"127.0.0.1\",\"local_port\":?localport?}";
std::string base_ssr_win = "{\"configs\":[?config?],\"index\":0,\"random\":true,\"sysProxyMode\":1,\"shareOverLan\":false,\"localPort\":?localport?,\"localAuthPassword\":null,\"localDnsServer\":\"\",\"dnsServer\":\"\",\"reconnectTimes\":2,\"balanceAlgorithm\":\"LowException\",\"randomInGroup\":false,\"TTL\":0,\"connectTimeout\":5,\"proxyRuleMode\":2,\"proxyEnable\":false,\"pacDirectGoProxy\":false,\"proxyType\":0,\"proxyHost\":null,\"proxyPort\":0,\"proxyAuthUser\":null,\"proxyAuthPass\":null,\"proxyUserAgent\":null,\"authUser\":null,\"authPass\":null,\"autoBan\":false,\"checkSwitchAutoCloseAll\":false,\"logEnable\":false,\"sameHostForSameTarget\":false,\"keepVisitTime\":180,\"isHideTips\":false,\"nodeFeedAutoUpdate\":true,\"serverSubscribes\":[],\"token\":{},\"portMap\":{}}";
std::string config_ssr_win = "{\"remarks\":\"?remarks?\",\"id\":\"18C4949EBCFE46687AE4A7645725D35F\",\"server\":\"?server?\",\"server_port\":?port?,\"server_udp_port\":0,\"password\":\"?password?\",\"method\":\"?method?\",\"protocol\":\"?protocol?\",\"protocolparam\":\"?protoparam?\",\"obfs\":\"?obfs?\",\"obfsparam\":\"?obfsparam?\",\"remarks_base64\":\"?remarks_base64?\",\"group\":\"?group?\",\"enable\":true,\"udp_over_tcp\":false}";
std::string config_ssr_libev = "{\"server\":\"?server?\",\"server_port\":?port?,\"protocol\":\"?protocol?\",\"method\":\"?method?\",\"obfs\":\"?obfs?\",\"password\":\"?password?\",\"obfs_param\":\"?obfsparam?\",\"protocol_param\":\"?protoparam?\",\"local_address\":\"127.0.0.1\",\"local_port\":?localport?}";
std::string base_vmess = "{\"inbounds\":[{\"port\":?localport?,\"listen\":\"127.0.0.1\",\"protocol\":\"socks\"}],\"outbounds\":[{\"tag\":\"proxy\",\"protocol\":\"vmess\",\"settings\":{\"vnext\":[{\"address\":\"?add?\",\"port\":?port?,\"users\":[{\"id\":\"?id?\",\"alterId\":?aid?,\"email\":\"t@t.tt\",\"security\":\"?cipher?\"}]}]},\"streamSettings\":{\"network\":\"?net?\",\"security\":\"?tls?\",\"tlsSettings\":?tlsset?,\"tcpSettings\":?tcpset?,\"wsSettings\":?wsset?},\"mux\":{\"enabled\":true}}],\"routing\":{\"domainStrategy\":\"IPIfNonMatch\"}}";
std::string wsset_vmess = "{\"connectionReuse\":true,\"path\":\"?path?\",\"headers\":{\"Host\":\"?host?\"}}";
std::string tcpset_vmess = "{\"connectionReuse\":true,\"header\":{\"type\":\"?type?\",\"request\":{\"version\":\"1.1\",\"method\":\"GET\",\"path\":[\"?path?\"],\"headers\":{\"Host\":[\"?host?\"],\"User-Agent\":[\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36\",\"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46\"],\"Accept-Encoding\":[\"gzip, deflate\"],\"Connection\":[\"keep-alive\"],\"Pragma\":\"no-cache\"}}}}";
std::string tlsset_vmess = "{\"serverName\":\"?serverName?\",\"allowInsecure\":false,\"allowInsecureCiphers\":false}";
string_array ss_ciphers = {"rc4-md5", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "bf-cfb", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "salsa20", "chacha20", "chacha20-ietf"};
string_array ssr_ciphers = {"none", "table", "rc4", "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "cast5-cfb", "des-cfb", "idea-cfb", "rc2-cfb", "seed-cfb", "salsa20", "chacha20", "chacha20-ietf"};
std::map<std::string, std::string> parsedMD5;
std::string modSSMD5 = "f7653207090ce3389115e9c88541afe0";
//remake from speedtestutil
template <typename T> void operator >> (const YAML::Node& node, T& i)
{
i = node.as<T>();
};
/*
std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string tls, int local_port)
{
std::string base = base_vmess;
std::string wsset = wsset_vmess;
std::string tcpset = tcpset_vmess;
std::string tlsset = tlsset_vmess;
base = replace_all_distinct(base, "?localport?", to_string(local_port));
base = replace_all_distinct(base, "?add?", add);
base = replace_all_distinct(base, "?port?", port);
base = replace_all_distinct(base, "?id?", id);
base = replace_all_distinct(base, "?aid?", aid);
base = replace_all_distinct(base, "?net?", net);
base = replace_all_distinct(base, "?cipher?", cipher);
if(net == "ws")
{
wsset = replace_all_distinct(wsset, "?host?", host == "" ? add : host);
wsset = replace_all_distinct(wsset, "?path?", path == "" ? "/" : path);
base = replace_all_distinct(base, "?wsset?", wsset);
}
if(type == "http")
{
tcpset = replace_all_distinct(tcpset, "?host?", host == "" ? add : host);
tcpset = replace_all_distinct(tcpset, "?type?", type);
tcpset = replace_all_distinct(tcpset, "?path?", path == "" ? "/" : path);
base = replace_all_distinct(base, "?tcpset?", tcpset);
}
if(host == "" && !isIPv4(add) && !isIPv6(add))
host = add;
if(host != "")
{
tlsset = replace_all_distinct(tlsset, "?serverName?", host);
base = replace_all_distinct(base, "?tlsset?", tlsset);
}
base = replace_all_distinct(base, "?tls?", tls);
base = replace_all_distinct(base, "?tcpset?", "null");
base = replace_all_distinct(base, "?wsset?", "null");
base = replace_all_distinct(base, "?tlsset?", "null");
return base;
}
std::string ssrConstruct(std::string group, std::string remarks, std::string remarks_base64, std::string server, std::string port, std::string protocol, std::string method, std::string obfs, std::string password, std::string obfsparam, std::string protoparam, int local_port, bool libev)
{
std::string base = base_ssr_win;
std::string config = config_ssr_win;
std::string config_libev = config_ssr_libev;
if(libev == true)
config = config_libev;
if(isIPv6(server))
server = "[" + server + "]";
config = replace_all_distinct(config, "?group?", group);
config = replace_all_distinct(config, "?remarks?", remarks);
config = replace_all_distinct(config, "?remarks_base64?", remarks_base64);
config = replace_all_distinct(config, "?server?", server);
config = replace_all_distinct(config, "?port?", port);
config = replace_all_distinct(config, "?protocol?", protocol);
config = replace_all_distinct(config, "?method?", method);
config = replace_all_distinct(config, "?obfs?", obfs);
config = replace_all_distinct(config, "?password?", password);
config = replace_all_distinct(config, "?obfsparam?", obfsparam);
config = replace_all_distinct(config, "?protoparam?", protoparam);
if(libev == true)
base = config;
else
base = replace_all_distinct(base, "?config?", config);
base = replace_all_distinct(base, "?localport?", to_string(local_port));
return base;
}
std::string ssConstruct(std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, std::string remarks, int local_port, bool libev)
{
std::string base = base_ss_win;
std::string config = config_ss_win;
std::string config_libev = config_ss_libev;
if(plugin == "obfs-local")
plugin = "simple-obfs";
if(libev == true)
config = config_libev;
if(isIPv6(server))
server = "[" + server + "]";
config = replace_all_distinct(config, "?server?", server);
config = replace_all_distinct(config, "?port?", port);
config = replace_all_distinct(config, "?password?", password);
config = replace_all_distinct(config, "?method?", method);
config = replace_all_distinct(config, "?plugin?", plugin);
config = replace_all_distinct(config, "?plugin_opts?", pluginopts);
config = replace_all_distinct(config, "?remarks?", remarks);
if(libev == true)
base = config;
else
base = replace_all_distinct(base, "?config?", config);
base = replace_all_distinct(base, "?localport?", to_string(local_port));
return base;
}
std::string socksConstruct(std::string remarks, std::string server, std::string port, std::string username, std::string password)
{
return "user=" + username + "&pass=" + password;
}
*/
void explodeVmess(std::string vmess, std::string custom_port, int local_port, nodeInfo &node)
{
std::string version, ps, add, port, type, id, aid, net, path, host, tls;
Document jsondata;
std::vector<std::string> vArray;
if(regMatch(vmess, "vmess://(.*?)\\?(.*)")) //kitsunebi style link
{
explodeKitsunebi(vmess, custom_port, local_port, node);
return;
}
else if(regMatch(vmess, "vmess1://(.*?)\\?(.*)")) // new kitsunebi style link
{
explodeKitsunebiNew(vmess, custom_port, local_port, node);
return;
}
vmess = urlsafe_base64_decode(regReplace(vmess, "(vmess|vmess1)://", ""));
if(regMatch(vmess, "(.*?) = (.*)"))
{
explodeQuan(vmess, custom_port, local_port, node);
return;
}
jsondata.Parse(vmess.data());
version = "1"; //link without version will treat as version 1
GetMember(jsondata, "v", version); //try to get version
GetMember(jsondata, "ps", ps);
GetMember(jsondata, "add", add);
GetMember(jsondata, "type", type);
GetMember(jsondata, "id", id);
GetMember(jsondata, "aid", aid);
GetMember(jsondata, "net", net);
GetMember(jsondata, "tls", tls);
if(custom_port != "")
port = custom_port;
else
GetMember(jsondata, "port", port);
GetMember(jsondata, "host", host);
if(version == "1")
{
if(host != "")
{
vArray = split(host, ";");
if(vArray.size() == 2)
{
host = vArray[0];
path = vArray[1];
}
}
}
else if(version == "2")
{
path = GetMember(jsondata, "path");
}
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.remarks = ps;
node.server = add;
node.port = stoi(port);
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, "auto", path, host, tls, local_port);
}
void explodeVmessConf(std::string content, std::string custom_port, int local_port, bool libev, std::vector<nodeInfo> &nodes)
{
nodeInfo node;
Document json;
std::string group, ps, add, port, type, id, aid, net, path, host, tls, cipher, subid;
int configType, index = nodes.size();
std::map<std::string, std::string> subdata;
std::map<std::string, std::string>::iterator iter;
json.Parse(content.data());
if(json.HasMember("outbounds")) //single config
{
if(json["outbounds"].Size() > 0 && json["outbounds"][0].HasMember("settings") && json["outbounds"][0]["settings"].HasMember("vnext") && json["outbounds"][0]["settings"]["vnext"].Size() > 0)
{
add = GetMember(json["outbounds"][0]["settings"]["vnext"][0], "address");
port = GetMember(json["outbounds"][0]["settings"]["vnext"][0], "port");
json["routing"].RemoveAllMembers();
json["routing"].AddMember("domainStrategy", "IPIfNonMatch", json.GetAllocator());
rapidjson::Value& inbounds = json["inbounds"];
rapidjson::Document newinbound(Type::kObjectType);
newinbound.AddMember("listen", "127.0.0.1", json.GetAllocator());
newinbound.AddMember("port", local_port, json.GetAllocator());
newinbound.AddMember("protocol", "socks", json.GetAllocator());
inbounds.Clear();
inbounds.PushBack(newinbound, json.GetAllocator());
node.proxyStr = SerializeObject(json);
node.group = V2RAY_DEFAULT_GROUP;
node.remarks = add + ":" + port;
node.server = add;
node.port = stoi(port);
nodes.push_back(node);
}
return;
}
//read all subscribe remark as group name
for(unsigned int i = 0; i < json["subItem"].Size(); i++)
{
subdata.insert(std::pair<std::string, std::string>(json["subItem"][i]["id"].GetString(), json["subItem"][i]["remarks"].GetString()));
}
for(unsigned int i = 0; i < json["vmess"].Size(); i++)
{
if(json["vmess"][i]["address"].IsNull() || json["vmess"][i]["port"].IsNull() || json["vmess"][i]["id"].IsNull())
{
continue;
}
//common info
json["vmess"][i]["remarks"] >> ps;
json["vmess"][i]["address"] >> add;
if(custom_port != "")
port = custom_port;
else
json["vmess"][i]["port"] >>port;
json["vmess"][i]["subid"] >> subid;
if(subid != "")
{
iter = subdata.find(subid);
if(iter != subdata.end())
{
group = iter->second;
}
}
if(ps == "")
{
ps = add + ":" + port;
}
json["vmess"][i]["configType"] >> configType;
switch(configType)
{
case 1: //vmess config
json["vmess"][i]["headerType"] >> type;
json["vmess"][i]["id"] >> id;
json["vmess"][i]["alterId"] >> aid;
json["vmess"][i]["network"] >> net;
json["vmess"][i]["path"] >> path;
json["vmess"][i]["requestHost"] >> host;
json["vmess"][i]["streamSecurity"] >> tls;
json["vmess"][i]["security"] >> cipher;
group = V2RAY_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, tls, local_port);
break;
case 3: //ss config
json["vmess"][i]["id"] >> id;
json["vmess"][i]["security"] >> cipher;
group = SS_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(add, port, id, cipher, "", "", ps, local_port, true);
break;
case 4: //socks config
group = SOCKS_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.proxyStr = socksConstruct(ps, add, port, "", "");
break;
default:
continue;
}
node.group = group;
node.remarks = ps;
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.id = index;
node.server = add;
node.port = stoi(port);
nodes.push_back(node);
}
return;
}
void explodeSS(std::string ss, bool libev, std::string custom_port, int local_port, nodeInfo &node)
{
std::string ps, password, method, server, port, plugins, plugin, pluginopts, addition, group = SS_DEFAULT_GROUP;
std::vector<std::string> args, secret;
ss = replace_all_distinct(replace_all_distinct(ss.substr(5), "\r", ""), "/?", "?");
if(strFind(ss, "#"))
{
ps = UrlDecode(ss.substr(ss.find("#") + 1));
ss = ss.substr(0, ss.find("#"));
}
if(strFind(ss, "?"))
{
addition = ss.substr(ss.find("?") + 1);
plugins = UrlDecode(getUrlArg(addition, "plugin"));
plugin = plugins.substr(0, plugins.find(";"));
pluginopts = plugins.substr(plugins.find(";") + 1);
if(getUrlArg(addition, "group") != "")
group = urlsafe_base64_decode(getUrlArg(addition, "group"));
ss = ss.substr(0, ss.find("?"));
}
if(strFind(ss, "@"))
{
ss = regReplace(ss, "(.*?)@(.*):(.*)", "$1,$2,$3");
args = split(ss, ",");
secret = split(urlsafe_base64_decode(args[0]), ":");
method = secret[0];
password = secret[1];
server = args[1];
port = custom_port == "" ? args[2] : custom_port;
}
else
{
ss = regReplace(urlsafe_base64_decode(ss), "(.*?):(.*?)@(.*):(.*)", "$1,$2,$3,$4");
args = split(ss, ",");
method = args[0];
password = args[1];
server = args[2];
port = custom_port == "" ? args[3] : custom_port;
}
if(ps == "")
ps = server + ":" + port;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = group;
node.remarks = ps;
node.server = server;
node.port = stoi(port);
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, ps, local_port, libev);
}
void explodeSSD(std::string link, bool libev, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes)
{
Document jsondata;
nodeInfo node;
unsigned int index = nodes.size();
std::string group, port, method, password, server, remarks;
std::string plugin, pluginopts;
link = urlsafe_base64_decode(link.substr(6));
jsondata.Parse(link.c_str());
if(!jsondata.HasMember("servers"))
return;
GetMember(jsondata, "airport", group);
for(unsigned int i = 0; i < jsondata["servers"].Size(); i++)
{
//get default info
GetMember(jsondata, "port", port);
GetMember(jsondata, "encryption", method);
GetMember(jsondata, "password", password);
GetMember(jsondata, "plugin", plugin);
GetMember(jsondata, "plugin_options", pluginopts);
//get server-specific info
jsondata["servers"][i]["server"] >> server;
GetMember(jsondata["servers"][i], "remarks", remarks);
GetMember(jsondata["servers"][i], "port", port);
GetMember(jsondata["servers"][i], "encryption", method);
GetMember(jsondata["servers"][i], "password", password);
GetMember(jsondata["servers"][i], "plugin", plugin);
GetMember(jsondata["servers"][i], "plugin_options", pluginopts);
if(custom_port != "")
port = custom_port;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = group;
node.remarks = remarks;
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.server = server;
node.port = stoi(port);
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, local_port, libev);
node.id = index;
nodes.push_back(node);
index++;
}
return;
}
void explodeSSAndroid(std::string ss, bool libev, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes)
{
Document json;
nodeInfo node;
std::string ps, password, method, server, port, group = SS_DEFAULT_GROUP;
int index = nodes.size();
//first add some extra data before parsing
ss = "{\"nodes\":" + ss + "}";
json.Parse(ss.data());
for(unsigned int i = 0; i < json["nodes"].Size(); i++)
{
json["nodes"][i]["remarks"] >> ps;
json["nodes"][i]["server"] >> server;
if(custom_port != "")
port = custom_port;
else
json["nodes"][i]["server_port"] >> port;
if(ps == "")
ps = server + ":" + port;
json["nodes"][i]["password"] >> password;
json["nodes"][i]["method"] >> method;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.id = index;
node.group = group;
node.remarks = ps;
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.server = server;
node.port = stoi(port);
node.proxyStr = ssConstruct(server, port, password, method, "", "", ps, local_port, libev);
nodes.push_back(node);
index++;
}
}
void explodeSSConf(std::string content, std::string custom_port, int local_port, bool libev, std::vector<nodeInfo> &nodes)
{
nodeInfo node;
Document json;
std::string ps, password, method, server, port, plugin, pluginopts, group = SS_DEFAULT_GROUP;
int index = nodes.size();
json.Parse(content.data());
if(json.HasMember("local_port") && json.HasMember("local_address")) //single libev config
{
server = GetMember(json, "server");
port = GetMember(json, "server_port");
json["local_port"].SetInt(local_port);
json["local_address"].SetString("127.0.0.1");
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = SS_DEFAULT_GROUP;
node.remarks = server + ":" + port;
node.server = server;
node.port = stoi(port);
node.proxyStr = SerializeObject(json);
nodes.push_back(node);
return;
}
for(unsigned int i = 0; i < json["configs"].Size(); i++)
{
json["configs"][i]["remarks"] >> ps;
if(custom_port != "")
port = custom_port;
else
json["configs"][i]["server_port"] >> port;
if(ps == "")
{
ps = server + ":" + port;
}
json["configs"][i]["password"] >> password;
json["configs"][i]["method"] >> method;
json["configs"][i]["server"] >> server;
json["configs"][i]["plugin"] >> plugin;
json["configs"][i]["plugin_opts"] >> pluginopts;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = group;
node.remarks = ps;
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.id = index;
node.server = server;
node.port = stoi(port);
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, ps, local_port, libev);
nodes.push_back(node);
index++;
}
return;
}
void explodeSSR(std::string ssr, bool ss_libev, bool ssr_libev, std::string custom_port, int local_port, nodeInfo &node)
{
std::string strobfs;
std::vector<std::string> strcfg;
std::string remarks, group, server, port, method, password, protocol, protoparam, obfs, obfsparam, remarks_base64;
ssr = replace_all_distinct(ssr.substr(6), "\r", "");
ssr = urlsafe_base64_decode(ssr);
if(strFind(ssr, "/?"))
{
strobfs = ssr.substr(ssr.find("/?") + 2);
ssr = ssr.substr(0, ssr.find("/?"));
group = urlsafe_base64_decode(getUrlArg(strobfs, "group"));
remarks = urlsafe_base64_decode(getUrlArg(strobfs, "remarks"));
remarks_base64 = urlsafe_base64_reverse(getUrlArg(strobfs, "remarks"));
obfsparam = regReplace(urlsafe_base64_decode(getUrlArg(strobfs, "obfsparam")), "\\s", "");
protoparam = regReplace(urlsafe_base64_decode(getUrlArg(strobfs, "protoparam")), "\\s", "");
}
ssr = regReplace(ssr, "(.*):(.*?):(.*?):(.*?):(.*?):(.*)", "$1,$2,$3,$4,$5,$6");
strcfg = split(ssr, ",");
if(strcfg.size() != 6)
return;
server = strcfg[0];
port = custom_port == "" ? strcfg[1] : custom_port;
protocol = strcfg[2];
method = strcfg[3];
obfs = strcfg[4];
password = urlsafe_base64_decode(strcfg[5]);
if(group == "")
group = SSR_DEFAULT_GROUP;
if(remarks == "")
{
remarks = server + ":" + port;
remarks_base64 = base64_encode(remarks);
}
node.group = group;
node.remarks = remarks;
node.server = server;
node.port = stoi(port);
if(find(ss_ciphers.begin(), ss_ciphers.end(), method) != ss_ciphers.end() && (obfs == "" || obfs == "plain") && (protocol == "" || protocol == "origin"))
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(server, port, password, method, "", "", remarks, local_port, ss_libev);
}
else
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
node.proxyStr = ssrConstruct(group, remarks, remarks_base64, server, port, protocol, method, obfs, password, obfsparam, protoparam, local_port, ssr_libev);
}
}
void explodeSSRConf(std::string content, std::string custom_port, int local_port, bool ss_libev, bool ssr_libev, std::vector<nodeInfo> &nodes)
{
nodeInfo node;
Document json;
std::string remarks, remarks_base64, group, server, port, method, password, protocol, protoparam, obfs, obfsparam;
int index = nodes.size();
json.Parse(content.data());
if(json.HasMember("local_port") && json.HasMember("local_address")) //single libev config
{
method = GetMember(json, "method");
obfs = GetMember(json, "obfs");
protocol = GetMember(json, "protocol");
if(find(ss_ciphers.begin(), ss_ciphers.end(), method) != ss_ciphers.end() && (obfs == "" || obfs == "plain") && (protocol == "" || protocol == "origin"))
{
explodeSSConf(content, custom_port, local_port, ss_libev, nodes);
return;
}
server = GetMember(json, "server");
port = GetMember(json, "server_port");
json["local_port"].SetInt(local_port);
json["local_address"].SetString("127.0.0.1");
node.linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
node.group = SSR_DEFAULT_GROUP;
node.remarks = server + ":" + port;
node.server = server;
node.port = stoi(port);
node.proxyStr = SerializeObject(json);
nodes.push_back(node);
return;
}
for(unsigned int i = 0; i < json["configs"].Size(); i++)
{
json["configs"][i]["group"] >> group;
if(group == "")
group = SSR_DEFAULT_GROUP;
json["configs"][i]["remarks"] >> remarks;
json["configs"][i]["server"] >> server;
if(custom_port != "")
port = custom_port;
else
json["configs"][i]["server_port"] >> port;
if(remarks == "")
remarks = server + ":" + port;
json["configs"][i]["remarks_base64"] >> remarks_base64;
json["configs"][i]["password"] >> password;
json["configs"][i]["method"] >> method;
json["configs"][i]["protocol"] >> protocol;
json["configs"][i]["protocolparam"] >> protoparam;
json["configs"][i]["obfs"] >> obfs;
json["configs"][i]["obfsparam"] >> obfsparam;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
node.group = group;
node.remarks = remarks;
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.id = index;
node.server = server;
node.port = stoi(port);
node.proxyStr = ssrConstruct(group, remarks, remarks_base64, server, port, protocol, method, obfs, password, obfsparam, protoparam, local_port, ssr_libev);
nodes.push_back(node);
index++;
}
return;
}
void explodeSocks(std::string link, std::string custom_port, nodeInfo &node)
{
std::string remarks, server, port, username, password;
if(strFind(link, "socks://")) //v2rayn socks link
{
std::vector<std::string> arguments;
if(strFind(link, "#"))
{
remarks = UrlDecode(link.substr(link.find("#") + 1));
link = link.substr(0, link.find("#"));
}
link = urlsafe_base64_decode(link.substr(8));
arguments = split(link, ":");
server = arguments[0];
port = arguments[1];
}
else if(strFind(link, "https://t.me/socks") || strFind(link, "tg://socks")) //telegram style socks link
{
server = getUrlArg(link, "server");
port = getUrlArg(link, "port");
username = getUrlArg(link, "user");
password = getUrlArg(link, "pass");
}
if(remarks == "")
{
remarks = server + ":" + port;
}
if(custom_port != "")
{
port = custom_port;
}
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.group = SOCKS_DEFAULT_GROUP;
node.remarks = remarks;
node.server = server;
node.port = stoi(port);
node.proxyStr = socksConstruct(remarks, server, port, username, password);
}
void explodeQuan(std::string quan, std::string custom_port, int local_port, nodeInfo &node)
{
std::string strTemp, itemName, itemVal;
std::string group = V2RAY_DEFAULT_GROUP, ps, add, port, cipher = "auto", type = "none", id, aid = "0", net = "tcp", path, host, tls;
std::vector<std::string> configs, vArray;
strTemp = regReplace(quan, "(.*?) = (.*)", "$1,$2");
configs = split(strTemp, ",");
if(configs[1] == "vmess")
{
ps = trim(configs[0]);
add = trim(configs[2]);
port = trim(configs[3]);
cipher = trim(configs[4]);
id = trim(replace_all_distinct(configs[5], "\"", ""));
//read link
for(unsigned int i = 6; i < configs.size(); i++)
{
vArray = split(configs[i], "=");
if(vArray.size() < 2)
continue;
itemName = trim(vArray[0]);
itemVal = trim(vArray[1]);
if(itemName == "group")
group = itemVal;
else if(itemName == "over-tls")
tls = itemVal == "true" ? "tls" : "";
else if(itemName == "tls-host")
host = itemVal;
else if(itemName == "obfs-path")
path = replace_all_distinct(itemVal, "\"", "");
else if(itemName == "obfs-header")
{
std::vector<std::string> headers = split(replace_all_distinct(replace_all_distinct(itemVal, "\"", ""), "[Rr][Nn]", "|"), "|");
for(unsigned int j = 0; j < headers.size(); j++)
{
if(strFind(headers[j], "Host: "))
host = headers[j].substr(6);
}
}
else if(itemName == "obfs" && itemVal == "ws")
net = "ws";
}
if(path == "")
path = "/";
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = group;
node.remarks = ps;
node.server = add;
node.port = stoi(port);
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, tls, local_port);
}
}
void explodeNetch(std::string netch, bool ss_libev, bool ssr_libev, std::string custom_port, int local_port, nodeInfo &node)
{
Document json;
std::string type, remark, address, port, username, password, method, plugin, pluginopts, protocol, protoparam, obfs, obfsparam, id, aid, transprot, faketype, host, path, tls;
netch = urlsafe_base64_decode(netch.substr(8));
json.Parse(netch.data());
json["Type"] >> type;
json["Remark"] >> remark;
json["Hostname"] >> address;
json["Port"] >> port;
method = GetMember(json, "EncryptMethod");
password = GetMember(json, "Password");
if(remark == "")
remark = address + ":" + port;
if(type == "SS")
{
plugin = GetMember(json, "Plugin");
pluginopts = GetMember(json, "PluginOption");
node.group = SS_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(address, port, password, method, plugin, pluginopts, remark, local_port, ss_libev);
}
else if(type == "SSR")
{
protocol = GetMember(json, "Protocol");
obfs = GetMember(json, "OBFS");
if(find(ss_ciphers.begin(), ss_ciphers.end(), method) != ss_ciphers.end() && (obfs == "" || obfs == "plain") && (protocol == "" || protocol == "origin"))
{
plugin = GetMember(json, "Plugin");
pluginopts = GetMember(json, "PluginOption");
node.group = SS_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(address, port, password, method, plugin, pluginopts, remark, local_port, ss_libev);
}
else
{
protoparam = GetMember(json, "ProtocolParam");
obfsparam = GetMember(json, "OBFSParam");
node.group = SSR_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
node.proxyStr = ssrConstruct(SSR_DEFAULT_GROUP, remark, base64_encode(remark), address, port, protocol, method, obfs, password, obfsparam, protoparam, local_port, ssr_libev);
}
}
else if(type == "VMess")
{
id = GetMember(json, "UserID");
aid = GetMember(json, "AlterID");
transprot = GetMember(json, "TransferProtocol");
faketype = GetMember(json, "FakeType");
host = GetMember(json, "Host");
path = GetMember(json, "Path");
tls = GetMember(json, "TLSSecure") == "true" ? "tls" : "";
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.proxyStr = vmessConstruct(address, port, faketype, id, aid, transprot, method, path, host, tls, local_port);
}
else if(type == "Socks5")
{
username = GetMember(json, "Username");
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.group = SOCKS_DEFAULT_GROUP;
node.proxyStr = socksConstruct(remark, address, port, username, password);
}
else
return;
node.remarks = remark;
node.server = address;
node.port = (unsigned short)stoi(port);
}
void explodeClash(Node yamlnode, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes, bool ss_libev, bool ssr_libev)
{
nodeInfo node;
unsigned int index = nodes.size();
std::string proxytype, strTemp, ps, server, port, cipher, group, password; //common
std::string type = "none", id, aid = "0", net = "tcp", path, host, tls; //vmess
std::string plugin, pluginopts, pluginopts_mode, pluginopts_host; //ss
std::string protocol, protoparam, obfs, obfsparam; //ssr
std::string user; //socks
for(unsigned int i = 0; i < yamlnode["Proxy"].size(); i++)
{
yamlnode["Proxy"][i]["type"] >> proxytype;
yamlnode["Proxy"][i]["name"] >> ps;
yamlnode["Proxy"][i]["server"] >> server;
port = custom_port == "" ? yamlnode["Proxy"][i]["port"].as<std::string>() : custom_port;
if(proxytype == "vmess")
{
group = V2RAY_DEFAULT_GROUP;
yamlnode["Proxy"][i]["uuid"] >> id;
yamlnode["Proxy"][i]["alterId"] >> aid;
yamlnode["Proxy"][i]["cipher"] >> cipher;
net = yamlnode["Proxy"][i]["network"].IsDefined() ? yamlnode["Proxy"][i]["network"].as<std::string>() : "tcp";
path = yamlnode["Proxy"][i]["ws-path"].IsDefined() ? yamlnode["Proxy"][i]["ws-path"].as<std::string>() : "/";
if(yamlnode["Proxy"][i]["tls"].IsDefined())
tls = yamlnode["Proxy"][i]["tls"].as<std::string>() == "true" ? "tls" : "";
else
tls = "";
if(yamlnode["Proxy"][i]["ws-headers"].IsDefined())
yamlnode["Proxy"][i]["ws-headers"]["Host"] >> host;
else
host = "";
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.proxyStr = vmessConstruct(server, port, type, id, aid, net, cipher, path, host, tls, local_port);
}
else if(proxytype == "ss")
{
group = SS_DEFAULT_GROUP;
yamlnode["Proxy"][i]["cipher"] >> cipher;
yamlnode["Proxy"][i]["password"] >> password;
if(yamlnode["Proxy"][i]["plugin"].IsDefined())
{
yamlnode["Proxy"][i]["plugin"] >> plugin;
if(yamlnode["Proxy"][i]["plugin-opts"].IsDefined())
{
yamlnode["Proxy"][i]["plugin-opts"]["mode"] >> pluginopts_mode;
if(yamlnode["Proxy"][i]["plugin-opts"]["host"].IsDefined())
yamlnode["Proxy"][i]["plugin-opts"]["host"] >> pluginopts_host;
}
}
else if(yamlnode["Proxy"][i]["obfs"].IsDefined())
{
plugin = "simple-obfs";
yamlnode["Proxy"][i]["obfs"] >> pluginopts_mode;
if(yamlnode["Proxy"][i]["obfs-host"].IsDefined())
yamlnode["Proxy"][i]["obfs-host"] >> pluginopts_host;
}
else
plugin = "";
if(plugin != "")
{
pluginopts = "obfs=" + pluginopts_mode;
pluginopts += pluginopts_host == "" ? "" : ";obfs-host=" + pluginopts_host;
}
//support for go-shadowsocks2
if(cipher == "AEAD_CHACHA20_POLY1305")
cipher = "chacha20-ietf-poly1305";
else if(strFind(cipher, "AEAD"))
{
cipher = replace_all_distinct(replace_all_distinct(cipher, "AEAD_", ""), "_", "-");
std::transform(cipher.begin(), cipher.end(), cipher.begin(), ::tolower);
}
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(server, port, password, cipher, plugin, pluginopts, ps, local_port, ss_libev);
}
else if(proxytype == "socks")
{
group = SOCKS_DEFAULT_GROUP;
if(yamlnode["Proxy"][i]["username"].IsDefined() && yamlnode["Proxy"][i]["password"].IsDefined())
{
yamlnode["Proxy"][i]["username"] >> user;
yamlnode["Proxy"][i]["password"] >> password;
}
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.proxyStr = socksConstruct(ps, server, port, user, password);
}
else if(proxytype == "ssr")
{
group = SSR_DEFAULT_GROUP;
yamlnode["Proxy"][i]["cipher"] >> cipher;
yamlnode["Proxy"][i]["password"] >> password;
yamlnode["Proxy"][i]["protocol"] >> protocol;
yamlnode["Proxy"][i]["protocolparam"] >> protoparam;
yamlnode["Proxy"][i]["obfs"] >> obfs;
yamlnode["Proxy"][i]["obfsparam"] >> obfsparam;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
node.proxyStr = ssrConstruct(group, ps, base64_encode(ps), server, port, protocol, cipher, obfs, password, obfsparam, protoparam, local_port, ssr_libev);
}
else
continue;
node.group = group;
node.remarks = ps;
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.server = server;
node.port = stoi(port);
node.id = index;
nodes.push_back(node);
index++;
}
return;
}
void explodeKitsunebi(std::string kit, std::string custom_port, int local_port, nodeInfo &node)
{
std::string ps, add, port, type, id, aid, net = "tcp", path, host, tls, cipher, remarks;
std::string addition;
string_array userinfo;
kit = replace_all_distinct(kit, "vmess://", "");
addition = kit.substr(kit.find("?") + 1);
kit = kit.substr(0, kit.find("?"));
userinfo = split(regReplace(urlsafe_base64_decode(kit), "(.*?):(.*?)@(.*):(.*)", "$1,$2,$3,$4"), ",");
cipher = userinfo[0];
id = userinfo[1];
add = userinfo[2];
port = userinfo[3];
remarks = UrlDecode(getUrlArg(addition, "remark"));
net = getUrlArg(addition, "network");
aid = getUrlArg(addition, "aid");
tls = getUrlArg(addition, "tls") == "1" ? "tls" : "";
host = getUrlArg(addition, "wsHost");
path = getUrlArg(addition, "wspath");
if(remarks == "")
remarks = add + ":" + port;
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.remarks = remarks;
node.server = add;
node.port = stoi(port);
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, tls, local_port);
}
void explodeKitsunebiNew(std::string kit, std::string custom_port, int local_port, nodeInfo &node)
{
std::string ps, add, port, type, id, aid = "0", net = "tcp", path, host, tls, cipher = "auto", remarks;
std::string addition;
string_array userinfo;
kit = replace_all_distinct(kit, "vmess1://", "");
if(strFind(kit, "#"))
{
remarks = kit.substr(kit.find("#") + 1);
kit = kit.substr(0, kit.find("#"));
}
addition = kit.substr(kit.find("?") + 1);
kit = kit.substr(0, kit.find("?"));
userinfo = split(regReplace(kit, "(.*?)@(.*):(.*)", "$1,$2,$3"), ",");
id = userinfo[0];
add = userinfo[1];
if(strFind(userinfo[2], "/"))
{
port = userinfo[2].substr(0, userinfo[2].find("/"));
path = userinfo[2].substr(userinfo[2].find("/"));
}
else
{
port = userinfo[2];
}
net = getUrlArg(addition, "network");
tls = getUrlArg(addition, "tls") == "true" ? "tls" : "";
host = getUrlArg(addition, "ws.host");
if(remarks == "")
remarks = add + ":" + port;
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.remarks = remarks;
node.server = add;
node.port = stoi(port);
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, tls, local_port);
}
bool explodeSurge(std::string surge, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes, bool libev)
{
std::string line, remarks, server, port, method, username, password; //common
std::string plugin, pluginopts, pluginopts_mode, pluginopts_host = "cloudfront.net", mod_url, mod_md5; //ss
std::string id, net, tls, host, path; //v2
std::string protocol, protoparam; //ssr
std::string itemName, itemVal;
std::stringstream data;
std::vector<std::string> configs, vArray, headers, header;
std::multimap<std::string, std::string> proxies;
nodeInfo node;
unsigned int i, index = nodes.size();
INIReader ini;
/*
if(!strFind(surge, "[Proxy]"))
return false;
*/
ini.store_isolated_line = true;
ini.SetIsolatedItemsSection("Proxy");
ini.IncludeSection("Proxy");
ini.Parse(surge);
if(!ini.SectionExist("Proxy"))
return false;
ini.EnterSection("Proxy");
ini.GetItems(proxies);
for(auto &x : proxies)
{
remarks = x.first;
configs = split(x.second, ",");
if(configs.size() < 2 || configs[0] == "direct")
continue;
else if(configs[0] == "custom") //surge 2 style custom proxy
{
mod_url = trim(configs[5]);
if(parsedMD5.count(mod_url) > 0)
{
mod_md5 = parsedMD5[mod_url]; //read calculated MD5 from map
}
else
{
mod_md5 = getMD5(webGet(mod_url)); //retrieve module and calculate MD5
parsedMD5.insert(std::pair<std::string, std::string>(mod_url, mod_md5)); //save unrecognized module MD5 to map
}
if(mod_md5 == modSSMD5) //is SSEncrypt module
{
server = trim(configs[1]);
port = custom_port == "" ? trim(configs[2]) : custom_port;
method = trim(configs[3]);
password = trim(configs[4]);
plugin = "";
for(i = 6; i < configs.size(); i++)
{
vArray = split(trim(configs[i]), "=");
if(vArray.size() < 2)
continue;
itemName = trim(vArray[0]);
itemVal = trim(vArray[1]);
if(itemName == "obfs")
{
plugin = "simple-obfs";
pluginopts_mode = itemVal;
}
else if(itemName == "obfs-host")
pluginopts_host = itemVal;
}
if(plugin != "")
{
pluginopts = "obfs=" + pluginopts_mode;
pluginopts += pluginopts_host == "" ? "" : ";obfs-host=" + pluginopts_host;
}
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = SS_DEFAULT_GROUP;
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, local_port, libev);
}
else
continue;
}
else if(configs[0] == "ss") //surge 3 style ss proxy
{
server = trim(configs[1]);
port = custom_port == "" ? trim(configs[2]) : custom_port;
plugin = "";
for(i = 3; i < configs.size(); i++)
{
vArray = split(trim(configs[i]), "=");
if(vArray.size() < 2)
continue;
itemName = trim(vArray[0]);
itemVal = trim(vArray[1]);
if(itemName == "encrypt-method")
method = itemVal;
else if(itemName == "password")
password = itemVal;
else if(itemName == "obfs")
{
plugin = "simple-obfs";
pluginopts_mode = itemVal;
}
else if(itemName == "obfs-host")
pluginopts_host = itemVal;
}
if(plugin != "")
{
pluginopts = "obfs=" + pluginopts_mode;
pluginopts += pluginopts_host == "" ? "" : ";obfs-host=" + pluginopts_host;
}
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = SS_DEFAULT_GROUP;
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, local_port, libev);
}
else if(configs[0] == "socks5") //surge 3 style socks5 proxy
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.group = SOCKS_DEFAULT_GROUP;
if(configs.size() >= 5)
{
username = trim(configs[2]);
password = trim(configs[3]);
}
node.proxyStr = socksConstruct(remarks, server, port, username, password);
}
else if(configs[0] == "vmess") //surge 4 style vmess proxy
{
server = trim(configs[1]);
port = custom_port == "" ? trim(configs[2]) : custom_port;
net = "tcp";
method = "auto";
for(i = 3; i < configs.size(); i++)
{
vArray = split(trim(configs[i]), "=");
if(vArray.size() != 2)
continue;
else if(vArray[0] == "username")
id = vArray[1];
else if(vArray[0] == "ws")
{
if(vArray[1] == "true")
net = "ws";
}
else if(vArray[0] == "tls")
{
if(vArray[1] == "true")
tls = "tls";
}
else if(vArray[0] == "ws-path")
path = vArray[1];
else if(vArray[0] == "ws-headers")
{
headers = split(trim(vArray[1]), "|");
for(auto &y : headers)
{
header = split(trim(y), ":");
if(header[0] == "Host")
host = header[1];
}
}
}
if(host == "" && !isIPv4(server) && !isIPv6(server))
host = server;
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.proxyStr = vmessConstruct(server, port, "", id, "0", net, method, path, host, tls, local_port);
}
else if(remarks == "shadowsocks") //quantumult style ss/ssr link
{
server = trim(configs[0].substr(0, configs[0].rfind(":")));
port = custom_port == "" ? trim(configs[0].substr(configs[0].rfind(":") + 1)) : custom_port;
plugin = protocol = remarks = "";
for(i = 1; i < configs.size(); i++)
{
vArray = split(trim(configs[i]), "=");
if(vArray.size() != 2)
continue;
else if(vArray[0] == "method")
method = vArray[1];
else if(vArray[0] == "password")
password = vArray[1];
else if(vArray[0] == "tag")
remarks = vArray[1];
else if(vArray[0] == "ssr-protocol")
protocol = vArray[1];
else if(vArray[0] == "ssr-protocol-param")
protoparam = vArray[1];
else if(vArray[0] == "obfs")
{
plugin = "simple-obfs";
pluginopts_mode = vArray[1];
}
else if(vArray[0] == "obfs-host")
pluginopts_host = vArray[1];
}
if(remarks == "")
remarks = server + ":" + port;
if(plugin != "")
{
pluginopts = "obfs=" + pluginopts_mode;
pluginopts += pluginopts_host == "" ? "" : ";obfs-host=" + pluginopts_host;
}
if(protocol.size())
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
node.group = SSR_DEFAULT_GROUP;
node.proxyStr = ssrConstruct(node.group, remarks, base64_encode(remarks), server, port, protocol, method, pluginopts_mode, password, pluginopts_host, protoparam, local_port, libev);
}
else
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = SS_DEFAULT_GROUP;
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, local_port, libev);
}
}
else
continue;
node.remarks = remarks;
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.server = server;
node.port = stoi(port);
node.id = index;
nodes.push_back(node);
index++;
}
return index;
}
void explodeSSTap(std::string sstap, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes, bool ss_libev, bool ssr_libev)
{
Document json;
nodeInfo node;
std::string configType, group, remarks, server, port;
std::string cipher;
std::string user, pass;
std::string protocol, protoparam, obfs, obfsparam;
unsigned int index = nodes.size();
json.Parse(sstap.data());
for(unsigned int i = 0; i < json["configs"].Size(); i++)
{
json["configs"][i]["group"] >> group;
json["configs"][i]["remarks"] >> remarks;
json["configs"][i]["server"] >> server;
json["configs"][i]["server_port"] >> port;
if(remarks == "")
remarks = server + ":" + port;
json["configs"][i]["password"] >> pass;
json["configs"][i]["type"] >> configType;
if(configType == "5") //socks 5
{
json["configs"][i]["username"] >> user;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.proxyStr = socksConstruct(remarks, server, port, user, pass);
}
else if(configType == "6") //ss/ssr
{
json["configs"][i]["protocol"] >> protocol;
json["configs"][i]["obfs"] >> obfs;
json["configs"][i]["method"] >> cipher;
if(find(ss_ciphers.begin(), ss_ciphers.end(), cipher) != ss_ciphers.end() && protocol == "origin" && obfs == "plain") //is ss
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(server, port, pass, cipher, "", "", remarks, local_port, ss_libev);
}
else //is ssr cipher
{
json["configs"][i]["obfsparam"] >> obfsparam;
json["configs"][i]["protocolparam"] >> protoparam;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
node.proxyStr = ssrConstruct(group, remarks, base64_encode(remarks), server, port, protocol, cipher, obfs, pass, obfsparam, protoparam, local_port, ssr_libev);
}
}
else
continue;
node.group = group;
node.remarks = remarks;
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.id = index;
node.server = server;
node.port = stoi(port);
nodes.push_back(node);
}
}
void explodeNetchConf(std::string netch, bool ss_libev, bool ssr_libev, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes)
{
Document json;
nodeInfo node;
unsigned int index = nodes.size();
json.Parse(netch.data());
if(!json.HasMember("Server"))
return;
for(unsigned int i = 0; i < json["Server"].Size(); i++)
{
explodeNetch("Netch://" + base64_encode(SerializeObject(json["Server"][i])), ss_libev, ssr_libev, custom_port, local_port, node);
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.id = index;
nodes.push_back(node);
index++;
}
}
bool chkIgnore(nodeInfo &node)
{
bool excluded = false, included = false;
//std::string remarks = UTF8ToGBK(node.remarks);
std::string remarks = node.remarks;
writeLog(LOG_TYPE_INFO, "Comparing exclude remarks...");
for(auto &x : exclude_remarks)
{
if(regFind(remarks, x))
excluded = true;
}
if(include_remarks.size() != 0)
{
writeLog(LOG_TYPE_INFO, "Comparing include remarks...");
for(auto &x : include_remarks)
{
if(regFind(remarks, x))
included = true;
}
}
else
{
included = true;
}
return excluded || !included;
}
int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes)
{
std::ifstream infile;
std::stringstream contentstrm;
infile.open(filepath);
contentstrm << infile.rdbuf();
infile.close();
return explodeConfContent(contentstrm.str(), custom_port, local_port, sslibev, ssrlibev, nodes);
}
int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes)
{
unsigned int index = 0;
int filetype = -1;
std::vector<nodeInfo>::iterator iter;
if(strFind(content, "\"version\""))
filetype = SPEEDTEST_MESSAGE_FOUNDSS;
else if(strFind(content, "\"serverSubscribes\""))
filetype = SPEEDTEST_MESSAGE_FOUNDSSR;
else if(strFind(content, "\"uiItem\"") || strFind(content, "vnext"))
filetype = SPEEDTEST_MESSAGE_FOUNDVMESS;
else if(strFind(content, "\"proxy_apps\""))
filetype = SPEEDTEST_MESSAGE_FOUNDSSCONF;
else if(strFind(content, "\"idInUse\""))
filetype = SPEEDTEST_MESSAGE_FOUNDSSTAP;
else if(strFind(content, "\"local_address\"") && strFind(content, "\"local_port\""))
filetype = SPEEDTEST_MESSAGE_FOUNDSSR; //use ssr config parser
else if(strFind(content, "\"ModeFileNameType\""))
filetype = SPEEDTEST_MESSAGE_FOUNDNETCH;
switch(filetype)
{
case SPEEDTEST_MESSAGE_FOUNDSS:
explodeSSConf(content, custom_port, local_port, sslibev, nodes);
break;
case SPEEDTEST_MESSAGE_FOUNDSSR:
explodeSSRConf(content, custom_port, local_port, sslibev, ssrlibev, nodes);
break;
case SPEEDTEST_MESSAGE_FOUNDVMESS:
explodeVmessConf(content, custom_port, local_port, sslibev, nodes);
break;
case SPEEDTEST_MESSAGE_FOUNDSSCONF:
explodeSSAndroid(content, sslibev, custom_port, local_port, nodes);
break;
case SPEEDTEST_MESSAGE_FOUNDSSTAP:
explodeSSTap(content, custom_port, local_port, nodes, sslibev, ssrlibev);
break;
case SPEEDTEST_MESSAGE_FOUNDNETCH:
explodeNetchConf(content, sslibev, ssrlibev, custom_port, local_port, nodes);
break;
default:
//try to parse as a local subscription
explodeSub(content, sslibev, ssrlibev, custom_port, local_port, nodes);
if(nodes.size() == 0)
return SPEEDTEST_ERROR_UNRECOGFILE;
else
return SPEEDTEST_ERROR_NONE;
}
iter = nodes.begin();
while(iter != nodes.end())
{
if(chkIgnore(*iter))
{
writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been ignored and will not be tested.");
nodes.erase(iter);
}
else
{
writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been added.");
iter->id = index;
index++;
iter++;
}
}
return SPEEDTEST_ERROR_NONE;
}
void explode(std::string link, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, nodeInfo &node)
{
if(strFind(link, "ssr://"))
explodeSSR(link, sslibev, ssrlibev, custom_port, local_port, node);
else if(strFind(link, "vmess://") || strFind(link, "vmess1://"))
explodeVmess(link, custom_port, local_port, node);
else if(strFind(link, "ss://"))
explodeSS(link, sslibev, custom_port, local_port, node);
else if(strFind(link, "socks://") || strFind(link, "https://t.me/socks") || strFind(link, "tg://socks"))
explodeSocks(link, custom_port, node);
else if(strFind(link, "Netch://"))
explodeNetch(link, sslibev, ssrlibev, custom_port, local_port, node);
}
void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes)
{
std::stringstream strstream;
std::string strLink;
nodeInfo node;
//try to parse as SSD configuration
if(strFind(sub, "ssd://"))
{
explodeSSD(sub, sslibev, custom_port, local_port, nodes);
return;
}
//try to parse as clash configuration
try
{
if(strFind(sub, "Proxy"))
{
Node yamlnode = Load(sub);
if(yamlnode.size() && yamlnode["Proxy"])
{
explodeClash(yamlnode, custom_port, local_port, nodes, sslibev, ssrlibev);
return;
}
}
}
catch (std::exception &e)
{
//ignore
}
//try to parse as surge configuration
if(explodeSurge(sub, custom_port, local_port, nodes, sslibev))
{
return;
}
//try to parse as normal subscription
sub = urlsafe_base64_decode(sub);
strstream << sub;
unsigned int index = nodes.size();
char delimiter = split(sub, "\n").size() < 1 ? split(sub, "\r").size() < 1 ? ' ' : '\r' : '\n';
while(getline(strstream, strLink, delimiter))
{
explode(strLink, sslibev, ssrlibev, custom_port, local_port, node);
if(strLink.size() == 0 || node.linkType == -1)
{
continue;
}
if(chkIgnore(node))
{
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been ignored and will not be tested.");
continue;
}
else
writeLog(LOG_TYPE_INFO, "Node " + node.group + " - " + node.remarks + " has been added.");
node.id = index;
nodes.push_back(node);
index++;
}
}
void remarksInit(string_array &exclude, string_array &include)
{
exclude_remarks = exclude;
include_remarks = include;
}