From c10766f1ade9e9fc59e2098ca95ba056339b89bc Mon Sep 17 00:00:00 2001 From: Tindy X <49061470+tindy2013@users.noreply.github.com> Date: Sat, 18 Dec 2021 13:13:01 +0800 Subject: [PATCH] Enhancements Add support for Trojan-WebSocket protocol. Fix skip-cert-verify option not correctly added to Surge subscriptions. Optimize codes. --- src/generator/config/subexport.cpp | 133 +++++++++++++++-------------- src/parser/subparser.cpp | 57 ++++++++----- 2 files changed, 103 insertions(+), 87 deletions(-) diff --git a/src/generator/config/subexport.cpp b/src/generator/config/subexport.cpp index bd432c0..c941d4c 100644 --- a/src/generator/config/subexport.cpp +++ b/src/generator/config/subexport.cpp @@ -308,30 +308,30 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr singleproxy["network"] = x.TransferProtocol; /* singleproxy["ws-opts"]["path"] = x.Path; - if(x.Host.size()) + if(!x.Host.empty()) singleproxy["ws-opts"]["headers"]["Host"] = x.Host; - if(x.Edge.size()) + if(!x.Edge.empty()) singleproxy["ws-opts"]["headers"]["Edge"] = x.Edge; */ singleproxy["ws-path"] = x.Path; - if(x.Host.size()) + if(!x.Host.empty()) singleproxy["ws-headers"]["Host"] = x.Host; - if(x.Edge.size()) + if(!x.Edge.empty()) singleproxy["ws-headers"]["Edge"] = x.Edge; break; case "http"_hash: singleproxy["network"] = x.TransferProtocol; singleproxy["http-opts"]["method"] = "GET"; singleproxy["http-opts"]["path"].push_back(x.Path); - if(x.Host.size()) + if(!x.Host.empty()) singleproxy["http-opts"]["headers"]["Host"].push_back(x.Host); - if(x.Edge.size()) + if(!x.Edge.empty()) singleproxy["http-opts"]["headers"]["Edge"].push_back(x.Edge); break; case "h2"_hash: singleproxy["network"] = x.TransferProtocol; singleproxy["h2-opts"]["path"] = x.Path; - if(x.Host.size()) + if(!x.Host.empty()) singleproxy["h2-opts"]["host"].push_back(x.Host); break; case "grpc"_hash: @@ -403,7 +403,7 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr case ProxyType::Trojan: singleproxy["type"] = "trojan"; singleproxy["password"] = x.Password; - if(x.Host.size()) + if(!x.Host.empty()) singleproxy["sni"] = x.Host; if(std::all_of(x.Password.begin(), x.Password.end(), ::isdigit) && !x.Password.empty()) singleproxy["password"].SetTag("str"); @@ -418,15 +418,20 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr if(!x.Path.empty()) singleproxy["grpc-opts"]["grpc-service-name"] = x.Path; break; + case "ws"_hash: + singleproxy["ws-opts"]["path"] = x.Path; + if(!x.Host.empty()) + singleproxy["ws-opts"]["headers"]["Host"] = x.Host; + break; } break; case ProxyType::Snell: singleproxy["type"] = "snell"; singleproxy["psk"] = x.Password; - if(x.OBFS.size()) + if(!x.OBFS.empty()) { singleproxy["obfs-opts"]["mode"] = x.OBFS; - if(x.Host.size()) + if(!x.Host.empty()) singleproxy["obfs-opts"]["host"] = x.Host; } if(std::all_of(x.Password.begin(), x.Password.end(), ::isdigit) && !x.Password.empty()) @@ -500,7 +505,7 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr for(const auto& y : x.Proxies) groupGenerate(y, nodelist, filtered_nodelist, true, ext); - if(x.UsingProvider.size()) + if(!x.UsingProvider.empty()) singlegroup["use"] = x.UsingProvider; else { @@ -559,7 +564,7 @@ std::string proxyToClash(std::vector &nodes, const std::string &base_conf if(!ext.enable_rule_generator) return YAML::Dump(yamlnode); - if(ext.managed_config_prefix.size() || ext.clash_script) + if(!ext.managed_config_prefix.empty() || ext.clash_script) { if(yamlnode["mode"].IsDefined()) { @@ -647,13 +652,13 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf { proxy = "custom, " + hostname + ", " + port + ", " + method + ", " + password + ", https://github.com/ConnersHua/SSEncrypt/raw/master/SSEncrypt.module"; } - if(plugin.size()) + if(!plugin.empty()) { switch(hash_(plugin)) { case "simple-obfs"_hash: case "obfs-local"_hash: - if(pluginopts.size()) + if(!pluginopts.empty()) proxy += "," + replaceAllDistinct(pluginopts, ";", ","); break; default: @@ -673,26 +678,26 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf break; case "ws"_hash: proxy += ", ws=true, ws-path=" + path + ", sni=" + host + ", ws-headers=Host:" + host; - if(edge.size()) + if(!edge.empty()) proxy += "|Edge:" + edge; break; default: continue; } if(!scv.is_undef()) - proxy += ", skip-cert-verify=" + std::string(scv.get() ? "1" : "0"); + proxy += ", skip-cert-verify=" + scv.get_str(); break; case ProxyType::ShadowsocksR: if(ext.surge_ssr_path.empty() || surge_ver < 2) continue; proxy = "external, exec=\"" + ext.surge_ssr_path + "\", args=\""; args = {"-l", std::to_string(local_port), "-s", hostname, "-p", port, "-m", method, "-k", password, "-o", obfs, "-O", protocol}; - if(obfsparam.size()) + if(!obfsparam.empty()) { args.emplace_back("-g"); args.emplace_back(std::move(obfsparam)); } - if(protoparam.size()) + if(!protoparam.empty()) { args.emplace_back("-G"); args.emplace_back(std::move(protoparam)); @@ -707,35 +712,35 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf break; case ProxyType::SOCKS5: proxy = "socks5, " + hostname + ", " + port; - if(username.size()) + if(!username.empty()) proxy += ", username=" + username; - if(password.size()) + if(!password.empty()) proxy += ", password=" + password; if(!scv.is_undef()) - proxy += ", skip-cert-verify=" + std::string(scv.get() ? "1" : "0"); + proxy += ", skip-cert-verify=" + scv.get_str(); break; case ProxyType::HTTP: proxy = "http, " + hostname + ", " + port; - if(username.size()) + if(!username.empty()) proxy += ", username=" + username; - if(password.size()) + if(!password.empty()) proxy += ", password=" + password; proxy += std::string(", tls=") + (x.TLSSecure ? "true" : "false"); if(!scv.is_undef()) - proxy += ", skip-cert-verify=" + std::string(scv.get() ? "1" : "0"); + proxy += ", skip-cert-verify=" + scv.get_str(); break; case ProxyType::Trojan: if(surge_ver < 4) continue; proxy = "trojan, " + hostname + ", " + port + ", password=" + password; - if(host.size()) + if(!host.empty()) proxy += ", sni=" + host; if(!scv.is_undef()) - proxy += ", skip-cert-verify=" + std::string(scv.get() ? "1" : "0"); + proxy += ", skip-cert-verify=" + scv.get_str(); break; case ProxyType::Snell: proxy = "snell, " + hostname + ", " + port + ", psk=" + password; - if(obfs.size()) + if(!obfs.empty()) proxy += ", obfs=" + obfs + ", obfs-host=" + host; break; default: @@ -789,7 +794,7 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf for(const auto &y : x.Proxies) groupGenerate(y, nodelist, filtered_nodelist, true, ext); - if(!filtered_nodelist.size()) + if(filtered_nodelist.empty()) filtered_nodelist.emplace_back("DIRECT"); if(filtered_nodelist.size() == 1) @@ -852,7 +857,7 @@ std::string proxyToSingle(std::vector &nodes, int types, extra_settings & if(ss) { proxyStr = "ss://" + urlSafeBase64Encode(method + ":" + password) + "@" + hostname + ":" + port; - if(plugin.size() && pluginopts.size()) + if(!plugin.empty() && !pluginopts.empty()) { proxyStr += "/?plugin=" + urlEncode(plugin + ";" + pluginopts); } @@ -860,7 +865,7 @@ std::string proxyToSingle(std::vector &nodes, int types, extra_settings & } else if(ssr) { - if(std::count(ssr_ciphers.begin(), ssr_ciphers.end(), method) > 0 && !GetMember(json, "Plugin").size() && !GetMember(json, "Plugin").size()) + if(std::count(ssr_ciphers.begin(), ssr_ciphers.end(), method) > 0 && GetMember(json, "Plugin").empty() && GetMember(json, "Plugin").empty()) proxyStr = "ssr://" + urlSafeBase64Encode(hostname + ":" + port + ":origin:" + method + ":plain:" + urlSafeBase64Encode(password) \ + "/?group=" + urlSafeBase64Encode(x.Group) + "&remarks=" + urlSafeBase64Encode(remark)); } @@ -997,7 +1002,7 @@ std::string proxyToQuan(std::vector &nodes, const std::string &base_conf, string_array allnodes; std::string allLinks; ini.GetAll("SERVER", "{NONAME}", allnodes); - if(allnodes.size()) + if(!allnodes.empty()) allLinks = join(allnodes, "\n"); return base64Encode(allLinks); } @@ -1047,7 +1052,7 @@ void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, const std::string &base_conf string_array allnodes; std::string allLinks; ini.GetAll("server_local", "{NONAME}", allnodes); - if(allnodes.size()) + if(!allnodes.empty()) allLinks = join(allnodes, "\n"); return allLinks; } @@ -1293,13 +1298,13 @@ void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, std::string &group, std::strin rapidjson::Writer writer(sb); size_t index = 0; - if(!group.size()) + if(group.empty()) group = "SSD"; writer.StartObject(); @@ -1519,7 +1524,7 @@ std::string proxyToSSD(std::vector &nodes, std::string &group, std::strin writer.String("aes-128-gcm"); writer.Key("password"); writer.String("password"); - if(userinfo.size()) + if(!userinfo.empty()) { std::string data = replaceAllDistinct(userinfo, "; ", "&"); std::string upload = getUrlArg(data, "upload"), download = getUrlArg(data, "download"), total = getUrlArg(data, "total"), expiry = getUrlArg(data, "expire"); @@ -1528,7 +1533,7 @@ std::string proxyToSSD(std::vector &nodes, std::string &group, std::strin writer.Double(used); writer.Key("traffic_total"); writer.Double(tot); - if(expiry.size()) + if(!expiry.empty()) { const time_t rawtime = to_int(expiry); char buffer[30]; @@ -1649,13 +1654,13 @@ void proxyToMellow(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, const std::string &base_conf, proxy = "Shadowsocks," + hostname + "," + port + "," + method + ",\"" + password + "\""; if(plugin == "simple-obfs" || plugin == "obfs-local") { - if(pluginopts.size()) + if(!pluginopts.empty()) proxy += "," + replaceAllDistinct(replaceAllDistinct(pluginopts, ";obfs-host=", ","), "obfs=", ""); } - else if(plugin.size()) + else if(!plugin.empty()) continue; break; case ProxyType::VMess: @@ -1846,7 +1851,7 @@ std::string proxyToLoon(std::vector &nodes, const std::string &base_conf, break; case ProxyType::Trojan: proxy = "trojan," + hostname + "," + port + "," + password; - if(host.size()) + if(!host.empty()) proxy += ",tls-name:" + host; if(!scv.is_undef()) proxy += ",skip-cert-verify:" + std::string(scv.get() ? "1" : "0"); @@ -1902,7 +1907,7 @@ std::string proxyToLoon(std::vector &nodes, const std::string &base_conf, for(const auto &y : x.Proxies) groupGenerate(y, nodelist, filtered_nodelist, true, ext); - if(!filtered_nodelist.size()) + if(filtered_nodelist.empty()) filtered_nodelist.emplace_back("DIRECT"); proxy = x.TypeStr() + ","; diff --git a/src/parser/subparser.cpp b/src/parser/subparser.cpp index d7410af..471a9d8 100644 --- a/src/parser/subparser.cpp +++ b/src/parser/subparser.cpp @@ -161,7 +161,7 @@ void explodeVmess(std::string vmess, Proxy &node) switch(to_int(version)) { case 1: - if(host.size()) + if(!host.empty()) { vArray = split(host, ";"); if(vArray.size() == 2) @@ -286,7 +286,7 @@ void explodeVmessConf(std::string content, std::vector &nodes) continue; json["vmess"][i]["subid"] >> subid; - if(subid.size()) + if(!subid.empty()) { iter = subdata.find(subid); if(iter != subdata.end()) @@ -346,8 +346,9 @@ void explodeSS(std::string ss, Proxy &node) plugins = urlDecode(getUrlArg(addition, "plugin")); plugin = plugins.substr(0, plugins.find(";")); pluginopts = plugins.substr(plugins.find(";") + 1); - if(getUrlArg(addition, "group").size()) - group = urlSafeBase64Decode(getUrlArg(addition, "group")); + group = getUrlArg(addition, "group"); + if(!group.empty()) + group = urlSafeBase64Decode(group); ss.erase(ss.find("?")); } if(strFind(ss, "@")) @@ -723,7 +724,7 @@ void explodeHTTPSub(std::string link, Proxy &node) void explodeTrojan(std::string trojan, Proxy &node) { - std::string server, port, psk, addition, group, remark, host; + std::string server, port, psk, addition, group, remark, host, path, network; tribool tfo, scv; trojan.erase(0, 9); string_size pos = trojan.rfind("#"); @@ -750,6 +751,12 @@ void explodeTrojan(std::string trojan, Proxy &node) scv = getUrlArg(addition, "allowInsecure"); group = urlDecode(getUrlArg(addition, "group")); + if(getUrlArg(addition, "ws") == "1") + { + path = getUrlArg(addition, "wspath"); + network = "ws"; + } + if(remark.empty()) remark = server + ":" + port; if(host.empty() && !isIPv4(server) && !isIPv6(server)) @@ -757,7 +764,7 @@ void explodeTrojan(std::string trojan, Proxy &node) if(group.empty()) group = TROJAN_DEFAULT_GROUP; - trojanConstruct(node, group, remark, server, port, psk, "", host, "", true, tribool(), tfo, scv); + trojanConstruct(node, group, remark, server, port, psk, network, host, path, true, tribool(), tfo, scv); } void explodeQuan(const std::string &quan, Proxy &node) @@ -1044,11 +1051,11 @@ void explodeClash(Node yamlnode, std::vector &nodes) break; case "v2ray-plugin"_hash: pluginopts = "mode=" + pluginopts_mode + ";" + tls + pluginopts_mux; - if(pluginopts_host.size()) + if(!pluginopts_host.empty()) pluginopts += "host=" + pluginopts_host + ";"; - if(path.size()) + if(!path.empty()) pluginopts += "path=" + path + ";"; - if(pluginopts_mux.size()) + if(!pluginopts_mux.empty()) pluginopts += "mux=" + pluginopts_mux + ";"; break; } @@ -1104,15 +1111,19 @@ void explodeClash(Node yamlnode, std::vector &nodes) group = TROJAN_DEFAULT_GROUP; singleproxy["password"] >>= password; singleproxy["sni"] >>= host; - if(safe_as(singleproxy["network"]) == "grpc") + singleproxy["network"] >>= net; + switch(hash_(net)) { - net = "grpc"; + case "grpc"_hash: singleproxy["grpc-opts"]["grpc-service-name"] >>= path; - } - else - { + break; + case "ws"_hash: + singleproxy["ws-opts"]["path"] >>= path; + break; + default: net = "tcp"; path.clear(); + break; } trojanConstruct(node, group, ps, server, port, password, net, host, path, true, udp, tfo, scv); @@ -1198,7 +1209,7 @@ void explodeShadowrocket(std::string rocket, Proxy &node) return; remarks = urlDecode(getUrlArg(addition, "remarks")); obfs = getUrlArg(addition, "obfs"); - if(obfs.size()) + if(!obfs.empty()) { if(obfs == "websocket") { @@ -1368,7 +1379,7 @@ bool explodeSurge(std::string surge, std::vector &nodes) continue; } } - if(plugin.size()) + if(!plugin.empty()) { pluginopts = "obfs=" + pluginopts_mode; pluginopts += pluginopts_host.empty() ? "" : ";obfs-host=" + pluginopts_host; @@ -1417,7 +1428,7 @@ bool explodeSurge(std::string surge, std::vector &nodes) continue; } } - if(plugin.size()) + if(!plugin.empty()) { pluginopts = "obfs=" + pluginopts_mode; pluginopts += pluginopts_host.empty() ? "" : ";obfs-host=" + pluginopts_host; @@ -1714,22 +1725,22 @@ bool explodeSurge(std::string surge, std::vector &nodes) { case "simple-obfs"_hash: pluginopts = "obfs=" + pluginopts_mode; - if(pluginopts_host.size()) + if(!pluginopts_host.empty()) pluginopts += ";obfs-host=" + pluginopts_host; break; case "v2ray-plugin"_hash: if(pluginopts_host.empty() && !isIPv4(server) && !isIPv6(server)) pluginopts_host = server; pluginopts = "mode=" + pluginopts_mode; - if(pluginopts_host.size()) + if(!pluginopts_host.empty()) pluginopts += ";host=" + pluginopts_host; - if(path.size()) + if(!path.empty()) pluginopts += ";path=" + path; pluginopts += ";" + tls; break; } - if(protocol.size()) + if(!protocol.empty()) { ssrConstruct(node, SSR_DEFAULT_GROUP, remarks, server, port, protocol, method, pluginopts_mode, password, pluginopts_host, protoparam, udp, tfo, scv); } @@ -2053,7 +2064,7 @@ int explodeConfContent(const std::string &content, std::vector &nodes) explodeSub(content, nodes); } - return nodes.size() != 0; + return !nodes.empty(); } void explode(const std::string &link, Proxy &node) @@ -2134,7 +2145,7 @@ void explodeSub(std::string sub, std::vector &nodes) if(strLink.rfind("\r") != strLink.npos) strLink.erase(strLink.size() - 1); explode(strLink, node); - if(strLink.size() == 0 || node.Type == ProxyType::Unknow) + if(strLink.empty() || node.Type == ProxyType::Unknow) { continue; }