diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 887f588f4..fc2559cf4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,5 +1,5 @@ name: GitHub CI -on: +on: push: branches: [ master ] tags: @@ -7,7 +7,7 @@ on: workflow_dispatch: # pull_request: -concurrency: +concurrency: group: ${{ github.ref }}-${{ github.workflow }} cancel-in-progress: true diff --git a/.gitignore b/.gitignore index 567b77607..1331b5717 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,7 @@ subconverter.exe cmake-build-debug .idea base/cache +scripts/quickjspp +scripts/yaml-cpp +.DS_Store +src/.DS_Store diff --git a/README.md b/README.md index 5bf8565af..f72353fd1 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,12 @@ Utility to convert between various proxy subscription formats. -[![Build Status](https://github.com/asdlokj1qpi23/subconverter/actions/workflows/docker.yml/badge.svg)](https://github.com/asdlokj1qpi23/subconverter/actions) -[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/asdlokj1qpi23/subconverter.svg)](https://github.com/asdlokj1qpi23/subconverter/tags) -[![GitHub release](https://img.shields.io/github/release/asdlokj1qpi23/subconverter.svg)](https://github.com/asdlokj1qpi23/subconverter/releases) -[![GitHub license](https://img.shields.io/github/license/asdlokj1qpi23/subconverter.svg)](https://github.com/tindy2013/subconverter/blob/master/LICENSE) +original git: https://github.com/asdlokj1qpi23/subconverter + +[![Build Status](https://github.com/asdlokj1qpi233/subconverter/actions/workflows/docker.yml/badge.svg)](https://github.com/asdlokj1qpi233/subconverter/actions) +[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/asdlokj1qpi233/subconverter.svg)](https://github.com/asdlokj1qpi23/subconverter/tags) +[![GitHub release](https://img.shields.io/github/release/asdlokj1qpi233/subconverter.svg)](https://github.com/asdlokj1qpi233/subconverter/releases) +[![GitHub license](https://img.shields.io/github/license/asdlokj1qpi233/subconverter.svg)](https://github.com/tindy2013/subconverter/blob/master/LICENSE) [Docker README](https://github.com/asdlokj1qpi23/subconverter/blob/master/README-docker.md) @@ -59,6 +61,7 @@ services: | Surge 2 | ✓ | ✓ | surge&ver=2 | | Surge 3 | ✓ | ✓ | surge&ver=3 | | Surge 4 | ✓ | ✓ | surge&ver=4 | +| Surge 5 | ✓ | ✓ | surge&ver=5 | | V2Ray | ✓ | ✓ | v2ray | | Telegram-liked HTTP/Socks 5 links | ✓ | × | Only as source | | Singbox | ✓ | ✓ | singbox | diff --git a/base/snippets/emoji.toml b/base/snippets/emoji.toml index a277aebed..f04cd88f3 100644 --- a/base/snippets/emoji.toml +++ b/base/snippets/emoji.toml @@ -3,15 +3,15 @@ match = "(?i:Bandwidth|expire|流量|时间|应急|过期)" emoji = "🏳️‍🌈" [[emoji]] -match = "(?i:\\bHK[G]?\\b|Hong.*?Kong|\\bHKT\\b|\\bHKBN\\b|\\bHGC\\b|\\bWTT\\b|\\bCMI\\b|[^-]港)" +match = "(?i:\\bHK[G]?\\d*\\b|Hong.*?Kong|\\bHKT\\b|\\bHKBN\\b|\\bHGC\\b|\\bWTT\\b|\\bCMI\\b|[^-]港)" emoji = "🇭🇰" [[emoji]] -match = "(?i:\\bTW[N]?\\b|Taiwan|新北|彰化|\\bCHT\\b|台湾|[^-]台|\\bHINET\\b)" +match = "(?i:\\bTW[N]?\\d*\\b|Taiwan|新北|彰化|\\bCHT\\b|台湾|[^-]台|\\bHINET\\b)" emoji = "🇨🇳" [[emoji]] -match = "(?i:\\bSG[P]?\\b|Singapore|新加坡|狮城|[^-]新)" +match = "(?i:\\bSG[P]?\\d*\\b|Singapore|新加坡|狮城|[^-]新)" emoji = "🇸🇬" [[emoji]] @@ -19,15 +19,15 @@ match = "(尼日利亚|Nigeria)" emoji = "🇳🇬" [[emoji]] -match = "(?i:\\bJP[N]?\\b|Japan|Tokyo|Osaka|Saitama|日本|东京|大阪|埼玉|[^-]日)" +match = "(?i:\\bJP[N]?\\d*\\b|Japan|Tokyo|Osaka|Saitama|日本|东京|大阪|埼玉|[^-]日)" emoji = "🇯🇵" [[emoji]] -match = "(?i:\\bK[O]?R\\b|Korea|首尔|韩|韓)" +match = "(?i:\\bK[O]?R\\d*\\b|Korea|(? /dev/null @@ -63,4 +63,4 @@ chmod +r ./* cd .. mv base subconverter -set +xe +set +xe \ No newline at end of file diff --git a/scripts/build.windows.release.sh b/scripts/build.windows.release.sh index 5de4c8dc3..a8ef8408c 100644 --- a/scripts/build.windows.release.sh +++ b/scripts/build.windows.release.sh @@ -1,9 +1,33 @@ #!/bin/bash set -xe -git clone https://github.com/curl/curl --depth=1 --branch curl-8_4_0 +# 获取系统架构 +ARCH=$(uname -m) + +if [ "$ARCH" == "x86_64" ]; then + TOOLCHAIN="mingw-w64-x86_64" +else + TOOLCHAIN="mingw-w64-i686" +fi + +pacman -S --needed --noconfirm base-devel ${TOOLCHAIN}-toolchain ${TOOLCHAIN}-cmake ${TOOLCHAIN}-nghttp2 ${TOOLCHAIN}-openssl + +git clone https://github.com/curl/curl --depth=1 --branch curl-8_8_0 cd curl -cmake -DCMAKE_BUILD_TYPE=Release -DCURL_USE_LIBSSH2=OFF -DHTTP_ONLY=ON -DCURL_USE_SCHANNEL=ON -DBUILD_SHARED_LIBS=OFF -DBUILD_CURL_EXE=OFF -DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" -G "Unix Makefiles" -DHAVE_LIBIDN2=OFF -DCURL_USE_LIBPSL=OFF . +cmake -DCMAKE_BUILD_TYPE=Release \ + -DCURL_USE_LIBSSH2=OFF \ + -DHTTP_ONLY=ON \ + -DCURL_USE_SCHANNEL=ON \ + -DBUILD_SHARED_LIBS=OFF \ + -DBUILD_CURL_EXE=OFF \ + -DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" \ + -G "Unix Makefiles" \ + -DHAVE_LIBIDN2=OFF \ + -DCURL_USE_LIBPSL=OFF \ + -DCURL_STATICLIB=ON \ + -DCURL_DISABLE_SOCKETPAIR=ON \ + -DCURL_DISABLE_NONBLOCKING=ON . + make install -j4 cd .. @@ -38,7 +62,7 @@ cmake -DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD make install -j4 cd .. -git clone https://github.com/ToruNiina/toml11 --depth=1 +git clone https://github.com/ToruNiina/toml11 --branch="v3.7.1" --depth=1 cd toml11 cmake -DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" -G "Unix Makefiles" -DCMAKE_CXX_STANDARD=11 . make install -j4 diff --git a/scripts/rules_config_bak.conf b/scripts/rules_config_bak.conf new file mode 100644 index 000000000..31b5fdda0 --- /dev/null +++ b/scripts/rules_config_bak.conf @@ -0,0 +1,23 @@ +[ACL4SSR] +name=ACL4SSR +url=https://github.com/ACL4SSR/ACL4SSR +checkout=1dc5c92b0c8ceaaecbc66530c309961f53e52c8c +match=Clash/*.list|Clash/Ruleset/** + +[ACL4SSR_config] +name=ACL4SSR +url=https://github.com/ACL4SSR/ACL4SSR +checkout=1dc5c92b0c8ceaaecbc66530c309961f53e52c8c +match=Clash/config/** +dest=base/config/ +keep_tree=false + +[DivineEngine] +url=https://github.com/DivineEngine/Profiles +checkout=f4d75f7d48a3f42129e030bef751d4d22bca02da +match=Surge/Ruleset/** + +[NobyDa] +url=https://github.com/NobyDa/Script +checkout=ae4c12f23de8078e02c373c9969b19af28257fcb +match=Surge/*.list diff --git a/src/generator/config/subexport.cpp b/src/generator/config/subexport.cpp index 1e1d5dd10..131d72974 100644 --- a/src/generator/config/subexport.cpp +++ b/src/generator/config/subexport.cpp @@ -343,6 +343,12 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo singleproxy["xudp"] = true; if (!tfo.is_undef()) singleproxy["tfo"] = tfo.get(); + if (!x.AlpnList.empty()) { + for (auto &item: x.AlpnList) { + singleproxy["alpn"].push_back(item); + } + } else if (!x.Alpn.empty()) + singleproxy["alpn"].push_back(x.Alpn); if (!scv.is_undef()) singleproxy["skip-cert-verify"] = scv.get(); if (!x.ServerName.empty()) @@ -454,6 +460,12 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo else if (!x.Host.empty()) { singleproxy["sni"] = x.Host; } + if (!x.AlpnList.empty()) { + for (auto &item: x.AlpnList) { + singleproxy["alpn"].push_back(item); + } + } else if (!x.Alpn.empty()) + singleproxy["alpn"].push_back(x.Alpn); if (std::all_of(x.Password.begin(), x.Password.end(), ::isdigit) && !x.Password.empty()) { singleproxy["password"].SetTag("str"); } @@ -553,14 +565,52 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo if (!x.Ports.empty()) singleproxy["ports"] = x.Ports; break; + case ProxyType::TUIC: + singleproxy["type"] = "tuic"; + if (!x.Password.empty()) { + singleproxy["password"] = x.Password; + } + if (!x.UserId.empty()) { + singleproxy["uuid"] = x.UserId; + } + if (!x.token.empty()) { + singleproxy["token"] = x.token; + } + if (!x.ServerName.empty()) { + singleproxy["sni"] = x.ServerName; + } + if (!scv.is_undef()) + singleproxy["skip-cert-verify"] = scv.get(); + if (!x.Alpn.empty()) + singleproxy["alpn"].push_back(x.Alpn); + singleproxy["disable-sni"] = x.DisableSni.get(); + singleproxy["reduce-rtt"] = x.ReduceRtt.get(); + singleproxy["request-timeout"] = x.RequestTimeout; + if (!x.UdpRelayMode.empty()) { + if (x.UdpRelayMode == "native" || x.UdpRelayMode == "quic") { + singleproxy["udp-relay-mode"] = x.UdpRelayMode; + } + } + if (!x.CongestionControl.empty()) { + singleproxy["congestion-controller"] = x.CongestionControl; + } + break; case ProxyType::VLESS: singleproxy["type"] = "vless"; singleproxy["uuid"] = x.UserId; singleproxy["tls"] = x.TLSSecure; + if (!x.AlpnList.empty()) { + for (auto &item: x.AlpnList) { + singleproxy["alpn"].push_back(item); + } + } if (!tfo.is_undef()) singleproxy["tfo"] = tfo.get(); if (xudp && udp) singleproxy["xudp"] = true; + if(!x.PacketEncoding.empty()){ + singleproxy["packet-encoding"] = x.PacketEncoding; + } if (!x.Flow.empty()) singleproxy["flow"] = x.Flow; if (!scv.is_undef()) @@ -571,11 +621,14 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo if (!x.ServerName.empty()) singleproxy["servername"] = x.ServerName; if (!x.ShortId.empty()) { - singleproxy["reality-opts"]["short-id"] = x.ShortId; + singleproxy["reality-opts"]["short-id"] = "" + x.ShortId; } if (!x.PublicKey.empty() || x.Flow == "xtls-rprx-vision") { singleproxy["client-fingerprint"] = "chrome"; } + if (!x.Fingerprint.empty()) { + singleproxy["client-fingerprint"] = x.Fingerprint; + } switch (hash_(x.TransferProtocol)) { case "tcp"_hash: singleproxy["network"] = x.TransferProtocol; @@ -627,7 +680,7 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo // UDP is not supported yet in clash using snell // sees in https://dreamacro.github.io/clash/configuration/outbound.html#snell - if (udp && x.Type != ProxyType::Snell) + if (udp && x.Type != ProxyType::Snell && x.Type != ProxyType::TUIC) singleproxy["udp"] = true; if (block) singleproxy.SetStyle(YAML::EmitterStyle::Block); @@ -717,6 +770,34 @@ proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupCo yamlnode["Proxy Group"] = original_groups; } +void formatterShortId(std::string &input) { + std::string target = "short-id:"; + size_t startPos = input.find(target); + + while (startPos != std::string::npos) { + // 查找对应实例的结束位置 + size_t endPos = input.find("}", startPos); + + if (endPos != std::string::npos) { + // 提取原始id + std::string originalId = input.substr(startPos + target.length(), endPos - startPos - target.length()); + + // 去除原始id中的空格 + originalId.erase(remove_if(originalId.begin(), originalId.end(), ::isspace), originalId.end()); + + // 添加引号 + std::string modifiedId = " \"" + originalId + "\" "; + + // 替换原始id为修改后的id + input.replace(startPos + target.length(), endPos - startPos - target.length(), modifiedId); + } + + // 继续查找下一个实例 + startPos = input.find(target, startPos + 1); + } + +} + std::string proxyToClash(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext) { @@ -764,6 +845,7 @@ std::string proxyToClash(std::vector &nodes, const std::string &base_conf //rulesetToClash(yamlnode, ruleset_content_array, ext.overwrite_original_rules, ext.clash_new_field_name); //std::string output_content = YAML::Dump(yamlnode); replaceAll(output_content, "! ", ""); + formatterShortId(output_content); return output_content; } @@ -1507,7 +1589,9 @@ void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, const std::string &base_conf, std::vector proxy += ", keepalive=" + std::to_string(x.KeepAlive); proxy += ", peers=[{" + generatePeer(x, true) + "}]"; break; + case ProxyType::Hysteria2: + proxy = "Hysteria2," + hostname + "," + port + ",\"" + password + "\""; + if (!x.ServerName.empty()) { + proxy += ",sni=" + x.ServerName; + } + if (!x.UpMbps.empty()) { + std::string search = " Mbps"; + size_t pos = x.UpMbps.find(search); + if (pos != std::string::npos) { + x.UpMbps.replace(pos, search.length(), ""); + } else { + search = "Mbps"; + pos = x.UpMbps.find(search); + if (pos != std::string::npos) { + x.UpMbps.replace(pos, search.length(), ""); + } + } + proxy += ",download-bandwidth=" + x.UpMbps; + } else { + proxy += ",download-bandwidth=100"; + } + if (!scv.is_undef()) + proxy += ",skip-cert-verify=" + std::string(scv.get() ? "true" : "false"); + break; default: continue; } - if (ext.tfo) + if (ext.tfo) { proxy += ",fast-open=true"; - if (ext.udp) + } else { + if (x.Type == ProxyType::Hysteria2) { + proxy += ",fast-open=false"; + } + } + if (ext.udp) { proxy += ",udp=true"; + } else { + if (x.Type == ProxyType::Hysteria2) { + proxy += ",udp=true"; + } + } if (ext.nodelist) @@ -2233,6 +2352,14 @@ static rapidjson::Value stringArrayToJsonArray(const std::string &array, const s return result; } +static rapidjson::Value +vectorToJsonArray(const std::vector &array, rapidjson::MemoryPoolAllocator<> &allocator) { + rapidjson::Value result(rapidjson::kArrayType); + for (const auto &x: array) + result.PushBack(rapidjson::Value(trim(x).c_str(), allocator), allocator); + return result; +} + bool isNumeric(const std::string &str) { for (char c: str) { if (!std::isdigit(static_cast(c))) { @@ -2319,6 +2446,9 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector proxy.AddMember("packet_encoding", rapidjson::StringRef("xudp"), allocator); if (!x.Flow.empty()) proxy.AddMember("flow", rapidjson::StringRef(x.Flow.c_str()), allocator); + if(!x.PacketEncoding.empty()){ + proxy.AddMember("packet_encoding", rapidjson::StringRef(x.PacketEncoding.c_str()), allocator); + } rapidjson::Value vlesstransport(rapidjson::kObjectType); rapidjson::Value vlessheaders(rapidjson::kObjectType); switch (hash_(x.TransferProtocol)) { @@ -2509,6 +2639,38 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector } break; } + case ProxyType::TUIC: { + addSingBoxCommonMembers(proxy, x, "tuic", allocator); + proxy.AddMember("password", rapidjson::StringRef(x.Password.c_str()), allocator); + proxy.AddMember("uuid", rapidjson::StringRef(x.UserId.c_str()), allocator); + if (!x.TLSSecure && !x.Alpn.empty()) { + rapidjson::Value tls(rapidjson::kObjectType); + tls.AddMember("enabled", true, allocator); + if (!scv.is_undef()) { + tls.AddMember("insecure", buildBooleanValue(scv), allocator); + } + if (!x.ServerName.empty()) + tls.AddMember("server_name", rapidjson::StringRef(x.ServerName.c_str()), allocator); + if (!x.Alpn.empty()) { + auto alpns = stringArrayToJsonArray(x.Alpn, ",", allocator); + tls.AddMember("alpn", alpns, allocator); + } + if (!x.DisableSni.is_undef()) { + tls.AddMember("disable_sni", buildBooleanValue(x.DisableSni), allocator); + } + proxy.AddMember("tls", tls, allocator); + } + if (!x.CongestionControl.empty()) { + proxy.AddMember("congestion_control", rapidjson::StringRef(x.CongestionControl.c_str()), allocator); + } + if (!x.UdpRelayMode.empty()) { + proxy.AddMember("udp_relay_mode", rapidjson::StringRef(x.UdpRelayMode.c_str()), allocator); + } + if (!x.ReduceRtt.is_undef()) { + proxy.AddMember("zero_rtt_handshake", buildBooleanValue(x.ReduceRtt), allocator); + } + break; + } default: continue; } @@ -2517,7 +2679,10 @@ proxyToSingBox(std::vector &nodes, rapidjson::Document &json, std::vector tls.AddMember("enabled", true, allocator); if (!x.ServerName.empty()) tls.AddMember("server_name", rapidjson::StringRef(x.ServerName.c_str()), allocator); - if (!x.Alpn.empty()) { + if (!x.AlpnList.empty()) { + auto alpns = vectorToJsonArray(x.AlpnList, allocator); + tls.AddMember("alpn", alpns, allocator); + } else if (!x.Alpn.empty()) { auto alpns = stringArrayToJsonArray(x.Alpn, ",", allocator); tls.AddMember("alpn", alpns, allocator); } diff --git a/src/handler/settings.cpp b/src/handler/settings.cpp index 655f5fdba..d630c5ca4 100644 --- a/src/handler/settings.cpp +++ b/src/handler/settings.cpp @@ -1,6 +1,5 @@ #include #include -#include #include "config/binding.h" #include "handler/webget.h" @@ -565,12 +564,12 @@ void readYAMLConf(YAML::Node &node) writeLog(0, "Load preference settings in YAML format completed.", LOG_LEVEL_INFO); } -template -void find_if_exist(const toml::value &v, const toml::key &k, T& target, U&&... args) -{ - if(v.contains(k)) target = toml::find(v, k); - if constexpr (sizeof...(args) > 0) find_if_exist(v, std::forward(args)...); -} +//template +//void find_if_exist(const toml::value &v, const toml::key &k, T& target, U&&... args) +//{ +// if(v.contains(k)) target = toml::find(v, k); +// if constexpr (sizeof...(args) > 0) find_if_exist(v, std::forward(args)...); +//} void operate_toml_kv_table(const std::vector &arr, const toml::key &key_name, const toml::key &value_name, std::function binary_op) { diff --git a/src/handler/settings.h b/src/handler/settings.h index 495adab1c..cc2d698a3 100644 --- a/src/handler/settings.h +++ b/src/handler/settings.h @@ -13,6 +13,7 @@ #include "utils/string.h" #include "utils/stl_extra.h" #include "utils/tribool.h" +#include struct Settings { @@ -100,7 +101,12 @@ extern Settings global; int importItems(string_array &target, bool scope_limit = true); int loadExternalConfig(std::string &path, ExternalConfig &ext); - +template +void find_if_exist(const toml::value &v, const toml::key &k, T& target, U&&... args) +{ + if(v.contains(k)) target = toml::find(v, k); + if constexpr (sizeof...(args) > 0) find_if_exist(v, std::forward(args)...); +} template void parseGroupTimes(const std::string &src, Args... args) { diff --git a/src/parser/config/proxy.h b/src/parser/config/proxy.h index e53e6d7ee..1e0ab2c8a 100644 --- a/src/parser/config/proxy.h +++ b/src/parser/config/proxy.h @@ -22,7 +22,8 @@ enum class ProxyType { WireGuard, VLESS, Hysteria, - Hysteria2 + Hysteria2, + TUIC }; inline String getProxyTypeName(ProxyType type) { @@ -51,6 +52,8 @@ inline String getProxyTypeName(ProxyType type) { return "Hysteria"; case ProxyType::Hysteria2: return "Hysteria2"; + case ProxyType::TUIC: + return "Tuic"; default: return "Unknown"; } @@ -64,7 +67,7 @@ struct Proxy { String Remark; String Hostname; uint16_t Port = 0; - + String CongestionControl; String Username; String Password; String EncryptMethod; @@ -122,6 +125,13 @@ struct Proxy { String ShortId; String Flow; bool FlowShow = false; + tribool DisableSni; + tribool ReduceRtt; + String UdpRelayMode = "native"; + uint16_t RequestTimeout = 15000; + String token; + std::vector AlpnList; + String PacketEncoding; }; #define SS_DEFAULT_GROUP "SSProvider" @@ -135,5 +145,6 @@ struct Proxy { #define XRAY_DEFAULT_GROUP "XRayProvider" #define HYSTERIA_DEFAULT_GROUP "HysteriaProvider" #define HYSTERIA2_DEFAULT_GROUP "Hysteria2Provider" +#define TUIC_DEFAULT_GROUP "TuicProvider" #endif // PROXY_H_INCLUDED diff --git a/src/parser/subparser.cpp b/src/parser/subparser.cpp index 3b9d28212..a1e4269c3 100644 --- a/src/parser/subparser.cpp +++ b/src/parser/subparser.cpp @@ -51,7 +51,8 @@ void commonConstruct(Proxy &node, ProxyType type, const std::string &group, cons void vmessConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &type, const std::string &id, const std::string &aid, const std::string &net, const std::string &cipher, const std::string &path, const std::string &host, - const std::string &edge, const std::string &tls, const std::string &sni, tribool udp, tribool tfo, + const std::string &edge, const std::string &tls, const std::string &sni, + const std::vector &alpnList, tribool udp, tribool tfo, tribool scv, tribool tls13) { commonConstruct(node, ProxyType::VMess, group, remarks, add, port, udp, tfo, scv, tls13); node.UserId = id.empty() ? "00000000-0000-0000-0000-000000000000" : id; @@ -62,6 +63,7 @@ void vmessConstruct(Proxy &node, const std::string &group, const std::string &re node.ServerName = sni; node.FakeType = type; node.TLSSecure = tls == "tls"; + node.AlpnList = alpnList; node.Host = (host.empty() && !isIPv4(add) && !isIPv6(add)) ? add : trim(host); node.Path = path.empty() ? "/" : trim(path); switch (hash_(net)) { @@ -120,16 +122,17 @@ void httpConstruct(Proxy &node, const std::string &group, const std::string &rem void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &password, const std::string &net, const std::string &mode, - const std::string &host, const std::string &path, const std::string &fp, const std::string &flow, const std::string &tls, const std::string &sni, + const std::string &host, const std::string &path, const std::string &fp, const std::string &sni, + const std::vector &alpnList, tribool udp, tribool tfo, tribool scv, tribool tls13) { commonConstruct(node, ProxyType::Trojan, group, remarks, add, port, udp, tfo, scv, tls13); node.Password = password; - node.TLSSecure = tls.empty() || tls == "true" || tls == "tls" || tls == "xtls"; + node.TLSSecure = true; node.TransferProtocol = net.empty() ? "tcp" : net; node.Fingerprint = fp; node.ServerName = sni; - node.Flow = flow; + node.AlpnList = alpnList; node.Host = (host.empty() && !isIPv4(add) && !isIPv6(add)) ? add : trim(host); node.Path = path.empty() ? "/" : trim(path); switch (hash_(net)) { @@ -200,6 +203,7 @@ void vlessConstruct(Proxy &node, const std::string &group, const std::string &re const std::string &net, const std::string &cipher, const std::string &flow, const std::string &mode, const std::string &path, const std::string &host, const std::string &edge, const std::string &tls, const std::string &pbk, const std::string &sid, const std::string &fp, const std::string &sni, + const std::vector &alpnList,const std::string &packet_encoding, tribool udp, tribool tfo, tribool scv, tribool tls13) { commonConstruct(node, ProxyType::VLESS, group, remarks, add, port, udp, tfo, scv, tls13); @@ -215,6 +219,8 @@ void vlessConstruct(Proxy &node, const std::string &group, const std::string &re node.ShortId = sid; node.Fingerprint = fp; node.ServerName = sni; + node.AlpnList = alpnList; + node.PacketEncoding = packet_encoding; node.Host = (host.empty() && !isIPv4(add) && !isIPv6(add)) ? add : trim(host); node.Path = path.empty() ? "/" : trim(path); switch (hash_(net)) { @@ -250,6 +256,27 @@ void hysteria2Construct(Proxy &node, const std::string &group, const std::string node.Ports = ports; } +void tuicConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, + const std::string &port, const std::string &password, const std::string &congestion_control, + const std::string &alpn, + const std::string &sni, const std::string &uuid, const std::string &udpRelayMode, + const std::string &token, + tribool udp, tribool tfo, + tribool scv, tribool reduceRtt, tribool disableSni, uint16_t request_timeout) { + commonConstruct(node, ProxyType::TUIC, group, remarks, add, port, udp, tfo, scv, tribool()); + node.Password = password; + node.Alpn = alpn; + node.ServerName = sni; + node.CongestionControl = congestion_control; + node.ReduceRtt = reduceRtt; + node.DisableSni = disableSni; + node.UserId = uuid; + node.UdpRelayMode = udpRelayMode; + node.token = token; + node.RequestTimeout = request_timeout; +} + + void explodeVmess(std::string vmess, Proxy &node) { std::string version, ps, add, port, type, id, aid, net, path, host, tls, sni; Document jsondata; @@ -309,7 +336,7 @@ void explodeVmess(std::string vmess, Proxy &node) { add = trim(add); - vmessConstruct(node, V2RAY_DEFAULT_GROUP, ps, add, port, type, id, aid, net, "auto", path, host, "", tls, sni); + vmessConstruct(node, V2RAY_DEFAULT_GROUP, ps, add, port, type, id, aid, net, "auto", path, host, "", tls, sni,std::vector{}); } void explodeVmessConf(std::string content, std::vector &nodes) { @@ -380,7 +407,7 @@ void explodeVmessConf(std::string content, std::vector &nodes) { } } vmessConstruct(node, V2RAY_DEFAULT_GROUP, add + ":" + port, add, port, type, id, aid, net, cipher, path, - host, edge, tls, "", udp, tfo, scv); + host, edge, tls, "",std::vector{}, udp, tfo, scv); nodes.emplace_back(std::move(node)); } return; @@ -433,7 +460,7 @@ void explodeVmessConf(std::string content, std::vector &nodes) { json["vmess"][i]["security"] >> cipher; json["vmess"][i]["sni"] >> sni; vmessConstruct(node, V2RAY_DEFAULT_GROUP, ps, add, port, type, id, aid, net, cipher, path, host, "", - tls, sni, udp, tfo, scv); + tls, sni,std::vector{}, udp, tfo, scv); break; case 3: //ss config json["vmess"][i]["id"] >> id; @@ -821,7 +848,7 @@ void explodeHTTPSub(std::string link, Proxy &node) { } void explodeTrojan(std::string trojan, Proxy &node) { - std::string server, port, psk, addition, group, remark, host, path, network, fp, sni, mode, flow, tls; + std::string server, port, psk, addition, group, remark, host, path, network, fp, sni, mode; tribool tfo, scv; trojan.erase(0, trojan.find("://") + 3); string_size pos = trojan.rfind("#"); @@ -853,8 +880,6 @@ void explodeTrojan(std::string trojan, Proxy &node) { fp = getUrlArg(addition, "fp"); scv = getUrlArg(addition, "allowInsecure"); group = urlDecode(getUrlArg(addition, "group")); - tls = getUrlArg(addition,"security"); - flow = getUrlArg(addition,"flow"); //Case WS if (getUrlArg(addition, "ws") == "1") { @@ -880,8 +905,13 @@ void explodeTrojan(std::string trojan, Proxy &node) { remark = server + ":" + port; if (group.empty()) group = TROJAN_DEFAULT_GROUP; - - trojanConstruct(node, group, remark, server, port, psk, network, mode, host, path, fp, flow, tls, sni, tribool(), tfo, scv); + std::string alpn = getUrlArg(addition, "alpn"); + std::vector alpnList; + if (!alpn.empty()) { + alpnList.push_back(alpn); + } + trojanConstruct(node, group, remark, server, port, psk, network, mode, host, path, fp, sni, alpnList, tribool(), + tfo, scv); } void explodeVless(std::string vless, Proxy &node) { @@ -969,7 +999,7 @@ void explodeQuan(const std::string &quan, Proxy &node) { if (path.empty()) path = "/"; - vmessConstruct(node, group, ps, add, port, type, id, aid, net, cipher, path, host, edge, tls, ""); + vmessConstruct(node, group, ps, add, port, type, id, aid, net, cipher, path, host, edge, tls, "",std::vector{}); } } @@ -1035,10 +1065,11 @@ void explodeNetch(std::string netch, Proxy &node) { edge = GetMember(json, "Edge"); tls = GetMember(json, "TLSSecure"); sni = GetMember(json, "ServerName"); + if (group.empty()) group = V2RAY_DEFAULT_GROUP; vmessConstruct(node, group, remark, address, port, faketype, id, aid, transprot, method, path, host, edge, - tls, sni, udp, tfo, scv); + tls, sni,std::vector{}, udp, tfo, scv); break; case "Socks5"_hash: username = GetMember(json, "Username"); @@ -1060,7 +1091,8 @@ void explodeNetch(std::string netch, Proxy &node) { sni = host; if (group.empty()) group = TROJAN_DEFAULT_GROUP; - trojanConstruct(node, group, remark, address, port, password, transprot, mode, host, path, fp, flow, tls, sni, + trojanConstruct(node, group, remark, address, port, password, transprot, mode, host, path, fp, sni, + std::vector{}, udp, tfo, scv); break; @@ -1084,7 +1116,7 @@ void explodeClash(Node yamlnode, std::vector &nodes) { for (uint32_t i = 0; i < yamlnode[section].size(); i++) { std::string proxytype, ps, server, port, cipher, group, password = "", ports, tempPassword; //common std::string type = "none", id, aid = "0", net = "tcp", path, host, edge, tls, sni; //vmess - std::string fp = "chrome", pbk, sid; //vless + std::string fp = "chrome", pbk, sid,packet_encoding; //vless std::string plugin, pluginopts, pluginopts_mode, pluginopts_host, pluginopts_mux, pluginopts_version, pluginopts_password; //ss std::string protocol, protoparam, obfs, obfsparam; //ssr std::string flow, mode; //trojan @@ -1092,8 +1124,11 @@ void explodeClash(Node yamlnode, std::vector &nodes) { std::string ip, ipv6, private_key, public_key, mtu; //wireguard std::string auth, up, down, obfsParam, insecure, alpn;//hysteria std::string obfsPassword;//hysteria2 + std::string congestion_control, udp_relay_mode, token;// tuic string_array dns_server; tribool udp, tfo, scv; + bool reduceRtt, disableSni;//tuic + std::vector alpnList; Proxy node; singleproxy = yamlnode[section][i]; singleproxy["type"] >>= proxytype; @@ -1143,8 +1178,9 @@ void explodeClash(Node yamlnode, std::vector &nodes) { break; } tls = safe_as(singleproxy["tls"]) == "true" ? "tls" : ""; - - vmessConstruct(node, group, ps, server, port, "", id, aid, net, cipher, path, host, edge, tls, sni, udp, + singleproxy["alpn"] >>= alpnList; + vmessConstruct(node, group, ps, server, port, "", id, aid, net, cipher, path, host, edge, tls, sni, + alpnList, udp, tfo, scv); break; case "ss"_hash: @@ -1283,8 +1319,9 @@ void explodeClash(Node yamlnode, std::vector &nodes) { path.clear(); break; } + singleproxy["alpn"] >>= alpnList; - trojanConstruct(node, group, ps, server, port, password, net, mode, host, path, fp, flow, tls, sni, udp, tfo, scv); + trojanConstruct(node, group, ps, server, port, password, net, mode, host, path, fp, sni, alpnList, udp, tfo, scv); break; case "snell"_hash: group = SNELL_DEFAULT_GROUP; @@ -1355,9 +1392,13 @@ void explodeClash(Node yamlnode, std::vector &nodes) { singleproxy["reality-opts"]["short-id"] >>= sid; } singleproxy["flow"] >>= flow; - + singleproxy["client-fingerprint"] >>= fp; + singleproxy["alpn"] >>= alpnList; + singleproxy["packet-encoding"] >>= packet_encoding; + bool vless_udp; + singleproxy["udp"] >> vless_udp; vlessConstruct(node, XRAY_DEFAULT_GROUP, ps, server, port, type, id, aid, net, "auto", flow, mode, path, - host, "", tls, pbk, sid, fp, sni); + host, "", tls, pbk, sid, fp, sni, alpnList,packet_encoding,udp); break; case "hysteria"_hash: group = HYSTERIA_DEFAULT_GROUP; @@ -1374,6 +1415,7 @@ void explodeClash(Node yamlnode, std::vector &nodes) { singleproxy["protocol"] >> type; singleproxy["sni"] >> host; singleproxy["alpn"][0] >> alpn; + singleproxy["alpn"] >> alpnList; singleproxy["protocol"] >> insecure; singleproxy["ports"] >> ports; sni = host; @@ -1398,6 +1440,27 @@ void explodeClash(Node yamlnode, std::vector &nodes) { hysteria2Construct(node, group, ps, server, port, password, host, up, down, alpn, obfsParam, obfsPassword, sni, public_key, ports, udp, tfo, scv); break; + case "tuic"_hash: + group = TUIC_DEFAULT_GROUP; + uint16_t request_timeout; + singleproxy["password"] >>= password; + singleproxy["uuid"] >>= id; + singleproxy["congestion-controller"] >>= congestion_control; + singleproxy["udp-relay-mode"] >>= udp_relay_mode; + singleproxy["sni"] >>= sni; + if (!singleproxy["alpn"].IsNull()) { + singleproxy["alpn"][0] >>= alpn; + } + singleproxy["disable-sni"] >>= disableSni; + singleproxy["reduce-rtt"] >>= reduceRtt; + singleproxy["token"] >>= token; + singleproxy["request-timeout"] >>= request_timeout; + tuicConstruct(node, TUIC_DEFAULT_GROUP, ps, server, port, password, congestion_control, alpn, sni, id, + udp_relay_mode, token, + tribool(), + tribool(), scv, reduceRtt, disableSni, request_timeout); + + break; default: continue; } @@ -1444,8 +1507,12 @@ void explodeStdVMess(std::string vmess, Proxy &node) { if (remarks.empty()) remarks = add + ":" + port; - - vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, add, port, type, id, aid, net, "auto", path, host, "", tls, ""); + std::string alpn = getUrlArg(addition, "alpn"); + std::vector alpnList; + if (!alpn.empty()) { + alpnList.push_back(alpn); + } + vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, add, port, type, id, aid, net, "auto", path, host, "", tls, "",alpnList); } @@ -1476,7 +1543,10 @@ void explodeStdHysteria(std::string hysteria, Proxy &node) { if (remarks.empty()) remarks = add + ":" + port; - + std::vector alpnList; + if (!alpn.empty()) { + alpnList.push_back(alpn); + } hysteriaConstruct(node, HYSTERIA_DEFAULT_GROUP, remarks, add, port, type, auth, auth_str, host, up, down, alpn, obfsParam, insecure, "", sni); @@ -1484,7 +1554,7 @@ void explodeStdHysteria(std::string hysteria, Proxy &node) { } void explodeStdHysteria2(std::string hysteria2, Proxy &node) { - std::string add, port, password, host, insecure, up, down, alpn, obfsParam, obfsPassword, remarks, sni; + std::string add, port, password, host, insecure, up, down, alpn, obfsParam, obfsPassword, remarks, sni, ports; std::string addition; tribool scv; hysteria2 = hysteria2.substr(12); @@ -1525,11 +1595,12 @@ void explodeStdHysteria2(std::string hysteria2, Proxy &node) { obfsPassword = getUrlArg(addition, "obfs-password"); host = getUrlArg(addition, "sni"); sni = host; + ports = getUrlArg(addition, "ports"); if (remarks.empty()) remarks = add + ":" + port; hysteria2Construct(node, HYSTERIA2_DEFAULT_GROUP, remarks, add, port, password, host, up, down, alpn, obfsParam, - obfsPassword, sni, "", "", tribool(), tribool(), scv); + obfsPassword, sni, "", ports, tribool(), tribool(), scv); return; } @@ -1555,7 +1626,12 @@ void explodeStdVless(std::string vless, Proxy &node) { pbk = getUrlArg(addition, "pbk"); sid = getUrlArg(addition, "sid"); fp = getUrlArg(addition, "fp"); - + std::string packet_encoding = getUrlArg(addition, "packet-encoding"); + std::string alpn = getUrlArg(addition, "alpn"); + std::vector alpnList; + if (!alpn.empty()) { + alpnList.push_back(alpn); + } switch (hash_(net)) { case "tcp"_hash: case "ws"_hash: @@ -1583,7 +1659,7 @@ void explodeStdVless(std::string vless, Proxy &node) { remarks = add + ":" + port; sni = getUrlArg(addition, "sni"); vlessConstruct(node, XRAY_DEFAULT_GROUP, remarks, add, port, type, id, aid, net, "auto", flow, mode, path, host, "", - tls, pbk, sid, fp, sni); + tls, pbk, sid, fp, sni, alpnList,packet_encoding); return; } @@ -1622,8 +1698,12 @@ void explodeShadowrocket(std::string rocket, Proxy &node) { if (remarks.empty()) remarks = add + ":" + port; - - vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, add, port, type, id, aid, net, cipher, path, host, "", tls, ""); + std::string alpn = getUrlArg(addition, "alpn"); + std::vector alpnList; + if (!alpn.empty()) { + alpnList.push_back(alpn); + } + vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, add, port, type, id, aid, net, cipher, path, host, "", tls, "",alpnList); } void explodeKitsunebi(std::string kit, Proxy &node) { @@ -1657,8 +1737,12 @@ void explodeKitsunebi(std::string kit, Proxy &node) { if (remarks.empty()) remarks = add + ":" + port; - - vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, add, port, type, id, aid, net, cipher, path, host, "", tls, ""); + std::string alpn = getUrlArg(addition, "alpn"); + std::vector alpnList; + if (!alpn.empty()) { + alpnList.push_back(alpn); + } + vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, add, port, type, id, aid, net, cipher, path, host, "", tls, "",alpnList); } // peer = (public-key = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=, allowed-ips = "0.0.0.0/0, ::/0", endpoint = engage.cloudflareclient.com:2408, client-id = 139/184/125),(public-key = bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=, endpoint = engage.cloudflareclient.com:2408) @@ -1944,7 +2028,7 @@ bool explodeSurge(std::string surge, std::vector &nodes) { } vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, server, port, "", id, aead, net, method, path, host, - edge, tls, "", udp, tfo, scv, tls13); + edge, tls, "",std::vector{}, udp, tfo, scv, tls13); break; case "http"_hash: //http proxy server = trim(configs[1]); @@ -2010,7 +2094,8 @@ bool explodeSurge(std::string surge, std::vector &nodes) { } } - trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, "", mode, host, "", fp, flow, tls, sni, + trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, "", mode, host, "", fp, sni, + std::vector{}, udp, tfo, scv); break; @@ -2273,7 +2358,7 @@ bool explodeSurge(std::string surge, std::vector &nodes) { remarks = server + ":" + port; vmessConstruct(node, V2RAY_DEFAULT_GROUP, remarks, server, port, "", id, aead, net, method, - path, host, "", tls, "", udp, tfo, scv, tls13); + path, host, "", tls, "",std::vector{}, udp, tfo, scv, tls13); break; case "vless"_hash: //quantumult x style vless link server = trim(configs[0].substr(0, configs[0].rfind(":"))); @@ -2340,7 +2425,7 @@ bool explodeSurge(std::string surge, std::vector &nodes) { remarks = server + ":" + port; vlessConstruct(node, XRAY_DEFAULT_GROUP, remarks, server, port, "", id, aead, net, method, "chrome", "", path, host, "", - tls, "", "", fp, sni, udp, tfo, scv, tls13); + tls, "", "", fp, sni, std::vector{},"", udp, tfo, scv, tls13); break; case "trojan"_hash: //quantumult x style trojan link server = trim(configs[0].substr(0, configs[0].rfind(':'))); @@ -2391,7 +2476,8 @@ bool explodeSurge(std::string surge, std::vector &nodes) { remarks = server + ":" + port; trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, "", mode, host, "", fp, - flow, tls, sni, udp, tfo, scv, tls13); + sni, std::vector{}, + udp, tfo, scv, tls13); break; case "http"_hash: //quantumult x style http links server = trim(configs[0].substr(0, configs[0].rfind(':'))); @@ -2619,7 +2705,7 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { if (outbounds[i].IsObject()) { std::string proxytype, ps, server, port, cipher, group, password, ports, tempPassword; //common std::string type = "none", id, aid = "0", net = "tcp", path, host, edge, tls, sni; //vmess - std::string fp = "chrome", pbk, sid; //vless + std::string fp = "chrome", pbk, sid,packet_encoding; //vless std::string plugin, pluginopts, pluginopts_mode, pluginopts_host, pluginopts_mux; //ss std::string protocol, protoparam, obfs, obfsparam; //ssr std::string flow, mode; //trojan @@ -2628,7 +2714,8 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { std::string auth, up, down, obfsParam, insecure, alpn;//hysteria std::string obfsPassword;//hysteria2 string_array dns_server; - tribool udp, tfo, scv; + std::string congestion_control, udp_relay_mode;//quic + tribool udp, tfo, scv, rrt, disableSni; rapidjson::Value singboxNode = outbounds[i].GetObject(); if (singboxNode.HasMember("type") && singboxNode["type"].IsString()) { Proxy node; @@ -2637,6 +2724,7 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { server = GetMember(singboxNode, "server"); port = GetMember(singboxNode, "server_port"); tfo = GetMember(singboxNode, "tcp_fast_open"); + std::vector alpnList; if (singboxNode.HasMember("tls") && singboxNode["tls"].IsObject()) { rapidjson::Value tlsObj = singboxNode["tls"].GetObject(); if (tlsObj.HasMember("enabled") && tlsObj["enabled"].IsBool() && tlsObj["enabled"].GetBool()) { @@ -2647,11 +2735,18 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { rapidjson::Value alpns = tlsObj["alpn"].GetArray(); if (alpns.Size() > 0) { alpn = alpns[0].GetString(); + for (auto &item: tlsObj["alpn"].GetArray()) { + if (item.IsString()) + alpnList.emplace_back(item.GetString()); + } } } if (tlsObj.HasMember("insecure") && tlsObj["insecure"].IsBool()) { scv = tlsObj["insecure"].GetBool(); } + if (tlsObj.HasMember("disable_sni") && tlsObj["disable_sni"].IsBool()) { + disableSni = tlsObj["disable_sni"].GetBool(); + } if (tlsObj.HasMember("certificate") && tlsObj["certificate"].IsString()) { public_key = tlsObj["certificate"].GetString(); } @@ -2682,7 +2777,7 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { cipher = GetMember(singboxNode, "security"); explodeSingboxTransport(singboxNode, net, host, path, edge); vmessConstruct(node, group, ps, server, port, "", id, aid, net, cipher, path, host, edge, tls, - sni, udp, + sni,alpnList, udp, tfo, scv); break; case "shadowsocks"_hash: @@ -2697,7 +2792,7 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { group = TROJAN_DEFAULT_GROUP; password = GetMember(singboxNode, "password"); explodeSingboxTransport(singboxNode, net, host, path, edge); - trojanConstruct(node, group, ps, server, port, password, net, mode, host, path, fp, flow, tls, sni, udp, + trojanConstruct(node, group, ps, server, port, password, net, mode, host, path, fp, sni, alpnList, udp, tfo, scv); break; @@ -2705,6 +2800,7 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { group = XRAY_DEFAULT_GROUP; id = GetMember(singboxNode, "uuid"); flow = GetMember(singboxNode, "flow"); + packet_encoding = GetMember(singboxNode,"packet_encoding"); if (singboxNode.HasMember("transport") && singboxNode["transport"].IsObject()) { rapidjson::Value transport = singboxNode["transport"].GetObject(); net = GetMember(transport, "type"); @@ -2741,8 +2837,9 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { } } } + vlessConstruct(node, group, ps, server, port, type, id, aid, net, "auto", flow, mode, path, - host, "", tls, pbk, sid, fp, sni); + host, "", tls, pbk, sid, fp, sni, alpnList,packet_encoding,udp); break; case "http"_hash: password = GetMember(singboxNode, "password"); @@ -2797,6 +2894,20 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { hysteria2Construct(node, group, ps, server, port, password, host, up, down, alpn, obfsParam, obfsPassword, sni, public_key, "", udp, tfo, scv); break; + case "tuic"_hash: + group = TUIC_DEFAULT_GROUP; + password = GetMember(singboxNode, "password"); + id = GetMember(singboxNode, "uuid"); + congestion_control = GetMember(singboxNode, "congestion_control"); + if (singboxNode.HasMember("zero_rtt_handshake") && singboxNode["zero_rtt_handshake"].IsBool()) { + rrt = singboxNode["zero_rtt_handshake"].GetBool(); + } + udp_relay_mode = GetMember(singboxNode, "udp_relay_mode"); + tuicConstruct(node, TUIC_DEFAULT_GROUP, ps, server, port, password, congestion_control, alpn, + sni, id, udp_relay_mode, "", + tribool(), + tribool(), scv, rrt, disableSni); + break; default: continue; } @@ -2808,6 +2919,67 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector &nodes) { } } +void explodeTuic(const std::string &tuic, Proxy &node) { + std::string add, port, password, host, insecure, alpn, remarks, sni, ports, congestion_control; + std::string addition; + tribool scv; + std::string link = tuic.substr(7); + string_size pos; + + pos = link.rfind("#"); + if (pos != std::string::npos) { + remarks = urlDecode(link.substr(pos + 1)); + link.erase(pos); + } + + pos = link.rfind("?"); + if (pos != std::string::npos) { + addition = link.substr(pos + 1); + link.erase(pos); + } + + std::string uuid; + pos = link.find(":"); + if (pos != std::string::npos) { + uuid = link.substr(0, pos); + link = link.substr(pos + 1); + if (strFind(link, "@")) { + pos = link.find("@"); + if (pos != std::string::npos) { + password = link.substr(0, pos); + link = link.substr(pos + 1); + } + } + } + + pos = link.find(":"); + if (pos != std::string::npos) { + add = link.substr(0, pos); + link = link.substr(pos + 1); + pos = link.find("?"); + if (pos != std::string::npos) { + port = link.substr(0, pos); + addition = link.substr(pos + 1); + } else { + port = link; + } + } + + + scv = getUrlArg(addition, "insecure"); + alpn = getUrlArg(addition, "alpn"); + sni = getUrlArg(addition, "sni"); + congestion_control = getUrlArg(addition, "congestion_control"); + if (remarks.empty()) + remarks = add + ":" + port; + tuicConstruct(node, TUIC_DEFAULT_GROUP, remarks, add, port, password, congestion_control, alpn, sni, uuid, "native", + "", + tribool(), + tribool(), scv); + + return; +} + void explode(const std::string &link, Proxy &node) { if (startsWith(link, "ssr://")) explodeSSR(link, node); @@ -2827,6 +2999,8 @@ void explode(const std::string &link, Proxy &node) { explodeVless(link, node); else if (strFind(link, "hysteria://") || strFind(link, "hy://")) explodeHysteria(link, node); + else if (strFind(link, "tuic://")) + explodeTuic(link, node); else if (strFind(link, "hysteria2://") || strFind(link, "hy2://")) explodeHysteria2(link, node); else if (isLink(link)) diff --git a/src/parser/subparser.h b/src/parser/subparser.h index f552c69f0..2d8825d54 100644 --- a/src/parser/subparser.h +++ b/src/parser/subparser.h @@ -19,16 +19,79 @@ enum class ConfType SUB, Local }; -void hysteriaConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &type, const std::string &auth, const std::string &auth_str, const std::string &host, const std::string &up, const std::string &down, const std::string &alpn, const std::string &obfsParam, const std::string &insecure,const std::string &ports,const std::string &sni,tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); -void hysteria2Construct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &password, const std::string &host, const std::string &up, const std::string &down, const std::string &alpn, const std::string &obfsParam, const std::string &obfsPassword, const std::string &sni, const std::string &publicKey, const std::string &ports, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool()); -void vlessConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &type, const std::string &id, const std::string &aid, const std::string &net, const std::string &cipher, const std::string &flow, const std::string &mode, const std::string &path, const std::string &host, const std::string &edge, const std::string &tls,const std::string &pkd, const std::string &sid, const std::string &fp,const std::string &sni, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); -void vmessConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &type, const std::string &id, const std::string &aid, const std::string &net, const std::string &cipher, const std::string &path, const std::string &host, const std::string &edge, const std::string &tls, const std::string &sni, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); -void ssrConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &protocol, const std::string &method, const std::string &obfs, const std::string &password, const std::string &obfsparam, const std::string &protoparam, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool()); -void ssConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &method, const std::string &plugin, const std::string &pluginopts, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); -void socksConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool()); -void httpConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, bool tls, tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); -void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, const std::string &port, const std::string &password, const std::string &net, const std::string &mode, const std::string &host, const std::string &path, const std::string &fp, const std::string &flow, const std::string &tls, const std::string &sni, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); -void snellConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &obfs, const std::string &host, uint16_t version = 0, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool()); + +void hysteriaConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, + const std::string &port, const std::string &type, const std::string &auth, + const std::string &auth_str, const std::string &host, const std::string &up, + const std::string &down, const std::string &alpn, const std::string &obfsParam, + const std::string &insecure, const std::string &ports, const std::string &sni, + tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), + tribool tls13 = tribool()); + +void hysteria2Construct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, + const std::string &port, const std::string &password, const std::string &host, + const std::string &up, const std::string &down, const std::string &alpn, + const std::string &obfsParam, const std::string &obfsPassword, const std::string &sni, + const std::string &publicKey, const std::string &ports, + tribool udp, tribool tfo, + tribool scv); + +void vlessConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, + const std::string &port, const std::string &type, const std::string &id, const std::string &aid, + const std::string &net, const std::string &cipher, const std::string &flow, const std::string &mode, + const std::string &path, const std::string &host, const std::string &edge, const std::string &tls, + const std::string &pkd, const std::string &sid, const std::string &fp, const std::string &sni, + const std::vector &alpnList,const std::string &packet_encoding, + tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), + tribool tls13 = tribool()); + +void vmessConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, + const std::string &port, const std::string &type, const std::string &id, const std::string &aid, + const std::string &net, const std::string &cipher, const std::string &path, const std::string &host, + const std::string &edge, const std::string &tls, const std::string &sni, + const std::vector &alpnList, tribool udp = tribool(), + tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); + +void ssrConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, + const std::string &port, const std::string &protocol, const std::string &method, + const std::string &obfs, const std::string &password, const std::string &obfsparam, + const std::string &protoparam, tribool udp = tribool(), tribool tfo = tribool(), + tribool scv = tribool()); + +void ssConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, + const std::string &port, const std::string &password, const std::string &method, + const std::string &plugin, const std::string &pluginopts, tribool udp = tribool(), + tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); + +void socksConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, + const std::string &port, const std::string &username, const std::string &password, + tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool()); + +void httpConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, + const std::string &port, const std::string &username, const std::string &password, bool tls, + tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); + +void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, + const std::string &port, const std::string &password, const std::string &net, const std::string &mode, + const std::string &host, const std::string &path, const std::string &fp, const std::string &sni, + const std::vector &alpnList, + tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), + tribool tls13 = tribool()); + +void snellConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, + const std::string &port, const std::string &password, const std::string &obfs, + const std::string &host, uint16_t version = 0, tribool udp = tribool(), tribool tfo = tribool(), + tribool scv = tribool()); + +void tuicConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &add, + const std::string &port, const std::string &password, const std::string &congestion_control, + const std::string &alpn, + const std::string &sni, const std::string &uuid, const std::string &udpRelayMode, + const std::string &token, + tribool udp = tribool(), tribool tfo = tribool(), + tribool scv = tribool(), tribool reduceRtt = tribool(), tribool disableSni = tribool(), + uint16_t request_timeout = 15000); + void explodeVmess(std::string vmess, Proxy &node); void explodeSSR(std::string ssr, Proxy &node); void explodeSS(std::string ss, Proxy &node); diff --git a/src/version.h b/src/version.h index b55542a77..dc22e778b 100644 --- a/src/version.h +++ b/src/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H_INCLUDED #define VERSION_H_INCLUDED -#define VERSION "v0.11.0 Mannix" +#define VERSION "v0.11.1 Mannix" #endif // VERSION_H_INCLUDED