#include "OutboundHandler.hpp" #include "3rdparty/QJsonStruct/QJsonIO.hpp" #include #include using namespace Qv2rayPlugin; const Qv2rayPlugin::OutboundInfoObject BuiltinSerializer::GetOutboundInfo(const QString &protocol, const QJsonObject &outbound) const { OutboundInfoObject obj; obj[INFO_PROTOCOL] = protocol; if (protocol == "http") { const auto http = HttpServerObject::fromJson(outbound["servers"].toArray().first()); obj[INFO_SERVER] = http.address; obj[INFO_PORT] = http.port; } else if (protocol == "socks") { const auto socks = SocksServerObject::fromJson(outbound["servers"].toArray().first()); obj[INFO_SERVER] = socks.address; obj[INFO_PORT] = socks.port; } else if (protocol == "vmess") { const auto vmess = VMessServerObject::fromJson(outbound["vnext"].toArray().first()); obj[INFO_SERVER] = vmess.address; obj[INFO_PORT] = vmess.port; } else if (protocol == "vless") { const auto vless = VLESSServerObject::fromJson(outbound["vnext"].toArray().first()); obj[INFO_SERVER] = vless.address; obj[INFO_PORT] = vless.port; } else if (protocol == "shadowsocks") { const auto ss = ShadowSocksServerObject::fromJson(outbound["servers"].toArray().first()); obj[INFO_SERVER] = ss.address; obj[INFO_PORT] = ss.port; } return obj; } const void BuiltinSerializer::SetOutboundInfo(const QString &protocol, const Qv2rayPlugin::OutboundInfoObject &info, QJsonObject &outbound) const { if ((QStringList{ "http", "socks", "shadowsocks" }).contains(protocol)) { QJsonIO::SetValue(outbound, info[INFO_SERVER].toString(), "servers", 0, "address"); QJsonIO::SetValue(outbound, info[INFO_PORT].toInt(), "servers", 0, "port"); } else if ((QStringList{ "vless", "vmess" }).contains(protocol)) { QJsonIO::SetValue(outbound, info[INFO_SERVER].toString(), "vnext", 0, "address"); QJsonIO::SetValue(outbound, info[INFO_PORT].toInt(), "vnext", 0, "port"); } } const QString BuiltinSerializer::SerializeOutbound(const QString &protocol, const QString &alias, const QString &, const QJsonObject &obj, const QJsonObject &objStream) const { if (protocol == "http" || protocol == "socks") { QUrl url; url.setScheme(protocol); url.setHost(QJsonIO::GetValue(obj, { "servers", 0, "address" }).toString()); url.setPort(QJsonIO::GetValue(obj, { "servers", 0, "port" }).toInt()); if (QJsonIO::GetValue(obj, { "servers", 0 }).toObject().contains("users")) { url.setUserName(QJsonIO::GetValue(obj, { "servers", 0, "users", 0, "user" }).toString()); url.setPassword(QJsonIO::GetValue(obj, { "servers", 0, "users", 0, "pass" }).toString()); } return url.toString(); } if (protocol == "vless") { QUrl url; url.setFragment(QUrl::toPercentEncoding(alias)); url.setScheme(protocol); url.setHost(QJsonIO::GetValue(obj, { "vnext", 0, "address" }).toString()); url.setPort(QJsonIO::GetValue(obj, { "vnext", 0, "port" }).toInt()); url.setUserName(QJsonIO::GetValue(obj, {"vnext", 0, "users", 0, "id" }).toString()); // -------- COMMON INFORMATION -------- QUrlQuery query; const auto encryption = QJsonIO::GetValue(obj, {"vnext", 0, "users", 0, "encryption"}).toString("none"); if (encryption != "none") query.addQueryItem("encryption", encryption); const auto network = QJsonIO::GetValue(objStream, "network").toString("tcp"); if (network != "tcp") query.addQueryItem("type", network); const auto security = QJsonIO::GetValue(objStream, "security").toString("none"); if (security != "none") query.addQueryItem("security", security); // -------- TRANSPORT RELATED -------- if (network == "kcp") { const auto seed = QJsonIO::GetValue(objStream, {"kcpSettings", "seed"}).toString(); if (!seed.isEmpty()) query.addQueryItem("seed", QUrl::toPercentEncoding(seed)); const auto headerType = QJsonIO::GetValue(objStream, {"kcpSettings", "header", "type"}).toString("none"); if (headerType != "none") query.addQueryItem("headerType", headerType); } else if (network == "http") { const auto path = QJsonIO::GetValue(objStream, {"httpSettings", "path"}).toString("/"); query.addQueryItem("path", QUrl::toPercentEncoding(path)); const auto hosts = QJsonIO::GetValue(objStream, {"httpSetting", "host"}).toArray(); QStringList hostList; for (const auto item: hosts) { const auto host = item.toString(); if (!host.isEmpty()) hostList << host; } query.addQueryItem("host", QUrl::toPercentEncoding(hostList.join(","))); } else if (network == "ws") { const auto path = QJsonIO::GetValue(objStream, {"wsSettings", "path"}).toString("/"); query.addQueryItem("path", QUrl::toPercentEncoding(path)); const auto host = QJsonIO::GetValue(objStream, {"wsSettings", "headers", "Host"}).toString(); query.addQueryItem("host", host); } else if (network == "quic") { const auto quicSecurity = QJsonIO::GetValue(objStream, {"quicSettings", "security"}).toString("none"); if (quicSecurity != "none") { query.addQueryItem("quicSecurity", quicSecurity); const auto key = QJsonIO::GetValue(objStream, {"quicSettings", "key"}).toString(); query.addQueryItem("key", QUrl::toPercentEncoding(key)); const auto headerType = QJsonIO::GetValue(objStream, {"quicSettings", "header", "type"}).toString("none"); if (headerType != "none") query.addQueryItem("headerType", headerType); } } // -------- TLS RELATED -------- const auto tlsKey = security == "xtls" ? "xtlsSettings" : "tlsSettings"; const auto sni = QJsonIO::GetValue(objStream, {tlsKey, "serverName"}).toString(); if (!sni.isEmpty()) query.addQueryItem("sni", sni); // TODO: ALPN Support // -------- XTLS Flow -------- if (security == "xtls") { const auto flow = QJsonIO::GetValue(obj, "vnext", 0, "users", 0, "flow").toString(); query.addQueryItem("flow", flow); } // ======== END OF QUERY ======== url.setQuery(query); return url.toString(QUrl::FullyEncoded); } return "(Unsupported)"; } const QPair BuiltinSerializer::DeserializeOutbound(const QString &link, QString *alias, QString *errorMessage) const { if (link.startsWith("http://") || link.startsWith("socks://")) { const QUrl url = link; QJsonObject root; QJsonIO::SetValue(root, url.host(), "servers", 0, "address"); QJsonIO::SetValue(root, url.port(), "servers", 0, "port"); if (url.userName().isEmpty() && url.password().isEmpty()) { QJsonIO::SetValue(root, url.userName(), "servers", 0, "users", 0, "user"); QJsonIO::SetValue(root, url.password(), "servers", 0, "users", 0, "pass"); } return { url.scheme(), root }; } return {}; } const QList BuiltinSerializer::SupportedLinkPrefixes() const { return { "http", "socks" }; }