#include #include #include #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; 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 parsedMD5; std::string modSSMD5 = "f7653207090ce3389115e9c88541afe0"; //remake from speedtestutil template void operator >> (const YAML::Node& node, T& i) { i = node.as(); }; /* 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; } std::string httpConstruct(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 vArray; if(regMatch(vmess, "vmess://(.*?)\\?(.*)")) //kitsunebi style link { explodeShadowrocket(vmess, custom_port, local_port, node); return; } else if(regMatch(vmess, "vmess1://(.*?)\\?(.*)")) // new kitsunebi style link { explodeKitsunebi(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()); if(jsondata.HasParseError()) return; 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 &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 subdata; std::map::iterator iter; json.Parse(content.data()); if(json.HasParseError()) return; 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(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; 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 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 &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.HasParseError()) return; 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; 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 &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()); if(json.HasParseError()) return; 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; 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 &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.HasParseError()) return; 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; 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 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 &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.HasParseError()) return; 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; 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 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 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 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()); if(json.HasParseError()) return; 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 &nodes, bool ss_libev, bool ssr_libev) { nodeInfo node; unsigned int index = nodes.size(); std::string proxytype, 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() : 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() : "tcp"; path = yamlnode["Proxy"][i]["ws-path"].IsDefined() ? yamlnode["Proxy"][i]["ws-path"].as() : "/"; if(yamlnode["Proxy"][i]["tls"].IsDefined()) tls = yamlnode["Proxy"][i]["tls"].as() == "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"].as() == "obfs") { plugin = "simple-obfs"; 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 if(proxytype == "http") { group = HTTP_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_FOUNDHTTP; node.proxyStr = httpConstruct(ps, server, port, user, password); } else continue; node.group = group; node.remarks = ps; node.server = server; node.port = stoi(port); node.id = index; nodes.push_back(node); index++; } return; } void explodeShadowrocket(std::string kit, std::string custom_port, int local_port, nodeInfo &node) { std::string 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(aid == "") aid = "0"; 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 explodeKitsunebi(std::string kit, std::string custom_port, int local_port, nodeInfo &node) { std::string 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 &nodes, bool libev) { std::string 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::vector configs, vArray, headers, header; std::multimap 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(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(configs[0] == "http") //http proxy { node.linkType = SPEEDTEST_MESSAGE_FOUNDHTTP; node.group = HTTP_DEFAULT_GROUP; if(configs.size() >= 5) { username = trim(configs[2]); password = trim(configs[3]); } node.proxyStr = httpConstruct(remarks, server, port, username, password); } 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; 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 &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()); if(json.HasParseError()) return; 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; 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 &nodes) { Document json; nodeInfo node; unsigned int index = nodes.size(); json.Parse(netch.data()); if(json.HasParseError()) return; 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); node.id = index; nodes.push_back(node); index++; } } bool chkIgnore(const nodeInfo &node, string_array &exclude_remarks, string_array &include_remarks) { bool excluded = false, included = false; //std::string remarks = UTF8ToGBK(node.remarks); std::string remarks = node.remarks; writeLog(LOG_TYPE_INFO, "Comparing exclude remarks..."); excluded = std::any_of(exclude_remarks.cbegin(), exclude_remarks.cend(), [&remarks](auto &x){return regFind(remarks, x);}); if(include_remarks.size() != 0) { writeLog(LOG_TYPE_INFO, "Comparing include remarks..."); included = std::any_of(include_remarks.cbegin(), include_remarks.cend(), [&remarks](auto &x){return regFind(remarks, x);}); } else { included = true; } return excluded || !included; } int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks) { 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, exclude_remarks, include_remarks); } int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks) { unsigned int index = 0; int filetype = -1; std::vector::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, exclude_remarks, include_remarks); if(nodes.size() == 0) return SPEEDTEST_ERROR_UNRECOGFILE; else return SPEEDTEST_ERROR_NONE; } iter = nodes.begin(); while(iter != nodes.end()) { if(chkIgnore(*iter, exclude_remarks, include_remarks)) { writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been ignored and will not be added."); 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 &nodes, string_array &exclude_remarks, string_array &include_remarks) { std::stringstream strstream; std::string strLink; bool processed = false; nodeInfo node; //try to parse as SSD configuration if(strFind(sub, "ssd://")) { explodeSSD(sub, sslibev, custom_port, local_port, nodes); processed = true; } //try to parse as clash configuration try { if(!processed && strFind(sub, "Proxy")) { Node yamlnode = Load(sub); if(yamlnode.size() && yamlnode["Proxy"]) { explodeClash(yamlnode, custom_port, local_port, nodes, sslibev, ssrlibev); processed = true; } } } catch (std::exception &e) { //ignore } //try to parse as surge configuration if(!processed && explodeSurge(sub, custom_port, local_port, nodes, sslibev)) { processed = true; } //try to parse as normal subscription if(!processed) { sub = urlsafe_base64_decode(sub); strstream << sub; char delimiter = count(sub.begin(), sub.end(), '\n') < 1 ? count(sub.begin(), sub.end(), '\r') < 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; } nodes.push_back(node); } } int index = 0; std::vector::iterator iter = nodes.begin(); while(iter != nodes.end()) { if(chkIgnore(*iter, exclude_remarks, include_remarks)) { writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been ignored and will not be added."); nodes.erase(iter); } else { writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been added."); iter->id = index; ++index; ++iter; } } }