diff --git a/client/core/models/protocols/xrayProtocolConfig.cpp b/client/core/models/protocols/xrayProtocolConfig.cpp index bb4e61457..be3edee3b 100644 --- a/client/core/models/protocols/xrayProtocolConfig.cpp +++ b/client/core/models/protocols/xrayProtocolConfig.cpp @@ -3,86 +3,274 @@ #include #include -#include "../../../core/utils/protocolEnum.h" -#include "../../../core/protocols/protocolUtils.h" -#include "../../../core/utils/constants/configKeys.h" -#include "../../../core/utils/constants/protocolConstants.h" +#include "core/utils/protocolEnum.h" +#include "core/protocols/protocolUtils.h" +#include "core/utils/constants/configKeys.h" +#include "core/utils/constants/protocolConstants.h" using namespace amnezia; using namespace ProtocolUtils; + namespace amnezia { +// ═════════════════════════════════════════════════════════════════════════════ +// XrayXPaddingConfig +// ═════════════════════════════════════════════════════════════════════════════ + +QJsonObject XrayXPaddingConfig::toJson() const +{ + QJsonObject obj; + if (!bytesMin.isEmpty()) obj[configKey::xPaddingBytesMin] = bytesMin; + if (!bytesMax.isEmpty()) obj[configKey::xPaddingBytesMax] = bytesMax; + obj[configKey::xPaddingObfsMode] = obfsMode; + if (!key.isEmpty()) obj[configKey::xPaddingKey] = key; + if (!header.isEmpty()) obj[configKey::xPaddingHeader] = header; + if (!placement.isEmpty()) obj[configKey::xPaddingPlacement] = placement; + if (!method.isEmpty()) obj[configKey::xPaddingMethod] = method; + return obj; +} + +XrayXPaddingConfig XrayXPaddingConfig::fromJson(const QJsonObject &json) +{ + XrayXPaddingConfig c; + c.bytesMin = json.value(configKey::xPaddingBytesMin).toString(); + c.bytesMax = json.value(configKey::xPaddingBytesMax).toString(); + c.obfsMode = json.value(configKey::xPaddingObfsMode).toBool(true); + c.key = json.value(configKey::xPaddingKey).toString("www.googletagmanager.com"); + c.header = json.value(configKey::xPaddingHeader).toString(); + c.placement = json.value(configKey::xPaddingPlacement).toString("Cookie"); + c.method = json.value(configKey::xPaddingMethod).toString("Repeat-x"); + return c; +} + +// ═════════════════════════════════════════════════════════════════════════════ +// XrayXmuxConfig +// ═════════════════════════════════════════════════════════════════════════════ + +QJsonObject XrayXmuxConfig::toJson() const +{ + QJsonObject obj; + obj[configKey::xmuxEnabled] = enabled; + if (!maxConcurrencyMin.isEmpty()) obj[configKey::xmuxMaxConcurrencyMin] = maxConcurrencyMin; + if (!maxConcurrencyMax.isEmpty()) obj[configKey::xmuxMaxConcurrencyMax] = maxConcurrencyMax; + if (!maxConnectionsMin.isEmpty()) obj[configKey::xmuxMaxConnectionsMin] = maxConnectionsMin; + if (!maxConnectionsMax.isEmpty()) obj[configKey::xmuxMaxConnectionsMax] = maxConnectionsMax; + if (!cMaxReuseTimesMin.isEmpty()) obj[configKey::xmuxCMaxReuseTimesMin] = cMaxReuseTimesMin; + if (!cMaxReuseTimesMax.isEmpty()) obj[configKey::xmuxCMaxReuseTimesMax] = cMaxReuseTimesMax; + if (!hMaxRequestTimesMin.isEmpty()) obj[configKey::xmuxHMaxRequestTimesMin] = hMaxRequestTimesMin; + if (!hMaxRequestTimesMax.isEmpty()) obj[configKey::xmuxHMaxRequestTimesMax] = hMaxRequestTimesMax; + if (!hMaxReusableSecsMin.isEmpty()) obj[configKey::xmuxHMaxReusableSecsMin] = hMaxReusableSecsMin; + if (!hMaxReusableSecsMax.isEmpty()) obj[configKey::xmuxHMaxReusableSecsMax] = hMaxReusableSecsMax; + if (!hKeepAlivePeriod.isEmpty()) obj[configKey::xmuxHKeepAlivePeriod] = hKeepAlivePeriod; + return obj; +} + +XrayXmuxConfig XrayXmuxConfig::fromJson(const QJsonObject &json) +{ + XrayXmuxConfig c; + c.enabled = json.value(configKey::xmuxEnabled).toBool(true); + c.maxConcurrencyMin = json.value(configKey::xmuxMaxConcurrencyMin).toString("0"); + c.maxConcurrencyMax = json.value(configKey::xmuxMaxConcurrencyMax).toString("0"); + c.maxConnectionsMin = json.value(configKey::xmuxMaxConnectionsMin).toString("0"); + c.maxConnectionsMax = json.value(configKey::xmuxMaxConnectionsMax).toString("0"); + c.cMaxReuseTimesMin = json.value(configKey::xmuxCMaxReuseTimesMin).toString("0"); + c.cMaxReuseTimesMax = json.value(configKey::xmuxCMaxReuseTimesMax).toString("0"); + c.hMaxRequestTimesMin = json.value(configKey::xmuxHMaxRequestTimesMin).toString("0"); + c.hMaxRequestTimesMax = json.value(configKey::xmuxHMaxRequestTimesMax).toString("0"); + c.hMaxReusableSecsMin = json.value(configKey::xmuxHMaxReusableSecsMin).toString("0"); + c.hMaxReusableSecsMax = json.value(configKey::xmuxHMaxReusableSecsMax).toString("0"); + c.hKeepAlivePeriod = json.value(configKey::xmuxHKeepAlivePeriod).toString(); + return c; +} + +// ═════════════════════════════════════════════════════════════════════════════ +// XrayXhttpConfig +// ═════════════════════════════════════════════════════════════════════════════ + +QJsonObject XrayXhttpConfig::toJson() const +{ + QJsonObject obj; + if (!mode.isEmpty()) obj[configKey::xhttpMode] = mode; + if (!host.isEmpty()) obj[configKey::xhttpHost] = host; + if (!path.isEmpty()) obj[configKey::xhttpPath] = path; + if (!headersTemplate.isEmpty()) obj[configKey::xhttpHeadersTemplate] = headersTemplate; + if (!uplinkMethod.isEmpty()) obj[configKey::xhttpUplinkMethod] = uplinkMethod; + obj[configKey::xhttpDisableGrpc] = disableGrpc; + obj[configKey::xhttpDisableSse] = disableSse; + + if (!sessionPlacement.isEmpty()) obj[configKey::xhttpSessionPlacement] = sessionPlacement; + if (!sessionKey.isEmpty()) obj[configKey::xhttpSessionKey] = sessionKey; + if (!seqPlacement.isEmpty()) obj[configKey::xhttpSeqPlacement] = seqPlacement; + if (!seqKey.isEmpty()) obj[configKey::xhttpSeqKey] = seqKey; + if (!uplinkDataPlacement.isEmpty()) obj[configKey::xhttpUplinkDataPlacement] = uplinkDataPlacement; + if (!uplinkDataKey.isEmpty()) obj[configKey::xhttpUplinkDataKey] = uplinkDataKey; + + if (!uplinkChunkSize.isEmpty()) obj[configKey::xhttpUplinkChunkSize] = uplinkChunkSize; + if (!scMaxBufferedPosts.isEmpty()) obj[configKey::xhttpScMaxBufferedPosts] = scMaxBufferedPosts; + if (!scMaxEachPostBytesMin.isEmpty()) obj[configKey::xhttpScMaxEachPostBytesMin] = scMaxEachPostBytesMin; + if (!scMaxEachPostBytesMax.isEmpty()) obj[configKey::xhttpScMaxEachPostBytesMax] = scMaxEachPostBytesMax; + if (!scMinPostsIntervalMsMin.isEmpty()) obj[configKey::xhttpScMinPostsIntervalMsMin] = scMinPostsIntervalMsMin; + if (!scMinPostsIntervalMsMax.isEmpty()) obj[configKey::xhttpScMinPostsIntervalMsMax] = scMinPostsIntervalMsMax; + if (!scStreamUpServerSecsMin.isEmpty()) obj[configKey::xhttpScStreamUpServerSecsMin] = scStreamUpServerSecsMin; + if (!scStreamUpServerSecsMax.isEmpty()) obj[configKey::xhttpScStreamUpServerSecsMax] = scStreamUpServerSecsMax; + + obj["xPadding"] = xPadding.toJson(); + obj["xmux"] = xmux.toJson(); + + return obj; +} + +XrayXhttpConfig XrayXhttpConfig::fromJson(const QJsonObject &json) +{ + XrayXhttpConfig c; + c.mode = json.value(configKey::xhttpMode).toString("Auto"); + c.host = json.value(configKey::xhttpHost).toString("www.googletagmanager.com"); + c.path = json.value(configKey::xhttpPath).toString(); + c.headersTemplate = json.value(configKey::xhttpHeadersTemplate).toString("HTTP"); + c.uplinkMethod = json.value(configKey::xhttpUplinkMethod).toString("POST"); + c.disableGrpc = json.value(configKey::xhttpDisableGrpc).toBool(true); + c.disableSse = json.value(configKey::xhttpDisableSse).toBool(true); + + c.sessionPlacement = json.value(configKey::xhttpSessionPlacement).toString("Path"); + c.sessionKey = json.value(configKey::xhttpSessionKey).toString("Path"); + c.seqPlacement = json.value(configKey::xhttpSeqPlacement).toString("Path"); + c.seqKey = json.value(configKey::xhttpSeqKey).toString(); + c.uplinkDataPlacement = json.value(configKey::xhttpUplinkDataPlacement).toString("Body"); + c.uplinkDataKey = json.value(configKey::xhttpUplinkDataKey).toString(); + + c.uplinkChunkSize = json.value(configKey::xhttpUplinkChunkSize).toString("0"); + c.scMaxBufferedPosts = json.value(configKey::xhttpScMaxBufferedPosts).toString(); + c.scMaxEachPostBytesMin = json.value(configKey::xhttpScMaxEachPostBytesMin).toString("1"); + c.scMaxEachPostBytesMax = json.value(configKey::xhttpScMaxEachPostBytesMax).toString("100"); + c.scMinPostsIntervalMsMin = json.value(configKey::xhttpScMinPostsIntervalMsMin).toString("100"); + c.scMinPostsIntervalMsMax = json.value(configKey::xhttpScMinPostsIntervalMsMax).toString("800"); + c.scStreamUpServerSecsMin = json.value(configKey::xhttpScStreamUpServerSecsMin).toString("1"); + c.scStreamUpServerSecsMax = json.value(configKey::xhttpScStreamUpServerSecsMax).toString("100"); + + c.xPadding = XrayXPaddingConfig::fromJson(json.value("xPadding").toObject()); + c.xmux = XrayXmuxConfig::fromJson(json.value("xmux").toObject()); + + return c; +} + +// ═════════════════════════════════════════════════════════════════════════════ +// XrayMkcpConfig +// ═════════════════════════════════════════════════════════════════════════════ + +QJsonObject XrayMkcpConfig::toJson() const +{ + QJsonObject obj; + if (!tti.isEmpty()) obj[configKey::mkcpTti] = tti; + if (!uplinkCapacity.isEmpty()) obj[configKey::mkcpUplinkCapacity] = uplinkCapacity; + if (!downlinkCapacity.isEmpty()) obj[configKey::mkcpDownlinkCapacity] = downlinkCapacity; + if (!readBufferSize.isEmpty()) obj[configKey::mkcpReadBufferSize] = readBufferSize; + if (!writeBufferSize.isEmpty()) obj[configKey::mkcpWriteBufferSize] = writeBufferSize; + obj[configKey::mkcpCongestion] = congestion; + return obj; +} + +XrayMkcpConfig XrayMkcpConfig::fromJson(const QJsonObject &json) +{ + XrayMkcpConfig c; + c.tti = json.value(configKey::mkcpTti).toString(); + c.uplinkCapacity = json.value(configKey::mkcpUplinkCapacity).toString(); + c.downlinkCapacity = json.value(configKey::mkcpDownlinkCapacity).toString(); + c.readBufferSize = json.value(configKey::mkcpReadBufferSize).toString(); + c.writeBufferSize = json.value(configKey::mkcpWriteBufferSize).toString(); + c.congestion = json.value(configKey::mkcpCongestion).toBool(true); + return c; +} + +// ═════════════════════════════════════════════════════════════════════════════ +// XrayServerConfig +// ═════════════════════════════════════════════════════════════════════════════ + QJsonObject XrayServerConfig::toJson() const { QJsonObject obj; - - if (!port.isEmpty()) { - obj[configKey::port] = port; - } - if (!transportProto.isEmpty()) { - obj[configKey::transportProto] = transportProto; - } - if (!subnetAddress.isEmpty()) { - obj[configKey::subnetAddress] = subnetAddress; - } - if (!site.isEmpty()) { - obj[configKey::site] = site; - } - - if (isThirdPartyConfig) { - obj[configKey::isThirdPartyConfig] = isThirdPartyConfig; - } - + + // Existing fields + if (!port.isEmpty()) obj[configKey::port] = port; + if (!transportProto.isEmpty()) obj[configKey::transportProto] = transportProto; + if (!subnetAddress.isEmpty()) obj[configKey::subnetAddress] = subnetAddress; + if (!site.isEmpty()) obj[configKey::site] = site; + if (isThirdPartyConfig) obj[configKey::isThirdPartyConfig] = isThirdPartyConfig; + + // New: Security + if (!security.isEmpty()) obj[configKey::xraySecurity] = security; + if (!flow.isEmpty()) obj[configKey::xrayFlow] = flow; + if (!fingerprint.isEmpty()) obj[configKey::xrayFingerprint] = fingerprint; + if (!sni.isEmpty()) obj[configKey::xraySni] = sni; + if (!alpn.isEmpty()) obj[configKey::xrayAlpn] = alpn; + + // New: Transport + if (!transport.isEmpty()) obj[configKey::xrayTransport] = transport; + obj["xhttp"] = xhttp.toJson(); + obj["mkcp"] = mkcp.toJson(); + return obj; } -XrayServerConfig XrayServerConfig::fromJson(const QJsonObject& json) +XrayServerConfig XrayServerConfig::fromJson(const QJsonObject &json) { - XrayServerConfig config; - - config.port = json.value(configKey::port).toString(); - config.transportProto = json.value(configKey::transportProto).toString(); - config.subnetAddress = json.value(configKey::subnetAddress).toString(); - config.site = json.value(configKey::site).toString(); - - config.isThirdPartyConfig = json.value(configKey::isThirdPartyConfig).toBool(false); - - return config; + XrayServerConfig c; + + // Existing fields + c.port = json.value(configKey::port).toString(); + c.transportProto = json.value(configKey::transportProto).toString(); + c.subnetAddress = json.value(configKey::subnetAddress).toString(); + c.site = json.value(configKey::site).toString(); + c.isThirdPartyConfig = json.value(configKey::isThirdPartyConfig).toBool(false); + + // New: Security + c.security = json.value(configKey::xraySecurity).toString("reality"); + c.flow = json.value(configKey::xrayFlow).toString("xtls-rprx-vision"); + c.fingerprint = json.value(configKey::xrayFingerprint).toString("Mozilla/5.0"); + c.sni = json.value(configKey::xraySni).toString("cdn.example.com"); + c.alpn = json.value(configKey::xrayAlpn).toString("HTTP/2"); + + // New: Transport + c.transport = json.value(configKey::xrayTransport).toString("raw"); + c.xhttp = XrayXhttpConfig::fromJson(json.value("xhttp").toObject()); + c.mkcp = XrayMkcpConfig::fromJson(json.value("mkcp").toObject()); + + return c; } -bool XrayServerConfig::hasEqualServerSettings(const XrayServerConfig& other) const +bool XrayServerConfig::hasEqualServerSettings(const XrayServerConfig &other) const { - return port == other.port && site == other.site; + return port == other.port + && site == other.site + && security == other.security + && flow == other.flow + && transport == other.transport + && fingerprint == other.fingerprint + && sni == other.sni; } +// ═════════════════════════════════════════════════════════════════════════════ +// XrayClientConfig (unchanged logic, kept as-is) +// ═════════════════════════════════════════════════════════════════════════════ + QJsonObject XrayClientConfig::toJson() const { QJsonObject obj; - - if (!nativeConfig.isEmpty()) { - obj[configKey::config] = nativeConfig; - } - if (!localPort.isEmpty()) { - obj[configKey::localPort] = localPort; - } - if (!id.isEmpty()) { - obj[configKey::clientId] = id; - } - + if (!nativeConfig.isEmpty()) obj[configKey::config] = nativeConfig; + if (!localPort.isEmpty()) obj[configKey::localPort] = localPort; + if (!id.isEmpty()) obj[configKey::clientId] = id; return obj; } -XrayClientConfig XrayClientConfig::fromJson(const QJsonObject& json) +XrayClientConfig XrayClientConfig::fromJson(const QJsonObject &json) { - XrayClientConfig config; - - config.nativeConfig = json.value(configKey::config).toString(); - config.localPort = json.value(configKey::localPort).toString(); - config.id = json.value(configKey::clientId).toString(); - - if (config.id.isEmpty() && !config.nativeConfig.isEmpty()) { - QJsonDocument doc = QJsonDocument::fromJson(config.nativeConfig.toUtf8()); + XrayClientConfig c; + c.nativeConfig = json.value(configKey::config).toString(); + c.localPort = json.value(configKey::localPort).toString(); + c.id = json.value(configKey::clientId).toString(); + + if (c.id.isEmpty() && !c.nativeConfig.isEmpty()) { + QJsonDocument doc = QJsonDocument::fromJson(c.nativeConfig.toUtf8()); if (!doc.isNull() && doc.isObject()) { QJsonObject configObj = doc.object(); if (configObj.contains(protocols::xray::outbounds)) { @@ -98,10 +286,7 @@ XrayClientConfig XrayClientConfig::fromJson(const QJsonObject& json) if (vnextObj.contains(protocols::xray::users)) { QJsonArray users = vnextObj[protocols::xray::users].toArray(); if (!users.isEmpty()) { - QJsonObject user = users[0].toObject(); - if (user.contains(protocols::xray::id)) { - config.id = user[protocols::xray::id].toString(); - } + c.id = users[0].toObject().value(protocols::xray::id).toString(); } } } @@ -111,16 +296,19 @@ XrayClientConfig XrayClientConfig::fromJson(const QJsonObject& json) } } } - - return config; + + return c; } +// ═════════════════════════════════════════════════════════════════════════════ +// XrayProtocolConfig (unchanged logic, kept as-is) +// ═════════════════════════════════════════════════════════════════════════════ + QJsonObject XrayProtocolConfig::toJson() const { QJsonObject obj = serverConfig.toJson(); - + if (clientConfig.has_value()) { - // Third-party import: nativeConfig is raw Xray JSON (inbounds/outbounds) QJsonDocument doc = QJsonDocument::fromJson(clientConfig->nativeConfig.toUtf8()); if (!doc.isNull() && doc.isObject() && doc.object().contains(protocols::xray::outbounds) && !doc.object().contains(configKey::config)) { @@ -130,42 +318,38 @@ QJsonObject XrayProtocolConfig::toJson() const obj[configKey::lastConfig] = QString::fromUtf8(QJsonDocument(clientJson).toJson(QJsonDocument::Compact)); } } - + return obj; } -XrayProtocolConfig XrayProtocolConfig::fromJson(const QJsonObject& json) +XrayProtocolConfig XrayProtocolConfig::fromJson(const QJsonObject &json) { - XrayProtocolConfig config; - - config.serverConfig = XrayServerConfig::fromJson(json); - + XrayProtocolConfig c; + c.serverConfig = XrayServerConfig::fromJson(json); + QString lastConfigStr = json.value(configKey::lastConfig).toString(); if (!lastConfigStr.isEmpty()) { QJsonDocument doc = QJsonDocument::fromJson(lastConfigStr.toUtf8()); if (doc.isObject()) { QJsonObject parsed = doc.object(); - // Third-party import stores raw Xray config (inbounds/outbounds) directly if (parsed.contains(protocols::xray::outbounds) && !parsed.contains(configKey::config)) { XrayClientConfig clientCfg; clientCfg.nativeConfig = lastConfigStr; if (parsed.contains(protocols::xray::inbounds)) { QJsonArray inbounds = parsed.value(protocols::xray::inbounds).toArray(); if (!inbounds.isEmpty()) { - QJsonObject inbound = inbounds[0].toObject(); - if (inbound.contains(protocols::xray::port)) { - clientCfg.localPort = QString::number(inbound.value(protocols::xray::port).toInt()); - } + clientCfg.localPort = QString::number( + inbounds[0].toObject().value(protocols::xray::port).toInt()); } } - config.clientConfig = clientCfg; + c.clientConfig = clientCfg; } else { - config.clientConfig = XrayClientConfig::fromJson(parsed); + c.clientConfig = XrayClientConfig::fromJson(parsed); } } } - - return config; + + return c; } bool XrayProtocolConfig::hasClientConfig() const @@ -173,7 +357,7 @@ bool XrayProtocolConfig::hasClientConfig() const return clientConfig.has_value(); } -void XrayProtocolConfig::setClientConfig(const XrayClientConfig& config) +void XrayProtocolConfig::setClientConfig(const XrayClientConfig &config) { clientConfig = config; } @@ -184,4 +368,3 @@ void XrayProtocolConfig::clearClientConfig() } } // namespace amnezia - diff --git a/client/core/models/protocols/xrayProtocolConfig.h b/client/core/models/protocols/xrayProtocolConfig.h index fc52a81b3..a4ae2645f 100644 --- a/client/core/models/protocols/xrayProtocolConfig.h +++ b/client/core/models/protocols/xrayProtocolConfig.h @@ -8,41 +8,138 @@ namespace amnezia { +// ── xPadding ───────────────────────────────────────────────────────────────── +struct XrayXPaddingConfig { + QString bytesMin; // xPaddingBytes min + QString bytesMax; // xPaddingBytes max + bool obfsMode = true; // xPaddingObfsMode + QString key; // xPaddingKey + QString header; // xPaddingHeader + QString placement = "Cookie"; // xPaddingPlacement: Cookie|Header|Query|Body + QString method = "Repeat-x"; // xPaddingMethod: Repeat-x|Random|Zero + + QJsonObject toJson() const; + static XrayXPaddingConfig fromJson(const QJsonObject &json); +}; + +// ── xmux ───────────────────────────────────────────────────────────────────── +struct XrayXmuxConfig { + bool enabled = true; + + QString maxConcurrencyMin = "0"; + QString maxConcurrencyMax = "0"; + QString maxConnectionsMin = "0"; + QString maxConnectionsMax = "0"; + QString cMaxReuseTimesMin = "0"; + QString cMaxReuseTimesMax = "0"; + QString hMaxRequestTimesMin = "0"; + QString hMaxRequestTimesMax = "0"; + QString hMaxReusableSecsMin = "0"; + QString hMaxReusableSecsMax = "0"; + QString hKeepAlivePeriod; + + QJsonObject toJson() const; + static XrayXmuxConfig fromJson(const QJsonObject &json); +}; + +// ── XHTTP transport ─────────────────────────────────────────────────────────── +struct XrayXhttpConfig { + QString mode = "Auto"; // Auto|Packet-up|Stream-up|Stream-one + QString host = "www.googletagmanager.com"; + QString path; + QString headersTemplate = "HTTP"; // HTTP|None + QString uplinkMethod = "POST"; // POST|PUT|PATCH + bool disableGrpc = true; + bool disableSse = true; + + // Session & Sequence + QString sessionPlacement = "Path"; // Path|Header|Cookie|None + QString sessionKey = "Path"; + QString seqPlacement = "Path"; + QString seqKey; + QString uplinkDataPlacement = "Body"; // Body|Query + QString uplinkDataKey; + + // Traffic Shaping + QString uplinkChunkSize = "0"; + QString scMaxBufferedPosts; + QString scMaxEachPostBytesMin = "1"; + QString scMaxEachPostBytesMax = "100"; + QString scMinPostsIntervalMsMin = "100"; + QString scMinPostsIntervalMsMax = "800"; + QString scStreamUpServerSecsMin = "1"; + QString scStreamUpServerSecsMax = "100"; + + XrayXPaddingConfig xPadding; + XrayXmuxConfig xmux; + + QJsonObject toJson() const; + static XrayXhttpConfig fromJson(const QJsonObject &json); +}; + +// ── mKCP transport ──────────────────────────────────────────────────────────── +struct XrayMkcpConfig { + QString tti; + QString uplinkCapacity; + QString downlinkCapacity; + QString readBufferSize; + QString writeBufferSize; + bool congestion = true; + + QJsonObject toJson() const; + static XrayMkcpConfig fromJson(const QJsonObject &json); +}; + +// ── Server config (settings editable by user) ───────────────────────────────── struct XrayServerConfig { + // Existing fields QString port; QString transportProto; QString subnetAddress; QString site; - bool isThirdPartyConfig = false; - + bool isThirdPartyConfig = false; + + // New: Security + QString security = "reality"; // none|tls|reality + QString flow = "xtls-rprx-vision"; // ""|xtls-rprx-vision|xtls-rprx-vision-udp443 + QString fingerprint = "Mozilla/5.0"; + QString sni = "cdn.example.com"; + QString alpn = "HTTP/2"; // TLS only: HTTP/2|HTTP/1.1|HTTP/2,HTTP/1.1 + + // New: Transport + QString transport = "raw"; // raw|xhttp|mkcp + XrayXhttpConfig xhttp; + XrayMkcpConfig mkcp; + QJsonObject toJson() const; - static XrayServerConfig fromJson(const QJsonObject& json); - - bool hasEqualServerSettings(const XrayServerConfig& other) const; + static XrayServerConfig fromJson(const QJsonObject &json); + + bool hasEqualServerSettings(const XrayServerConfig &other) const; }; +// ── Client config (generated, not edited by user) ───────────────────────────── struct XrayClientConfig { QString nativeConfig; QString localPort; QString id; - + QJsonObject toJson() const; - static XrayClientConfig fromJson(const QJsonObject& json); + static XrayClientConfig fromJson(const QJsonObject &json); }; +// ── Top-level protocol config ────────────────────────────────────────────────── struct XrayProtocolConfig { - XrayServerConfig serverConfig; + XrayServerConfig serverConfig; std::optional clientConfig; - + QJsonObject toJson() const; - static XrayProtocolConfig fromJson(const QJsonObject& json); - + static XrayProtocolConfig fromJson(const QJsonObject &json); + bool hasClientConfig() const; - void setClientConfig(const XrayClientConfig& config); + void setClientConfig(const XrayClientConfig &config); void clearClientConfig(); }; } // namespace amnezia #endif // XRAYPROTOCOLCONFIG_H - diff --git a/client/core/utils/constants/configKeys.h b/client/core/utils/constants/configKeys.h index 40bc842b1..c6a7818c7 100644 --- a/client/core/utils/constants/configKeys.h +++ b/client/core/utils/constants/configKeys.h @@ -121,6 +121,76 @@ namespace amnezia constexpr QLatin1String latestHandshake("latestHandshake"); constexpr QLatin1String dataReceived("dataReceived"); constexpr QLatin1String dataSent("dataSent"); + + // ── Xray-specific keys ──────────────────────────────────────── + + // Security + constexpr QLatin1String xraySecurity("xray_security"); // none | tls | reality + constexpr QLatin1String xrayFlow("xray_flow"); // "" | xtls-rprx-vision | xtls-rprx-vision-udp443 + constexpr QLatin1String xrayFingerprint("xray_fingerprint"); // Mozilla/5.0 | chrome | firefox | ... + constexpr QLatin1String xraySni("xray_sni"); // Server Name (SNI) + constexpr QLatin1String xrayAlpn("xray_alpn"); // HTTP/2 | HTTP/1.1 | HTTP/2,HTTP/1.1 + + // Transport — common + constexpr QLatin1String xrayTransport("xray_transport"); // raw | xhttp | mkcp + + // Transport — XHTTP + constexpr QLatin1String xhttpMode("xhttp_mode"); // Auto | Packet-up | Stream-up | Stream-one + constexpr QLatin1String xhttpHost("xhttp_host"); + constexpr QLatin1String xhttpPath("xhttp_path"); + constexpr QLatin1String xhttpHeadersTemplate("xhttp_headers_template"); // HTTP | None + constexpr QLatin1String xhttpUplinkMethod("xhttp_uplink_method"); // POST | PUT | PATCH + constexpr QLatin1String xhttpDisableGrpc("xhttp_disable_grpc"); // bool + constexpr QLatin1String xhttpDisableSse("xhttp_disable_sse"); // bool + + // Transport — XHTTP Session & Sequence + constexpr QLatin1String xhttpSessionPlacement("xhttp_session_placement"); // Path | Header | Cookie | None + constexpr QLatin1String xhttpSessionKey("xhttp_session_key"); + constexpr QLatin1String xhttpSeqPlacement("xhttp_seq_placement"); + constexpr QLatin1String xhttpSeqKey("xhttp_seq_key"); + constexpr QLatin1String xhttpUplinkDataPlacement("xhttp_uplink_data_placement"); // Body | Query + constexpr QLatin1String xhttpUplinkDataKey("xhttp_uplink_data_key"); + + // Transport — XHTTP Traffic Shaping + constexpr QLatin1String xhttpUplinkChunkSize("xhttp_uplink_chunk_size"); + constexpr QLatin1String xhttpScMaxBufferedPosts("xhttp_sc_max_buffered_posts"); + constexpr QLatin1String xhttpScMaxEachPostBytesMin("xhttp_sc_max_each_post_bytes_min"); + constexpr QLatin1String xhttpScMaxEachPostBytesMax("xhttp_sc_max_each_post_bytes_max"); + constexpr QLatin1String xhttpScMinPostsIntervalMsMin("xhttp_sc_min_posts_interval_ms_min"); + constexpr QLatin1String xhttpScMinPostsIntervalMsMax("xhttp_sc_min_posts_interval_ms_max"); + constexpr QLatin1String xhttpScStreamUpServerSecsMin("xhttp_sc_stream_up_server_secs_min"); + constexpr QLatin1String xhttpScStreamUpServerSecsMax("xhttp_sc_stream_up_server_secs_max"); + + // Transport — mKCP + constexpr QLatin1String mkcpTti("mkcp_tti"); + constexpr QLatin1String mkcpUplinkCapacity("mkcp_uplink_capacity"); + constexpr QLatin1String mkcpDownlinkCapacity("mkcp_downlink_capacity"); + constexpr QLatin1String mkcpReadBufferSize("mkcp_read_buffer_size"); + constexpr QLatin1String mkcpWriteBufferSize("mkcp_write_buffer_size"); + constexpr QLatin1String mkcpCongestion("mkcp_congestion"); // bool + + // xPadding + constexpr QLatin1String xPaddingBytesMin("xpadding_bytes_min"); + constexpr QLatin1String xPaddingBytesMax("xpadding_bytes_max"); + constexpr QLatin1String xPaddingObfsMode("xpadding_obfs_mode"); // bool + constexpr QLatin1String xPaddingKey("xpadding_key"); + constexpr QLatin1String xPaddingHeader("xpadding_header"); + constexpr QLatin1String xPaddingPlacement("xpadding_placement"); // Cookie | Header | Query | Body + constexpr QLatin1String xPaddingMethod("xpadding_method"); // Repeat-x | Random | Zero + + // xmux + constexpr QLatin1String xmuxEnabled("xmux_enabled"); // bool + constexpr QLatin1String xmuxMaxConcurrencyMin("xmux_max_concurrency_min"); + constexpr QLatin1String xmuxMaxConcurrencyMax("xmux_max_concurrency_max"); + constexpr QLatin1String xmuxMaxConnectionsMin("xmux_max_connections_min"); + constexpr QLatin1String xmuxMaxConnectionsMax("xmux_max_connections_max"); + constexpr QLatin1String xmuxCMaxReuseTimesMin("xmux_c_max_reuse_times_min"); + constexpr QLatin1String xmuxCMaxReuseTimesMax("xmux_c_max_reuse_times_max"); + constexpr QLatin1String xmuxHMaxRequestTimesMin("xmux_h_max_request_times_min"); + constexpr QLatin1String xmuxHMaxRequestTimesMax("xmux_h_max_request_times_max"); + constexpr QLatin1String xmuxHMaxReusableSecsMin("xmux_h_max_reusable_secs_min"); + constexpr QLatin1String xmuxHMaxReusableSecsMax("xmux_h_max_reusable_secs_max"); + constexpr QLatin1String xmuxHKeepAlivePeriod("xmux_h_keep_alive_period"); } }