Compare commits

...

34 Commits

Author SHA1 Message Date
asdlokj1qpi233
add52ec9cb Compatible with lowercase host in vless and vmess.(#55) 2025-08-29 15:50:36 +08:00
asdlokj1qpi23
d22cd9b50a Merge pull request #56 from mi0e/anytls-conversion
feat: add anytls link parser
2025-08-27 17:29:48 +08:00
asdlokj1qpi23
d22cba4c69 Merge pull request #54 from greatcoolge/master
参数映射缺失
2025-08-27 17:29:14 +08:00
mi0e
dfa8188739 feat: add anytls link parser 2025-08-26 13:15:58 +08:00
greatcoolge
4bd91f89d8 subparser.cpp 2025-08-23 13:54:48 +08:00
asdlokj1qpi23
57c18b0c69 Merge pull request #51 from ionull/master
vless ws on loon
2025-08-05 13:01:49 +08:00
Tsung Wu
d95995a807 fix: compile error 2025-08-02 00:56:12 +08:00
Tsung Wu
add0e6da1a fix: vless ws nodes export on loon 2025-08-01 22:09:32 +08:00
Tsung Wu
80363459f2 fix: some vless nodes not exported on loon 2025-08-01 21:08:59 +08:00
asdlokj1qpi233
1624d0f10d add xhttp support (#43) 2025-07-31 18:06:05 +08:00
asdlokj1qpi233
36229fc2ad fix compile error 2025-07-29 11:28:01 +08:00
asdlokj1qpi233
aa721c5b06 fix compile error 2025-07-29 11:25:32 +08:00
asdlokj1qpi233
60d20e42c8 fix bug(#37),(#39),(#42) 2025-07-29 11:21:19 +08:00
asdlokj1qpi233
645361a07e fix compile error 2025-07-29 11:03:32 +08:00
asdlokj1qpi23
9947eebbfc Merge pull request #47 from gengjiawen/patch-1
support surge hy2 port-hopping
2025-07-29 10:22:55 +08:00
Jiawen Geng
11bc5c7236 Update subexport.cpp 2025-07-25 01:38:00 -07:00
Jiawen Geng
4b4d227603 support surge hy2 port-hopping 2025-07-25 01:36:12 -07:00
asdlokj1qpi23
383deb54a7 Fix error about hysteria2.(#33) 2025-05-16 11:18:27 +08:00
asdlokj1qpi23
766434b771 Fix error about hysteria2.(#33) 2025-05-14 17:45:46 +08:00
asdlokj1qpi23
cc9e3b77d4 Fix error about v2ray-http-upgrade for vless.(#31) 2025-05-06 10:14:15 +08:00
asdlokj1qpi23
4ae0d1b0bd Resolve the VLESS TCP error.(#29) 2025-04-28 09:48:58 +08:00
asdlokj1qpi23
a428c844a2 update template 2025-04-27 15:02:54 +08:00
asdlokj1qpi23
3a0784f1f6 fix surge error,(#27) (#28) 2025-04-27 12:42:34 +08:00
asdlokj1qpi23
4787d821bd fix surge error,(#27) (#28) 2025-04-27 11:13:36 +08:00
asdlokj1qpi23
97192f9e5d fix windows compile error
Some checks failed
GitHub CI / Linux aarch64 Build (push) Failing after 29s
GitHub CI / Linux amd64 Build (push) Failing after 25s
GitHub CI / Linux armv7 Build (push) Failing after 30s
GitHub CI / Linux x86 Build (push) Failing after 20s
Publish Docker Image / Build linux/386 Image (push) Failing after 6m53s
Publish Docker Image / Build linux/amd64 Image (push) Failing after 14m57s
Publish Docker Image / Build linux/arm/v7 Image (push) Failing after 41m26s
Publish Docker Image / Build linux/arm64 Image (push) Failing after 23m44s
Publish Docker Image / Merge (push) Has been skipped
GitHub CI / macOS arm Build (push) Has been cancelled
GitHub CI / macOS x86 Build (push) Has been cancelled
GitHub CI / Windows amd64 Build (push) Has been cancelled
GitHub CI / Windows x86 Build (push) Has been cancelled
2025-04-24 15:10:37 +08:00
asdlokj1qpi23
b0b04a0130 fix bugs, (#23) 2025-04-24 14:55:15 +08:00
asdlokj1qpi23
4e73f5695e fix bugs, (#25)
Some checks failed
GitHub CI / Linux aarch64 Build (push) Failing after 31s
GitHub CI / Linux amd64 Build (push) Failing after 18s
GitHub CI / Linux armv7 Build (push) Failing after 22s
GitHub CI / Linux x86 Build (push) Failing after 20s
Publish Docker Image / Build linux/386 Image (push) Failing after 7m6s
Publish Docker Image / Build linux/amd64 Image (push) Failing after 21m7s
Publish Docker Image / Build linux/arm/v7 Image (push) Failing after 31m29s
Publish Docker Image / Build linux/arm64 Image (push) Failing after 33m17s
GitHub CI / macOS arm Build (push) Has been cancelled
GitHub CI / macOS x86 Build (push) Has been cancelled
GitHub CI / Windows amd64 Build (push) Has been cancelled
GitHub CI / Windows x86 Build (push) Has been cancelled
Publish Docker Image / Merge (push) Has been cancelled
2025-04-21 09:27:39 +08:00
asdlokj1qpi23
dd59d514bd fix bugs, (#24) 2025-04-21 09:22:53 +08:00
asdlokj1qpi23
57ed33f96c fix error
Some checks failed
GitHub CI / Linux aarch64 Build (push) Failing after 43s
GitHub CI / Linux amd64 Build (push) Failing after 20s
GitHub CI / Linux armv7 Build (push) Failing after 32s
GitHub CI / Linux x86 Build (push) Failing after 18s
Publish Docker Image / Build linux/386 Image (push) Failing after 6m32s
Publish Docker Image / Build linux/amd64 Image (push) Failing after 27m34s
Publish Docker Image / Build linux/arm/v7 Image (push) Failing after 31m23s
Publish Docker Image / Build linux/arm64 Image (push) Failing after 34m18s
Publish Docker Image / Merge (push) Has been skipped
GitHub CI / macOS arm Build (push) Has been cancelled
GitHub CI / macOS x86 Build (push) Has been cancelled
GitHub CI / Windows amd64 Build (push) Has been cancelled
GitHub CI / Windows x86 Build (push) Has been cancelled
2025-04-18 17:07:25 +08:00
asdlokj1qpi23
eab18a9568 support hy2 and vless for mixed (#2) (#23) (#9) 2025-04-18 17:04:22 +08:00
asdlokj1qpi23
c242e631ba support vless reality for Loon,(#22) 2025-04-18 15:04:42 +08:00
asdlokj1qpi23
b649fdc253 fix mieru error
Some checks failed
GitHub CI / Linux aarch64 Build (push) Failing after 1m4s
GitHub CI / Linux amd64 Build (push) Failing after 51s
GitHub CI / Linux armv7 Build (push) Failing after 54s
GitHub CI / Linux x86 Build (push) Failing after 51s
Publish Docker Image / Build linux/386 Image (push) Failing after 9m26s
Publish Docker Image / Build linux/amd64 Image (push) Failing after 22m56s
Publish Docker Image / Build linux/arm/v7 Image (push) Failing after 52m6s
Publish Docker Image / Build linux/arm64 Image (push) Failing after 28m15s
Publish Docker Image / Merge (push) Has been skipped
GitHub CI / macOS arm Build (push) Has been cancelled
GitHub CI / macOS x86 Build (push) Has been cancelled
GitHub CI / Windows amd64 Build (push) Has been cancelled
GitHub CI / Windows x86 Build (push) Has been cancelled
2025-04-17 17:38:13 +08:00
asdlokj1qpi23
5759f52ebc fix bugs,add mierus link support 2025-04-17 16:03:58 +08:00
asdlokj1qpi23
e4b3ee816d add support anytls for singbox, add support mieru for clash 2025-04-17 15:10:37 +08:00
12 changed files with 888 additions and 537 deletions

View File

@@ -7,6 +7,7 @@ body:
value: |
遇到问题请先尝试使用[Action](https://github.com/tindy2013/subconverter/actions)中的最新版本
如果是使用公共转换服务中遇到的问题,请先联系服务的提供者。
Please note that if the bug report does not include reproduction steps, configuration files, or relevant URL content, it will not be considered.
- type: checkboxes
id: version-check
attributes:

View File

@@ -1,6 +1,6 @@
PROJECT(subconverter LANGUAGES CXX)
SET(BUILD_TARGET_NAME ${PROJECT_NAME})
CMAKE_MINIMUM_REQUIRED(VERSION 3.4)
CMAKE_MINIMUM_REQUIRED(VERSION 3.5)
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/")
INCLUDE_DIRECTORIES("${CMAKE_SOURCE_DIR}/include/")

View File

@@ -8,8 +8,10 @@ WORKDIR /
RUN set -xe && \
apk add --no-cache --virtual .build-tools git g++ build-base linux-headers cmake python3 && \
apk add --no-cache --virtual .build-deps curl-dev rapidjson-dev pcre2-dev yaml-cpp-dev && \
git clone https://github.com/ftk/quickjspp --depth=1 && \
git clone --no-checkout https://github.com/ftk/quickjspp.git && \
cd quickjspp && \
git fetch origin 0c00c48895919fc02da3f191a2da06addeb07f09 && \
git checkout 0c00c48895919fc02da3f191a2da06addeb07f09 && \
git submodule update --init && \
cmake -DCMAKE_BUILD_TYPE=Release . && \
make quickjs -j $THREADS && \

View File

@@ -16,8 +16,10 @@ cmake -DCMAKE_BUILD_TYPE=Release -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOO
make install -j3 > /dev/null
cd ..
git clone https://github.com/ftk/quickjspp --depth=1
git clone --no-checkout https://github.com/ftk/quickjspp.git
cd quickjspp
git fetch origin 0c00c48895919fc02da3f191a2da06addeb07f09
git checkout 0c00c48895919fc02da3f191a2da06addeb07f09
cmake -DCMAKE_BUILD_TYPE=Release .
make quickjs -j3 > /dev/null
install -d /usr/lib/quickjs/

View File

@@ -18,8 +18,10 @@ make -j6 > /dev/null
sudo make install > /dev/null
cd ..
git clone https://github.com/ftk/quickjspp --depth=1
git clone --no-checkout https://github.com/ftk/quickjspp.git
cd quickjspp
git fetch origin 0c00c48895919fc02da3f191a2da06addeb07f09
git checkout 0c00c48895919fc02da3f191a2da06addeb07f09
cmake -DCMAKE_BUILD_TYPE=Release .
make quickjs -j6 > /dev/null
sudo install -d /usr/local/lib/quickjs/

View File

@@ -13,8 +13,10 @@ cmake -DCMAKE_BUILD_TYPE=Release -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOO
make install -j4
cd ..
git clone https://github.com/ftk/quickjspp --depth=1
git clone --no-checkout https://github.com/ftk/quickjspp.git
cd quickjspp
git fetch origin 0c00c48895919fc02da3f191a2da06addeb07f09
git checkout 0c00c48895919fc02da3f191a2da06addeb07f09
patch quickjs/quickjs-libc.c -i ../scripts/patches/0001-quickjs-libc-add-realpath-for-Windows.patch
cmake -G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=Release \

View File

@@ -16,8 +16,10 @@ cd rapidjson
cp -r include/* $PREFIX/include/
cd ..
git clone https://github.com/ftk/quickjspp --depth=1
git clone --no-checkout https://github.com/ftk/quickjspp.git
cd quickjspp
git fetch origin 0c00c48895919fc02da3f191a2da06addeb07f09
git checkout 0c00c48895919fc02da3f191a2da06addeb07f09
cmake -DCMAKE_BUILD_TYPE=Release .
make quickjs -j3
install -d $PREFIX/lib/quickjs/

View File

@@ -37,6 +37,15 @@ const string_array clash_ssr_ciphers = {
"rc4-md5", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb",
"aes-192-cfb", "aes-256-cfb", "chacha20-ietf", "xchacha20", "none"
};
bool isNumeric(const std::string &str) {
for (char c: str) {
if (!std::isdigit(static_cast<unsigned char>(c))) {
return false;
}
}
return true;
}
std::string
vmessLinkConstruct(const std::string &remarks, const std::string &add, const std::string &port, const std::string &type,
@@ -573,6 +582,25 @@ proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const ProxyGroupCo
}
}
break;
case ProxyType::Mieru:
singleproxy["type"] = "mieru";
if (!x.Password.empty()) {
singleproxy["password"] = x.Password;
}
if (!x.Username.empty()) {
singleproxy["username"] = x.Username;
}
if (!x.Multiplexing.empty()) {
singleproxy["multiplexing"] = x.Multiplexing;
}
if (!x.TransferProtocol.empty()) {
singleproxy["transport"] = x.TransferProtocol;
}
if (!x.Ports.empty()) {
singleproxy["port-range"] = x.Ports;
singleproxy.remove("port");
}
break;
case ProxyType::VLESS:
singleproxy["type"] = "vless";
singleproxy["uuid"] = x.UserId;
@@ -619,6 +647,9 @@ proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const ProxyGroupCo
singleproxy["ws-opts"]["headers"]["Host"] = x.Host;
if (!x.Edge.empty())
singleproxy["ws-opts"]["headers"]["Edge"] = x.Edge;
if (!x.V2rayHttpUpgrade.is_undef()) {
singleproxy["ws-opts"]["v2ray-http-upgrade"] = x.V2rayHttpUpgrade.get();
}
} else {
singleproxy["ws-path"] = x.Path;
if (!x.Host.empty())
@@ -911,6 +942,7 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
std::string proxy, section, real_section;
string_array args, headers;
std::string search = " Mbps";
switch (x.Type) {
case ProxyType::Shadowsocks:
@@ -1039,16 +1071,25 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
case ProxyType::Hysteria2:
if (surge_ver < 4)
continue;
proxy = "hysteria, " + hostname + ", " + port + ", password=" + password;
if (x.DownSpeed)
proxy += ", download-bandwidth=" + x.DownSpeed;
proxy = "hysteria2, " + hostname + ", " + port + ", password=" + password;
if (!x.DownMbps.empty()) {
if (!isNumeric(x.DownMbps)) {
size_t pos = x.DownMbps.find(search);
if (pos != std::string::npos) {
x.DownMbps.replace(pos, search.length(), "");
}
}
proxy += ", download-bandwidth=" +x.DownMbps;
}
if (!scv.is_undef())
proxy += ",skip-cert-verify=" + std::string(scv.get() ? "true" : "false");
if (!x.Fingerprint.empty())
proxy += ",server-cert-fingerprint-sha256=" + x.Fingerprint;
if (!x.SNI.empty())
proxy += ",sni=" + x.SNI;
if (!x.ServerName.empty())
proxy += ",sni=" + x.ServerName;
if (!x.Ports.empty())
proxy += ",port-hopping=" + x.Ports;
break;
case ProxyType::WireGuard:
if (surge_ver < 4 && surge_ver != -3)
@@ -1162,22 +1203,24 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
}
std::string proxyToSingle(std::vector<Proxy> &nodes, int types, extra_settings &ext) {
/// types: SS=1 SSR=2 VMess=4 Trojan=8
/// types: SS=1 SSR=2 VMess=4 Trojan=8,hysteria2=16,vless=32
std::string proxyStr, allLinks;
bool ss = GETBIT(types, 1), ssr = GETBIT(types, 2), vmess = GETBIT(types, 3), trojan = GETBIT(types, 4);
bool ss = GETBIT(types, 1), ssr = GETBIT(types, 2), vmess = GETBIT(types, 3), trojan = GETBIT(types, 4), hysteria2 =
GETBIT(types, 5), vless = GETBIT(types, 6);
for (Proxy &x: nodes) {
std::string remark = x.Remark;
std::string &hostname = x.Hostname, &sni = x.ServerName, &password = x.Password, &method = x.EncryptMethod, &
plugin = x.Plugin, &pluginopts = x.PluginOption, &protocol = x.Protocol, &protoparam = x.
ProtocolParam,
&obfs = x.OBFS, &obfsparam = x.OBFSParam, &id = x.UserId, &transproto = x.TransferProtocol, &host = x.
Host, &
path = x.Path, &faketype = x.FakeType;
ProtocolParam, &flow = x.Flow, &pbk = x.PublicKey, &sid = x.ShortId, &fp = x.Fingerprint,
&packet_encoding = x.PacketEncoding, &fake_type = x.FakeType, &mode = x.GRPCMode,
&obfs = x.OBFS, &obfsparam = x.OBFSParam, &obfsPassword = x.OBFSPassword, &id = x.UserId, &transproto =
x.TransferProtocol, &host = x.
Host, &tls = x.TLSStr, &path = x.Path, &faketype = x.FakeType, &ports = x.Ports;
bool &tlssecure = x.TLSSecure;
std::vector<string> alpns = x.AlpnList;
std::string port = std::to_string(x.Port);
std::string aid = std::to_string(x.AlterId);
switch (x.Type) {
case ProxyType::Shadowsocks:
if (ss) {
@@ -1223,6 +1266,93 @@ std::string proxyToSingle(std::vector<Proxy> &nodes, int types, extra_settings &
vmessLinkConstruct(remark, hostname, port, faketype, id, aid, transproto, path, host,
tlssecure ? "tls" : ""));
break;
case ProxyType::Hysteria2:
if (!hysteria2)
continue;
proxyStr = "hysteria2://" + password + "@" + hostname + ":" + port + (ports.empty() ? "" : "," + ports)
+ "?insecure=" +
(x.AllowInsecure.get() ? "1" : "0");
if (!obfsparam.empty()) {
proxyStr += "&obfs=" + obfsparam;
if (!obfsPassword.empty()) {
proxyStr += "&obfs-password=" + obfsparam;
}
}
if (!sni.empty()) {
proxyStr += "&sni=" + sni;
}
proxyStr += "#" + urlEncode(remark);
break;
case ProxyType::VLESS:
if (!vless)
continue;
// tls = getUrlArg(addition, "security");
// net = getUrlArg(addition, "type");
// flow = getUrlArg(addition, "flow");
// 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");
proxyStr = "vless://" + (id.empty()
? "00000000-0000-0000-0000-000000000000"
: id) + "@" + hostname + ":" + port+"?";
if (!tls.empty()) {
if (!pbk.empty()) {
proxyStr += "&security=reality";
}else {
proxyStr += "&security=" + tls;
}
}
if (!flow.empty()) {
proxyStr += "&flow=" + flow;
}
if (!pbk.empty()) {
proxyStr += "&pbk=" + pbk;
}
if (!sid.empty()) {
proxyStr += "&sid=" + sid;
}
if (!fp.empty()) {
proxyStr += "&fp=" + fp;
}
if (!packet_encoding.empty()) {
proxyStr += "&packet-encoding=" + packet_encoding;
}
if (!alpns.empty()) {
proxyStr += "&alpn=" + alpns[0];
}
if (!sni.empty()) {
proxyStr += "&sni=" + sni;
}
if (!transproto.empty()) {
proxyStr += "&type=" + transproto;
switch (hash_(transproto)) {
case "tcp"_hash:
case "ws"_hash:
case "h2"_hash:
proxyStr += "&headerType=" + fake_type;
if (!host.empty()) {
proxyStr += "&host=" + host;
}
proxyStr += "&path=" + urlEncode(path.empty() ? "/" : path);
break;
case "grpc"_hash:
proxyStr += "&serviceName=" + path;
proxyStr += "&mode=" + mode;
break;
case "quic"_hash:
proxyStr += "&headerType=" + fake_type;
proxyStr += "&quicSecurity=" + (host.empty() ? sni : host);
proxyStr += "&key=" + path;
break;
default:
break;
}
}
proxyStr += "#" + urlEncode(remark);
break;
case ProxyType::Trojan:
if (!trojan)
continue;
@@ -1248,8 +1378,7 @@ std::string proxyToSingle(std::vector<Proxy> &nodes, int types, extra_settings &
if (ext.nodelist)
return allLinks;
else
return base64Encode(allLinks);
return base64Encode(allLinks);
}
std::string proxyToSSSub(std::string base_conf, std::vector<Proxy> &nodes, extra_settings &ext) {
@@ -2088,13 +2217,13 @@ proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
std::string &hostname = x.Hostname, &username = x.Username, &password = x.Password, &method = x.EncryptMethod, &
plugin = x.Plugin, &pluginopts = x.PluginOption, &id = x.UserId, &transproto = x.TransferProtocol, &host
= x.Host, &path = x.Path, &protocol = x.Protocol, &protoparam = x.ProtocolParam, &obfs = x.OBFS, &
obfsparam = x.OBFSParam;
obfsparam = x.OBFSParam, flow = x.Flow, pk = x.PublicKey, shortId = x.ShortId, sni = x.ServerName;
std::string port = std::to_string(x.Port), aid = std::to_string(x.AlterId);
bool &tlssecure = x.TLSSecure;
tribool scv = ext.skip_cert_verify;
scv.define(x.AllowInsecure);
tribool udp = x.UDP.is_undef() ? ext.udp.is_undef() ? false : ext.udp.get() : x.UDP.get();
std::string proxy;
switch (x.Type) {
@@ -2129,6 +2258,36 @@ proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
if (!scv.is_undef())
proxy += ",skip-cert-verify=" + std::string(scv.get() ? "true" : "false");
break;
case ProxyType::VLESS:
if (flow != "xtls-rprx-vision") {
if (transproto == "ws") {
proxy = "VLESS," + hostname + "," + port + ",\"" + id + "\"" +
",path=" + path + ",host=" + host + ",transport=" + transproto +
",udp=" + (udp.get() ? "true" : "false") + ",over-tls=" + (
tlssecure ? "true" : "false") + ",sni=" + sni;
} else {
continue;
}
} else {
proxy = "VLESS," + hostname + "," + port + ",\"" + id + "\",flow=" + flow + ",public-key=\"" + pk +
"\",short-id=" + shortId + ",udp=" + (udp.get() ? "true" : "false") + ",over-tls=" + (
tlssecure ? "true" : "false") + ",sni=" + sni;
}
switch (hash_(transproto)) {
case "tcp"_hash:
proxy += ",transport=tcp";
break;
default:
if (transproto != "ws") {
continue;
} else {
break;;
}
}
if (!scv.is_undef())
proxy += ",skip-cert-verify=" + std::string(scv.get() ? "true" : "false");
break;
case ProxyType::ShadowsocksR:
proxy = "ShadowsocksR," + hostname + "," + port + "," + method + ",\"" + password + "\",protocol=" +
protocol + ",protocol-param=" + protoparam + ",obfs=" + obfs + ",obfs-param=" + obfsparam;
@@ -2397,15 +2556,6 @@ vectorToJsonArray(const std::vector<std::string> &array, rapidjson::MemoryPoolAl
return result;
}
bool isNumeric(const std::string &str) {
for (char c: str) {
if (!std::isdigit(static_cast<unsigned char>(c))) {
return false;
}
}
return true;
}
void
proxyToSingBox(std::vector<Proxy> &nodes, rapidjson::Document &json,
std::vector<RulesetContent> &ruleset_content_array,
@@ -2709,6 +2859,29 @@ proxyToSingBox(std::vector<Proxy> &nodes, rapidjson::Document &json,
}
break;
}
case ProxyType::AnyTLS: {
addSingBoxCommonMembers(proxy, x, "anytls", allocator);
proxy.AddMember("password", rapidjson::StringRef(x.Password.c_str()), allocator);
rapidjson::Value tls(rapidjson::kObjectType);
tls.AddMember("enabled", true, allocator);
if (!scv.is_undef()) {
tls.AddMember("insecure", buildBooleanValue(scv), allocator);
}
if (!x.SNI.empty())
tls.AddMember("server_name", rapidjson::StringRef(x.SNI.c_str()), allocator);
if (!x.AlpnList.empty()) {
auto alpns = vectorToJsonArray(x.AlpnList, allocator);
tls.AddMember("alpn", alpns, allocator);
}
if (!x.Fingerprint.empty()) {
rapidjson::Value utls(rapidjson::kObjectType);
utls.AddMember("enabled", true, allocator);
utls.AddMember("fingerprint", rapidjson::StringRef(x.Fingerprint.c_str()), allocator);
tls.AddMember("utls", utls, allocator);
}
proxy.AddMember("tls", tls, allocator);
break;
}
default:
continue;
}

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,8 @@ enum class ProxyType
Hysteria,
Hysteria2,
TUIC,
AnyTLS
AnyTLS,
Mieru
};
inline String getProxyTypeName(ProxyType type) {
@@ -89,6 +90,7 @@ struct Proxy {
uint16_t IdleSessionCheckInterval=30;
uint16_t IdleSessionTimeout=30;
uint16_t MinIdleSession=0;
String TLSStr;
bool TLSSecure = false;
String Host;
@@ -142,6 +144,8 @@ struct Proxy {
String UnderlyingProxy;
std::vector<String> AlpnList;
String PacketEncoding;
String Multiplexing;
tribool V2rayHttpUpgrade;
};
#define SS_DEFAULT_GROUP "SSProvider"
@@ -157,4 +161,5 @@ struct Proxy {
#define HYSTERIA2_DEFAULT_GROUP "Hysteria2Provider"
#define TUIC_DEFAULT_GROUP "TuicProvider"
#define ANYTLS_DEFAULT_GROUP "AnyTLSProvider"
#define MIERU_DEFAULT_GROUP "MieruProvider"
#endif // PROXY_H_INCLUDED

View File

@@ -37,14 +37,25 @@ std::map<std::string, std::string> parsedMD5;
std::string modSSMD5 = "f7653207090ce3389115e9c88541afe0";
//remake from speedtestutil
std::string removeBrackets(const std::string& input) {
std::string result = input;
size_t left = result.find('[');
size_t right = result.find(']');
if (left != std::string::npos && right != std::string::npos && right > left) {
result.erase(right, 1); // 删除 ']'
result.erase(left, 1); // 删除 '['
}
return result;
}
void commonConstruct(Proxy &node, ProxyType type, const std::string &group, const std::string &remarks,
const std::string &server, const std::string &port, const tribool &udp, const tribool &tfo,
const tribool &scv, const tribool &tls13, const std::string &underlying_proxy) {
node.Type = type;
node.Group = group;
node.Remark = remarks;
node.Hostname = server;
node.Hostname = removeBrackets(server);
node.Port = to_int(port);
node.UDP = udp;
node.TCPFastOpen = tfo;
@@ -212,6 +223,21 @@ void anyTlSConstruct(Proxy &node, const std::string &group, const std::string &r
node.MinIdleSession = minIdleSession;
}
void mieruConstruct(Proxy &node, const std::string &group, const std::string &remarks,
const std::string &port, const std::string &password,
const std::string &host, const std::string &ports,
const std::string &username, const std::string &multiplexing,
const std::string &transfer_protocol, tribool udp,
tribool tfo, tribool scv,
tribool tls13, const std::string &underlying_proxy) {
commonConstruct(node, ProxyType::Mieru, group, remarks, host, port, udp, tfo, scv, tls13, underlying_proxy);
node.Host = trim(host);
node.Password = password;
node.Ports = ports;
node.TransferProtocol = transfer_protocol.empty() ? "TCP" : trim(transfer_protocol);
node.Username = username;
node.Multiplexing = multiplexing.empty() ? "MULTIPLEXING_LOW" : trim(multiplexing);
}
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,
@@ -220,7 +246,7 @@ void vlessConstruct(Proxy &node, const std::string &group, const std::string &re
const std::string &pbk, const std::string &sid, const std::string &fp, const std::string &sni,
const std::vector<std::string> &alpnList, const std::string &packet_encoding,
tribool udp, tribool tfo,
tribool scv, tribool tls13, const std::string &underlying_proxy) {
tribool scv, tribool tls13, const std::string &underlying_proxy, tribool v2ray_http_upgrade) {
commonConstruct(node, ProxyType::VLESS, group, remarks, add, port, udp, tfo, scv, tls13, underlying_proxy);
node.UserId = id.empty() ? "00000000-0000-0000-0000-000000000000" : id;
node.AlterId = to_int(aid);
@@ -236,6 +262,7 @@ void vlessConstruct(Proxy &node, const std::string &group, const std::string &re
node.ServerName = sni;
node.AlpnList = alpnList;
node.PacketEncoding = packet_encoding;
node.TLSStr = tls;
switch (hash_(net)) {
case "grpc"_hash:
node.Host = host;
@@ -243,12 +270,13 @@ void vlessConstruct(Proxy &node, const std::string &group, const std::string &re
node.GRPCServiceName = path.empty() ? "/" : urlEncode(urlDecode(trim(path)));
break;
case "quic"_hash:
node.QUICSecure = host;
node.Host = host;
node.QUICSecret = path.empty() ? "/" : trim(path);
break;
default:
node.Host = (host.empty() && !isIPv4(add) && !isIPv6(add)) ? add.data() : trim(host);
node.Path = path.empty() ? "/" : urlDecode(trim(path));
node.V2rayHttpUpgrade = v2ray_http_upgrade;
break;
}
}
@@ -915,6 +943,11 @@ void explodeTrojan(std::string trojan, Proxy &node) {
network = "ws";
}
else if (getUrlArg(addition, "type") == "grpc") {
path = getUrlArg(addition, "serviceName");
network = "grpc";
}
if (remark.empty())
remark = server + ":" + port;
if (group.empty())
@@ -935,6 +968,24 @@ void explodeVless(std::string vless, Proxy &node) {
}
}
void explodeMierus(std::string mierus, Proxy &node) {
if (strFind(mierus, "mierus://")) {
if (regMatch(mierus, "mierus://(.*?)@(.*)")) {
explodeStdMieru(mierus.substr(9), node);
} else {
mierus = urlSafeBase64Decode(mierus.substr(9));
explodeStdMieru("mierus://" + mierus, node);
}
} else if (strFind(mierus, "mieru://")) {
if (regMatch(mierus, "mierus://(.*?)@(.*)")) {
explodeStdMieru(mierus.substr(8), node);
} else {
mierus = urlSafeBase64Decode(mierus.substr(8));
explodeStdMieru("mierus://" + mierus, node);
}
}
}
void explodeHysteria(std::string hysteria, Proxy &node) {
printf("explodeHysteria\n");
hysteria = regReplace(hysteria, "(hysteria|hy)://", "hysteria://");
@@ -1144,7 +1195,7 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
string_array dns_server;
std::vector<String> alpns;
String alpn2;
std::string fingerprint;
std::string fingerprint, multiplexing, transfer_protocol, v2ray_http_upgrade;
tribool udp, tfo, scv;
bool reduceRtt, disableSni; //tuic
std::vector<std::string> alpnList;
@@ -1154,8 +1205,11 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
singleproxy["name"] >>= ps;
singleproxy["server"] >>= server;
singleproxy["port"] >>= port;
singleproxy["port-range"] >>= ports;
if (port.empty() || port == "0")
continue;
if (ports.empty())
continue;
udp = safe_as<std::string>(singleproxy["udp"]);
scv = safe_as<std::string>(singleproxy["skip-cert-verify"]);
switch (hash_(proxytype)) {
@@ -1182,6 +1236,9 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
singleproxy["ws-opts"]["path"])
: "/";
singleproxy["ws-opts"]["headers"]["Host"] >>= host;
if (host.empty()) {
singleproxy["ws-opts"]["headers"]["host"] >>= host;
}
singleproxy["ws-opts"]["headers"]["Edge"] >>= edge;
} else {
path = singleproxy["ws-path"].IsDefined()
@@ -1353,7 +1410,6 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
break;
case "vless"_hash:
group = XRAY_DEFAULT_GROUP;
singleproxy["uuid"] >>= id;
singleproxy["alterId"] >>= aid;
net = singleproxy["network"].IsDefined() ? safe_as<std::string>(singleproxy["network"]) : "tcp";
@@ -1362,6 +1418,7 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
: safe_as<std::string>(
singleproxy["servername"]);
switch (hash_(net)) {
case "tcp"_hash:
case "http"_hash:
singleproxy["http-opts"]["path"][0] >>= path;
singleproxy["http-opts"]["headers"]["Host"][0] >>= host;
@@ -1374,7 +1431,13 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
singleproxy["ws-opts"]["path"])
: "/";
singleproxy["ws-opts"]["headers"]["Host"] >>= host;
if (host.empty()) {
singleproxy["ws-opts"]["headers"]["host"] >>= host;
}
singleproxy["ws-opts"]["headers"]["Edge"] >>= edge;
if (singleproxy["ws-opts"]["v2ray-http-upgrade"].IsDefined()) {
v2ray_http_upgrade = safe_as<std::string>(singleproxy["ws-opts"]["v2ray-http-upgrade"]);
}
} else {
path = singleproxy["ws-path"].IsDefined()
? safe_as<std::string>(singleproxy["ws-path"])
@@ -1382,6 +1445,7 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
singleproxy["ws-headers"]["Host"] >>= host;
singleproxy["ws-headers"]["Edge"] >>= edge;
}
break;
case "h2"_hash:
singleproxy["h2-opts"]["path"] >>= path;
@@ -1413,7 +1477,8 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
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, alpnList, packet_encoding, udp);
host, "", tls, pbk, sid, fp, sni, alpnList, packet_encoding, udp, tribool(), tribool(),
tribool(), "", v2ray_http_upgrade);
break;
case "hysteria"_hash:
group = HYSTERIA_DEFAULT_GROUP;
@@ -1443,8 +1508,24 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
singleproxy["password"] >>= password;
if (password.empty())
singleproxy["auth"] >>= password;
singleproxy["up"] >>= up;
singleproxy["down"] >>= down;
if (singleproxy["up"].IsDefined()) {
singleproxy["up"] >>= up;
if (up.empty()) {
try {
up = singleproxy["up"].as<std::string>();
} catch (const YAML::BadConversion& e) {
}
}
}
if (singleproxy["down"].IsDefined()) {
singleproxy["down"] >>= down;
if (down.empty()) {
try {
down = singleproxy["down"].as<std::string>();
} catch (const YAML::BadConversion& e) {
}
}
}
singleproxy["obfs"] >>= obfsParam;
singleproxy["obfs-password"] >>= obfsPassword;
singleproxy["sni"] >>= host;
@@ -1480,7 +1561,7 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
singleproxy["password"] >>= password;
singleproxy["sni"] >>= sni;
if (!singleproxy["alpn"].IsNull()&& singleproxy["alpn"].size() >= 1) {
if (!singleproxy["alpn"].IsNull() && singleproxy["alpn"].size() >= 1) {
singleproxy["alpn"][0] >>= alpn;
alpns.push_back(alpn);
if (singleproxy["alpn"].size() >= 2 && !singleproxy["alpn"][1].IsNull()) {
@@ -1491,7 +1572,24 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes) {
singleproxy["client-fingerprint"] >>= fingerprint;
anyTlSConstruct(node, ANYTLS_DEFAULT_GROUP, ps, port, password, server, alpns, fingerprint, sni,
udp,
tribool(), scv, tribool(),"",30,30,0);
tribool(), scv, tribool(), "", 30, 30, 0);
break;
case "mieru"_hash:
group = MIERU_DEFAULT_GROUP;
singleproxy["password"] >>= password;
singleproxy["username"] >>= user;
singleproxy["port-range"] >>= ports;
if (!singleproxy["multiplexing"].IsNull()) {
singleproxy["multiplexing"] >>= multiplexing;
}
transfer_protocol = "TCP";
if (!singleproxy["transport"].IsNull()) {
singleproxy["transport"] >>= transfer_protocol;
}
mieruConstruct(node, MIERU_DEFAULT_GROUP, ps, port, password, server, ports, user, multiplexing,
transfer_protocol,
udp,
tribool(), scv, tribool(), "");
break;
default:
continue;
@@ -1587,6 +1685,51 @@ void explodeStdHysteria(std::string hysteria, Proxy &node) {
return;
}
void explodeStdMieru(std::string mieru, Proxy &node) {
std::string username, password, host, port, ports, profile, protocol, multiplexing, mtu, remarks;
std::string addition;
tribool udp, tfo, scv, tls13;
// 去除前缀
string_size pos;
// 提取 remarks
pos = mieru.rfind("#");
if (pos != mieru.npos) {
remarks = urlDecode(mieru.substr(pos + 1));
mieru.erase(pos);
}
// 提取参数
pos = mieru.rfind("?");
if (pos != mieru.npos) {
addition = mieru.substr(pos + 1);
mieru.erase(pos);
}
// 账号密码@host
if (regGetMatch(mieru, R"(^(.*?):(.*?)@(.*)$)", 4, 0, &username, &password, &host))
return;
// 提取端口port=多个情况)
port = getUrlArg(addition, "port");
if (port.find('-') != std::string::npos) {
ports = port;
}
// 提取协议(多个 protocol
protocol = getUrlArg(addition, "protocol");
multiplexing = getUrlArg(addition, "multiplexing");
mtu = getUrlArg(addition, "mtu");
if (remarks.empty())
remarks = host;
mieruConstruct(node, "MieruGroup", remarks, port,
password, host, ports, username, multiplexing, protocol,
udp, tfo, scv, tls13, "");
}
void explodeStdHysteria2(std::string hysteria2, Proxy &node) {
std::string add, port, password, host, insecure, up, down, alpn, obfsParam, obfsPassword, remarks, sni, ports;
std::string addition;
@@ -1675,6 +1818,12 @@ void explodeStdVless(std::string vless, Proxy &node) {
host = getUrlArg(addition, strFind(addition, "sni") ? "sni" : "host");
path = getUrlArg(addition, "path");
break;
case "xhttp"_hash: // 新增对 type=xhttp 的支持
net = "h2"; // 视为 h2/http2 传输
type = getUrlArg(addition, "headerType");
host = getUrlArg(addition, strFind(addition, "sni") ? "sni" : "host");
path = getUrlArg(addition, "path");
break;
case "grpc"_hash:
host = getUrlArg(addition, "sni");
path = getUrlArg(addition, "serviceName");
@@ -2751,6 +2900,7 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector<Proxy> &nodes) {
std::string auth, up, down, obfsParam, insecure, alpn; //hysteria
std::string obfsPassword; //hysteria2
string_array dns_server;
std::string fingerprint;
std::string congestion_control, udp_relay_mode; //quic
tribool udp, tfo, scv, rrt, disableSni;
rapidjson::Value singboxNode = outbounds[i].GetObject();
@@ -2800,6 +2950,12 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector<Proxy> &nodes) {
sid = reality["short_id"].GetString();
}
}
if (tlsObj.HasMember("utls") && tlsObj["utls"].IsObject()) {
if (rapidjson::Value reality = tlsObj["utls"].GetObject();
reality.HasMember("fingerprint") && reality["fingerprint"].IsString()) {
fingerprint = reality["fingerprint"].GetString();
}
}
} else {
tls = "false";
}
@@ -2919,6 +3075,14 @@ void explodeSingbox(rapidjson::Value &outbounds, std::vector<Proxy> &nodes) {
obfsParam, insecure, ports, sni,
udp, tfo, scv);
break;
case "anytls"_hash:
group = ANYTLS_DEFAULT_GROUP;
password = GetMember(singboxNode, "password");
anyTlSConstruct(node, ANYTLS_DEFAULT_GROUP, ps, port, password, server, alpnList, fingerprint,
sni,
udp,
tribool(), scv, tribool(), "", 30, 30, 0);
break;
case "hysteria2"_hash:
group = HYSTERIA2_DEFAULT_GROUP;
password = GetMember(singboxNode, "password");
@@ -3018,6 +3182,61 @@ void explodeTuic(const std::string &tuic, Proxy &node) {
return;
}
void explodeAnyTLS(std::string anytls, Proxy &node) {
std::string add, port, password, remarks, addition, sni, fp;
std::vector<std::string> alpnList;
tribool udp, tfo, scv;
anytls = anytls.substr(9);
string_size pos;
pos = anytls.rfind("#");
if (pos != anytls.npos) {
remarks = urlDecode(anytls.substr(pos + 1));
anytls.erase(pos);
}
pos = anytls.rfind("?");
if (pos != anytls.npos) {
addition = anytls.substr(pos + 1);
anytls.erase(pos);
}
pos = anytls.find("@");
if (pos != anytls.npos) {
password = anytls.substr(0, pos);
anytls = anytls.substr(pos + 1);
}
pos = anytls.find(":");
if (pos != anytls.npos) {
add = anytls.substr(0, pos);
port = anytls.substr(pos + 1);
}
if (remarks.empty())
remarks = add + ":" + port;
std::string alpn = getUrlArg(addition, "alpn");
if (!alpn.empty()) {
auto alpns = split(alpn, ",");
for (auto &item : alpns) {
if (!item.empty())
alpnList.emplace_back(item);
}
}
fp = getUrlArg(addition, "fp");
if (fp.empty())
fp = getUrlArg(addition, "fingerprint");
sni = getUrlArg(addition, "sni");
udp = getUrlArg(addition, "udp");
tfo = getUrlArg(addition, "tfo");
scv = getUrlArg(addition, "insecure");
anyTlSConstruct(node, ANYTLS_DEFAULT_GROUP, remarks, port, password, add, alpnList, fp, sni, udp, tfo, scv,
tribool(), "", 30, 30, 0);
}
void explode(const std::string &link, Proxy &node) {
if (startsWith(link, "ssr://"))
explodeSSR(link, node);
@@ -3039,8 +3258,12 @@ void explode(const std::string &link, Proxy &node) {
explodeHysteria(link, node);
else if (strFind(link, "tuic://"))
explodeTuic(link, node);
else if (strFind(link, "anytls://"))
explodeAnyTLS(link, node);
else if (strFind(link, "hysteria2://") || strFind(link, "hy2://"))
explodeHysteria2(link, node);
else if (strFind(link, "mierus://") || strFind(link, "mieru://"))
explodeMierus(link, node);
else if (isLink(link))
explodeHTTPSub(link, node);
}

View File

@@ -42,7 +42,7 @@ void vlessConstruct(Proxy &node, const std::string &group, const std::string &re
const std::string &pkd, const std::string &sid, const std::string &fp, const std::string &sni,
const std::vector<std::string> &alpnList,const std::string &packet_encoding,
tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(),
tribool tls13 = tribool(),const std::string& underlying_proxy="");
tribool tls13 = tribool(),const std::string& underlying_proxy="",tribool v2ray_http_upgrade=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,
@@ -95,7 +95,13 @@ void tuicConstruct(Proxy &node, const std::string &group, const std::string &rem
tribool udp = tribool(), tribool tfo = tribool(),
tribool scv = tribool(), tribool reduceRtt = tribool(), tribool disableSni = tribool(),
uint16_t request_timeout = 15000,const std::string& underlying_proxy="");
void mieruConstruct(Proxy &node, const std::string &group, const std::string &remarks,
const std::string &port, const std::string &password,
const std::string &host, const std::string &ports,
const std::string &username,const std::string &multiplexing,
const std::string &transfer_protocol, tribool udp,
tribool tfo, tribool scv,
tribool tls13, const std::string &underlying_proxy);
void explodeVmess(std::string vmess, Proxy &node);
void explodeSSR(std::string ssr, Proxy &node);
@@ -105,11 +111,11 @@ void explodeSS(std::string ss, Proxy &node);
void explodeTrojan(std::string trojan, Proxy &node);
void explodeQuan(const std::string &quan, Proxy &node);
void explodeMierus(std::string mieru, Proxy &node);
void explodeStdVMess(std::string vmess, Proxy &node);
void explodeStdVless(std::string vless, Proxy &node);
void explodeStdMieru(std::string mieru, Proxy &node);
void explodeStdHysteria(std::string hysteria, Proxy &node);
void explodeStdHysteria2(std::string hysteria2, Proxy &node);
@@ -124,6 +130,8 @@ void explodeHysteria(std::string hysteria, Proxy &node);
void explodeHysteria2(std::string hysteria2, Proxy &node);
void explodeAnyTLS(std::string anytls, Proxy &node);
/// Parse a link
void explode(const std::string &link, Proxy &node);