diff --git a/src/constants.h b/src/constants.h index 1fc5c44..64dc76e 100644 --- a/src/constants.h +++ b/src/constants.h @@ -6,7 +6,7 @@ static const QString APP_NAME = "V2Ray-Desktop"; static const int APP_VERSION_MAJOR = 2; static const int APP_VERSION_MINOR = 1; -static const int APP_VERSION_PATCH = 4; +static const int APP_VERSION_PATCH = 5; static const QString APP_RELEASES_URL = "https://api.github.com/repos/Dr-Incognito/V2Ray-Desktop/releases"; diff --git a/src/serverconfighelper.cpp b/src/serverconfighelper.cpp index 7098d82..7a7caa5 100644 --- a/src/serverconfighelper.cpp +++ b/src/serverconfighelper.cpp @@ -102,6 +102,8 @@ QStringList ServerConfigHelper::getV2RayServerConfigErrors( Utility::getStringConfigError(serverConfig, "network", tr("Network"))); errors.append(Utility::getStringConfigError(serverConfig, "networkSecurity", tr("Network Security"))); + errors.append(Utility::getStringConfigError(serverConfig, "tcpHeaderType", + tr("TCP Header"))); errors.append(getV2RayStreamSettingsErrors( serverConfig, serverConfig["network"].toString())); @@ -146,16 +148,57 @@ QJsonObject ServerConfigHelper::getPrettyV2RayConfig( {"tls", serverConfig["networkSecurity"].toString().toLower() == "tls"}, {"skip-cert-verify", serverConfig["allowInsecure"].toBool()}}; - QString network = serverConfig["network"].toString(); + QString network = serverConfig["network"].toString(); + QString tcpHeader = serverConfig["tcpHeaderType"].toString(); + qDebug() << network << tcpHeader; if (network == "ws") { v2RayConfig["network"] = "ws"; v2RayConfig["ws-path"] = serverConfig["networkPath"].toString(); v2RayConfig["ws-headers"] = QJsonObject{{"Host", serverConfig["networkHost"].toString()}}; + } else if (network == "tcp" && tcpHeader == "http") { + v2RayConfig["network"] = "http"; + v2RayConfig["http-opts"] = QJsonObject{ + {"method", "GET"}, + {"path", "/"}, + {"headers", + QJsonObject{ + {"host", + QJsonArray{"www.baidu.com", "www.bing.com", "www.163.com", + "www.netease.com", "www.qq.com", "www.tencent.com", + "www.taobao.com", "www.tmall.com", "www.alibaba-inc.com", + "www.aliyun.com", "www.sensetime.com", "www.megvii.com"}}, + {"User-Agent", getRandomUserAgents(24)}, + {"Accept-Encoding", QJsonArray{"gzip, deflate"}}, + {"Connection", QJsonArray{"keep-alive"}}, + {"Pragma", "no-cache"}, + }}, + }; } + qDebug() << v2RayConfig; return v2RayConfig; } +QJsonArray ServerConfigHelper::getRandomUserAgents(int n) { + QStringList OPERATING_SYSTEMS{"Macintosh; Intel Mac OS X 10_15", + "X11; Linux x86_64", + "Windows NT 10.0; Win64; x64"}; + QJsonArray userAgents; + for (int i = 0; i < n; ++i) { + int osIndex = std::rand() % 3; + int chromeMajorVersion = std::rand() % 30 + 50; + int chromeBuildVersion = std::rand() % 4000 + 1000; + int chromePatchVersion = std::rand() % 100; + userAgents.append(QString("Mozilla/5.0 (%1) AppleWebKit/537.36 (KHTML, " + "like Gecko) Chrome/%2.0.%3.%4 Safari/537.36") + .arg(OPERATING_SYSTEMS[osIndex], + QString::number(chromeMajorVersion), + QString::number(chromeBuildVersion), + QString::number(chromePatchVersion))); + } + return userAgents; +} + QJsonObject ServerConfigHelper::getV2RayServerConfigFromUrl( const QString& server, const QString& subscriptionUrl) { // Ref: @@ -200,6 +243,9 @@ QJsonObject ServerConfigHelper::getV2RayServerConfigFromUrl( {"networkPath", rawServerConfig.contains("path") ? rawServerConfig["path"].toString() : ""}, + {"tcpHeaderType", rawServerConfig.contains("type") + ? rawServerConfig["type"].toString() + : ""}, {"networkSecurity", rawServerConfig.contains("tls") ? "tls" : "none"}}; return serverConfig; diff --git a/src/serverconfighelper.h b/src/serverconfighelper.h index a633214..b4e7962 100644 --- a/src/serverconfighelper.h +++ b/src/serverconfighelper.h @@ -36,6 +36,7 @@ class ServerConfigHelper : public QObject { static QJsonObject getV2RayStreamSettingsFromConfig( const QJsonObject &transport, const QJsonObject &streamSettings); static QJsonObject getPrettyV2RayConfig(const QJsonObject &serverConfig); + static QJsonArray getRandomUserAgents(int n); static QJsonObject getV2RayServerConfigFromUrl( const QString &server, const QString &subscriptionUrl); static QStringList getShadowsocksServerConfigErrors( diff --git a/src/ui/servers.qml b/src/ui/servers.qml index c81a4b8..281078a 100644 --- a/src/ui/servers.qml +++ b/src/ui/servers.qml @@ -492,6 +492,42 @@ ColumnLayout { leftPadding: -3 } + Label { + text: qsTr("Network Security") + color: "white" + } + + ComboBox { + id: comboV2RayNetworkSecurity + Layout.fillWidth: true + textRole: "text" + valueRole: "value" + model: ListModel{ + ListElement { text: "None"; value: "none" } + ListElement { text: "TLS"; value: "tls" } + } + background: Rectangle { + color: Qt.rgba(255, 255, 255, .1) + border.color: Qt.rgba(120, 130, 140, .2) + } + contentItem: Text { + text: comboV2RayNetworkSecurity.displayText + color: "white" + leftPadding: 10 + verticalAlignment: Text.AlignVCenter + } + } + + Label { + text: qsTr("Allow Insecure") + color: "white" + } + + CheckBox { + id: checkboxV2RayAllowInsecure + leftPadding: -2 + } + Label { text: qsTr("Network") color: "white" @@ -518,12 +554,17 @@ ColumnLayout { verticalAlignment: Text.AlignVCenter } onCurrentTextChanged: function() { + labelV2RayTcpHeaderType.visible = false + comboV2RayTcpHeaderType.visible = false labelV2RayNetworkHost.visible = false textV2RayNetworktHost.visible = false labelV2RayNetworkPath.visible = false textV2RayNetworkPath.visible = false - if ( comboV2RayNetwork.currentText === "Websocket" ) { + if ( comboV2RayNetwork.currentText === "TCP" ) { + labelV2RayTcpHeaderType.visible = true + comboV2RayTcpHeaderType.visible = true + } else if ( comboV2RayNetwork.currentText === "Websocket" ) { labelV2RayNetworkHost.visible = true textV2RayNetworktHost.visible = true labelV2RayNetworkPath.visible = true @@ -533,41 +574,33 @@ ColumnLayout { } Label { - text: qsTr("Network Security") + id: labelV2RayTcpHeaderType + text: qsTr("TCP Header") color: "white" } ComboBox { - id: comboV2RayNetworkSecurity + id: comboV2RayTcpHeaderType + Layout.columnSpan: 3 Layout.fillWidth: true textRole: "text" valueRole: "value" model: ListModel{ ListElement { text: "None"; value: "none" } - ListElement { text: "TLS"; value: "tls" } + ListElement { text: "HTTP"; value: "http" } } background: Rectangle { color: Qt.rgba(255, 255, 255, .1) border.color: Qt.rgba(120, 130, 140, .2) } contentItem: Text { - text: comboV2RayNetworkSecurity.displayText + text: comboV2RayTcpHeaderType.displayText color: "white" leftPadding: 10 verticalAlignment: Text.AlignVCenter } } - Label { - text: qsTr("Allow Insecure") - color: "white" - } - - CheckBox { - id: checkboxV2RayAllowInsecure - leftPadding: -2 - } - Label { id: labelV2RayNetworkHost text: qsTr("Host") @@ -625,6 +658,7 @@ ColumnLayout { "security": comboV2RaySecurity.currentText, "udp": checkboxV2RayEnableUdp.checked, "network": comboV2RayNetwork.currentValue, + "tcpHeaderType": comboV2RayTcpHeaderType.currentValue, "networkSecurity": comboV2RayNetworkSecurity.currentText, "allowInsecure": checkboxV2RayAllowInsecure.checked, "networkHost": textV2RayNetworktHost.text, @@ -1207,7 +1241,7 @@ ColumnLayout { radius: 4 } onClicked: function() { - menuSyncServers.enabled = true + menuItemSyncServers.enabled = true buttonSyncServers.enabled = false buttonSyncServers.text = qsTr("Please wait ...") AppProxy.updateSubscriptionServers(menuSubscription.currentSubscription) @@ -1509,7 +1543,12 @@ ColumnLayout { comboV2RayNetwork.currentIndex = comboV2RayNetwork.indexOfValue(server["network"]) comboV2RayNetworkSecurity.currentIndex = server["tls"] ? 1 : 0 checkboxV2RayAllowInsecure.checked = server["skip-cert-verify"] - if (server["network"] === "ws") { + if (server["network"] === "tcp") { + comboV2RayTcpHeaderType.currentIndex = 0 + } else if (server["network"] === "http") { + comboV2RayNetwork.currentIndex = comboV2RayNetwork.indexOfValue("tcp") + comboV2RayTcpHeaderType.currentIndex = comboV2RayTcpHeaderType.indexOfValue("http") + } else if (server["network"] === "ws") { textV2RayNetworktHost.text = server["ws-headers"]["Host"] textV2RayNetworkPath.text = server["ws-path"] } @@ -1563,6 +1602,7 @@ ColumnLayout { comboV2RayNetwork.currentIndex = 0 comboV2RayNetworkSecurity.currentIndex = 0 checkboxV2RayAllowInsecure.checked = false + comboV2RayTcpHeaderType.currentIndex = 0 textV2RayNetworktHost.text = "" textV2RayNetworkPath.text = "" // Clear text fields for Shadowsocks