Qv2ray/src/plugins/protocols/core/OutboundHandler.cpp
2020-12-27 06:30:30 +08:00

176 lines
7.4 KiB
C++

#include "OutboundHandler.hpp"
#include "3rdparty/QJsonStruct/QJsonIO.hpp"
#include <QUrl>
#include <QUrlQuery>
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<QString, QJsonObject> 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<QString> BuiltinSerializer::SupportedLinkPrefixes() const
{
return { "http", "socks" };
}