refactor vpn settings

fix: nekoray_core freedom udp loopback

feat: copy custom config
This commit is contained in:
arm64v8a
2022-09-25 13:12:29 +08:00
parent 913b5c8be6
commit bbcd9df977
24 changed files with 454 additions and 190 deletions

1
.gitignore vendored
View File

@@ -80,3 +80,4 @@ CMakeLists.txt.user*
# Deploy
/deployment
/neko*.sh

View File

@@ -181,6 +181,10 @@ set(PROJECT_SOURCES
ui/dialog_manage_routes.h
ui/dialog_manage_routes.ui
ui/dialog_vpn_settings.cpp
ui/dialog_vpn_settings.h
ui/dialog_vpn_settings.ui
ui/dialog_hotkey.cpp
ui/dialog_hotkey.h
ui/dialog_hotkey.ui

View File

@@ -1,9 +1,11 @@
#include "db/ConfigBuilder.hpp"
#include "db/Database.hpp"
#include "fmt/includes.h"
#include "fmt/Preset.hpp"
#include <QApplication>
#include <QFile>
#include <QFileInfo>
namespace NekoRay {
@@ -694,7 +696,7 @@ namespace NekoRay {
{"outbound", "dns-out"}};
// geopath
auto geopath = NekoRay::dataStore->v2ray_asset_dir;
auto geopath = dataStore->v2ray_asset_dir;
if (geopath.isEmpty()) geopath = QApplication::applicationDirPath();
auto geoip = geopath + "/geoip.db";
auto geosite = geopath + "/geosite.db";
@@ -714,4 +716,58 @@ namespace NekoRay {
return result;
}
QString WriteVPNSingBoxConfig() {
//
QString process_name_rule = dataStore->vpn_bypass_process.trimmed();
if (!process_name_rule.isEmpty()) {
auto arr = SplitLines(process_name_rule);
QJsonObject rule{{"outbound", "direct"},
{"process_name", QList2QJsonArray(arr)}};
process_name_rule = "," + QJsonObject2QString(rule, false);
}
QString cidr_rule = dataStore->vpn_bypass_cidr.trimmed();
if (!cidr_rule.isEmpty()) {
auto arr = SplitLines(cidr_rule);
QJsonObject rule{{"outbound", "direct"},
{"ip_cidr", QList2QJsonArray(arr)}};
cidr_rule = "," + QJsonObject2QString(rule, false);
}
// gen config
auto configFn = ":/nekoray/vpn/sing-box-vpn.json";
if (QFile::exists("vpn/sing-box-vpn.json")) configFn = "vpn/sing-box-vpn.json";
auto config = ReadFileText(configFn)
.replace("%IPV6_ADDRESS%", dataStore->vpn_ipv6 ? R"("inet6_address": "fdfe:dcba:9876::1/126",)" : "")
.replace("%MTU%", Int2String(dataStore->vpn_mtu))
.replace("%STACK%", Preset::SingBox::VpnImplementation.value(dataStore->vpn_implementation))
.replace("%PROCESS_NAME_RULE%", process_name_rule)
.replace("%CIDR_RULE%", cidr_rule)
.replace("%PORT%", Int2String(dataStore->inbound_socks_port));
// write config
QFile file;
file.setFileName(QFileInfo(configFn).fileName());
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
file.write(config.toUtf8());
file.close();
return QFileInfo(file).absoluteFilePath();
}
QString WriteVPNLinuxScript(const QString &protectPath, const QString &configPath) {
// gen script
auto scriptFn = ":/nekoray/vpn/vpn-run-root.sh";
if (QFile::exists("vpn/vpn-run-root.sh")) scriptFn = "vpn/vpn-run-root.sh";
auto script = ReadFileText(scriptFn)
.replace("$PORT", Int2String(dataStore->inbound_socks_port))
.replace("./nekobox_core", QApplication::applicationDirPath() + "/nekobox_core")
.replace("$PROTECT_LISTEN_PATH", protectPath)
.replace("$CONFIG_PATH", configPath)
.replace("$TABLE_FWMARK", "514");
// write script
QFile file2;
file2.setFileName(QFileInfo(scriptFn).fileName());
file2.open(QIODevice::ReadWrite | QIODevice::Truncate);
file2.write(script.toUtf8());
file2.close();
return QFileInfo(file2).absoluteFilePath();
}
}

View File

@@ -51,4 +51,8 @@ namespace NekoRay {
QString BuildChainInternal(int chainId, const QList<QSharedPointer<ProxyEntity>> &ents,
const QSharedPointer<BuildConfigStatus> &status);
QString WriteVPNSingBoxConfig();
QString WriteVPNLinuxScript(const QString &protectPath, const QString &configPath);
}

View File

@@ -22,9 +22,14 @@
{
"type": "block",
"tag": "block"
},
{
"type": "direct",
"tag": "direct"
}
],
"route": {
"auto_detect_interface": true,
"rules": [
{
"network": "udp",
@@ -36,7 +41,23 @@
5353
],
"outbound": "block"
},
{
"ip_cidr": [
"224.0.0.0/4",
"ff00::/8"
],
"outbound": "block"
},
{
"source_ip_cidr": [
"224.0.0.0/4",
"ff00::/8"
],
"outbound": "block"
}
%PROCESS_NAME_RULE%
%CIDR_RULE%
]
}
}

View File

@@ -15,6 +15,7 @@ namespace NekoRay::fmt {
QStringList env;
QStringList arguments;
QString error;
QString config_export;
};
class AbstractBean : public JsonStore {
@@ -38,6 +39,8 @@ namespace NekoRay::fmt {
[[nodiscard]] virtual QString DisplayName();
virtual QString DisplayCoreType() { return software_core_name; };
virtual QString DisplayType() { return {}; };
virtual QString DisplayTypeAndName();

View File

@@ -26,14 +26,17 @@ namespace NekoRay::fmt {
return result;
}
auto _serverAddress = sni.isEmpty() ? serverAddress : sni;
auto is_export = mapping_port == 114514;
auto domain_address = sni.isEmpty() ? serverAddress : sni;
auto connect_address = is_export ? serverAddress : "127.0.0.1";
auto connect_port = is_export ? serverPort : mapping_port;
result.arguments += "--log";
result.arguments += "--listen=socks://127.0.0.1:" + Int2String(socks_port);
result.arguments += "--proxy=" + protocol + "://" +
username + ":" + password + "@" +
_serverAddress + ":" + Int2String(mapping_port);
result.arguments += "--host-resolver-rules=MAP " + _serverAddress + " 127.0.0.1";
result.arguments += "--proxy=" + protocol + "://" + username + ":" + password + "@" +
domain_address + ":" + Int2String(connect_port);
if (domain_address != connect_address)
result.arguments += "--host-resolver-rules=MAP " + domain_address + " " + connect_address;
if (insecure_concurrency > 0) result.arguments += "--insecure-concurrency=" + Int2String(insecure_concurrency);
if (!extra_headers.isEmpty()) result.arguments += "--extra-headers=" + extra_headers;
if (!certificate.isEmpty()) {
@@ -41,6 +44,10 @@ namespace NekoRay::fmt {
result.env += "SSL_CERT_FILE=" + TempFile;
}
auto config_export = QStringList{result.program};
config_export += result.arguments;
result.config_export = QStringList2Command(config_export);
return result;
}
@@ -74,6 +81,8 @@ namespace NekoRay::fmt {
for (int i = 0; i < result.arguments.count(); i++) {
result.arguments[i] = result.arguments[i].replace("%config%", TempFile);
}
result.config_export = config;
}
return result;

View File

@@ -19,6 +19,8 @@ namespace NekoRay::fmt {
QString DisplayType() override { return core; };
QString DisplayCoreType() override { return NeedExternal() ? core : software_core_name; };
bool NeedExternal() override {
if (IS_NEKO_BOX && core == "hysteria") return false;
return true;

View File

@@ -23,6 +23,8 @@ namespace NekoRay::fmt {
_add(new configItem("insecure_concurrency", &insecure_concurrency, itemType::integer));
};
QString DisplayCoreType() override { return "Naive"; };
QString DisplayType() override { return "Naive"; };
bool NeedExternal() override { return true; };

View File

@@ -71,7 +71,7 @@ func updateRoutes() {
func getBindInterfaceIndex(address string) uint32 {
host, _, _ := net.SplitHostPort(address)
if !net.ParseIP(host).IsGlobalUnicast() {
if net.ParseIP(host).IsLoopback() {
return 0
}

View File

@@ -54,6 +54,7 @@ inline QMenu *CreateMenu(QWidget *parent, const QList<QString> &texts, const std
#define P_SAVE_STRING_QTEXTEDIT(a) bean->a = ui->a->toPlainText();
#define D_LOAD_STRING(a) ui->a->setText(NekoRay::dataStore->a);
#define D_SAVE_STRING(a) NekoRay::dataStore->a = ui->a->text();
#define D_SAVE_STRING_QTEXTEDIT(a) NekoRay::dataStore->a = ui->a->toPlainText();
#define P_C_LOAD_STRING(a) CACHE.a = bean->a;
#define P_C_SAVE_STRING(a) bean->a = CACHE.a;
#define D_C_LOAD_STRING(a) CACHE.a = NekoRay::dataStore->a;

View File

@@ -53,6 +53,8 @@ namespace NekoRay {
_add(new configItem("vpn_mtu", &vpn_mtu, itemType::integer));
_add(new configItem("vpn_ipv6", &vpn_ipv6, itemType::boolean));
_add(new configItem("vpn_hide_console", &vpn_hide_consloe, itemType::boolean));
_add(new configItem("vpn_bypass_process", &vpn_bypass_process, itemType::string));
_add(new configItem("vpn_bypass_cidr", &vpn_bypass_cidr, itemType::string));
_add(new configItem("check_include_pre", &check_include_pre, itemType::boolean));
_add(new configItem("neko_core", &neko_core, itemType::integer));
}

View File

@@ -109,6 +109,8 @@ namespace NekoRay {
int vpn_mtu = 9000;
bool vpn_ipv6 = false;
bool vpn_hide_consloe = false;
QString vpn_bypass_process = "";
QString vpn_bypass_cidr = "";
// Hotkey
QString hotkey_mainwindow = "";

View File

@@ -35,6 +35,15 @@ inline QString SubStrAfter(QString str, const QString &sub) {
return str.right(str.length() - str.indexOf(sub) - sub.length());
}
inline QString QStringList2Command(const QStringList &list) {
QStringList new_list;
for (auto str: list) {
auto q = "\"" + str.replace("\"", "\\\"") + "\"";
new_list << q;
}
return new_list.join(" ");
}
inline QString
DecodeB64IfValid(const QString &input, QByteArray::Base64Option options = QByteArray::Base64Option::Base64Encoding) {
auto result = QByteArray::fromBase64Encoding(input.toUtf8(),

View File

@@ -486,17 +486,32 @@
<source>Custom (global)</source>
<translation> ()</translation>
</message>
</context>
<context>
<name>DialogVPNSettings</name>
<message>
<source>VPN Settings</source>
<translation>VPN </translation>
</message>
<message>
<source>VPN Implementation</source>
<translation>VPN </translation>
</message>
<message>
<source>Hide Console</source>
<translation></translation>
</message>
<message>
<source>VPN Enable IPv6</source>
<translation> VPN IPv6</translation>
</message>
<message>
<source>Hide Console</source>
<translation></translation>
<source>Bypass CIDR</source>
<translation> CIDR</translation>
</message>
<message>
<source>Bypass Process Name</source>
<translation></translation>
</message>
</context>
<context>
@@ -819,10 +834,6 @@
<source>Stop</source>
<translation></translation>
</message>
<message>
<source>Routing VPN Settings</source>
<translation> VPN </translation>
</message>
<message>
<source>Add profile from clipboard</source>
<translation></translation>
@@ -1029,10 +1040,6 @@ End: %2</source>
<source>QR Code and link</source>
<translation> QR Code </translation>
</message>
<message>
<source>Export V2ray config</source>
<translation type="vanished"> V2ray </translation>
</message>
<message>
<source>Active Routing</source>
<translation></translation>
@@ -1141,6 +1148,14 @@ End: %2</source>
<source>Export %1 config</source>
<translation> %1 </translation>
</message>
<message>
<source>Routing Settings</source>
<translation></translation>
</message>
<message>
<source>VPN Settings</source>
<translation>VPN </translation>
</message>
</context>
<context>
<name>ProxyItem</name>

View File

@@ -3,7 +3,6 @@
#include "qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp"
#include "qv2ray/v3/components/GeositeReader/GeositeReader.hpp"
#include "fmt/Preset.hpp"
#include "main/GuiUtils.hpp"
#include <QFile>
@@ -31,11 +30,9 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) :
title_base = windowTitle();
if (IS_NEKO_BOX) {
ui->fake_dns->setVisible(false);
ui->enhance_resolve_server_domain->setVisible(false);
ui->domain_v2ray->setVisible(false);
} else {
ui->fake_dns->setVisible(true);
ui->enhance_resolve_server_domain->setVisible(true);
ui->domain_v2ray->setVisible(true);
}
@@ -44,20 +41,11 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) :
ui->outbound_domain_strategy->setCurrentText(NekoRay::dataStore->outbound_domain_strategy);
ui->domainMatcherCombo->setCurrentIndex(NekoRay::dataStore->domain_matcher);
ui->domainStrategyCombo->setCurrentText(NekoRay::dataStore->domain_strategy);
ui->fake_dns->setChecked(NekoRay::dataStore->fake_dns);
ui->dns_routing->setChecked(NekoRay::dataStore->dns_routing);
ui->dns_remote->setText(NekoRay::dataStore->remote_dns);
ui->dns_direct->setText(NekoRay::dataStore->direct_dns);
ui->enhance_resolve_server_domain->setChecked(NekoRay::dataStore->enhance_resolve_server_domain);
D_C_LOAD_STRING(custom_route_global)
//
ui->vpn_implementation->setCurrentIndex(NekoRay::dataStore->vpn_implementation);
ui->vpn_mtu->setCurrentText(Int2String(NekoRay::dataStore->vpn_mtu));
ui->vpn_ipv6->setChecked(NekoRay::dataStore->vpn_ipv6);
ui->hide_console->setChecked(NekoRay::dataStore->vpn_hide_consloe);
#ifndef Q_OS_WIN
ui->hide_console->setVisible(false);
#endif
//
connect(ui->custom_route_edit, &QPushButton::clicked, this, [=] {
C_EDIT_JSON_ALLOW_EMPTY(custom_route)
@@ -113,25 +101,6 @@ void DialogManageRoutes::accept() {
NekoRay::dataStore->direct_dns = ui->dns_direct->text();
NekoRay::dataStore->enhance_resolve_server_domain = ui->enhance_resolve_server_domain->isChecked();
D_C_SAVE_STRING(custom_route_global)
//
bool vpnChanged = false;
auto fakedns = ui->fake_dns->isChecked();
auto mtu = ui->vpn_mtu->currentText().toInt();
if (mtu > 10000 || mtu < 1000) mtu = 9000;
auto ipv6 = ui->vpn_ipv6->isChecked();
//
auto impl = ui->vpn_implementation->currentIndex();
vpnChanged |= NekoRay::dataStore->vpn_implementation != impl;
NekoRay::dataStore->vpn_implementation = impl;
//
vpnChanged |= NekoRay::dataStore->fake_dns != fakedns;
vpnChanged |= NekoRay::dataStore->vpn_mtu != mtu;
vpnChanged |= NekoRay::dataStore->vpn_ipv6 != ipv6;
NekoRay::dataStore->fake_dns = fakedns;
NekoRay::dataStore->vpn_mtu = mtu;
NekoRay::dataStore->vpn_ipv6 = ipv6;
NekoRay::dataStore->vpn_hide_consloe = ui->hide_console->isChecked();
//
bool routeChanged = false;
if (NekoRay::dataStore->active_routing != active_routing) routeChanged = true;
SAVE_TO_ROUTING(NekoRay::dataStore->routing)
@@ -141,7 +110,6 @@ void DialogManageRoutes::accept() {
//
QString info = "UpdateDataStore";
if (routeChanged) info += "RouteChanged";
if (vpnChanged) info += "VPNChanged";
dialog_message(Dialog_DialogManageRoutes, info);
QDialog::accept();
}

View File

@@ -26,7 +26,6 @@ private:
QString custom_route_global;
} CACHE;
QMenu *builtInSchemesMenu;
Qv2ray::ui::widgets::AutoCompleteTextEdit *directDomainTxt;
Qv2ray::ui::widgets::AutoCompleteTextEdit *proxyDomainTxt;
@@ -41,6 +40,7 @@ private:
//
QString title_base;
QString active_routing;
public slots:
void accept() override;

View File

@@ -111,106 +111,6 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>VPN Implementation</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="vpn_implementation">
<item>
<property name="text">
<string notr="true">gVisor</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">System</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">MTU</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="vpn_mtu">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string notr="true">9000</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">1500</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QCheckBox" name="hide_console">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Hide Console</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="vpn_ipv6">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>VPN Enable IPv6</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="fake_dns">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">FakeDNS</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
@@ -470,9 +370,6 @@
<tabstops>
<tabstop>sniffing_mode</tabstop>
<tabstop>outbound_domain_strategy</tabstop>
<tabstop>vpn_implementation</tabstop>
<tabstop>vpn_mtu</tabstop>
<tabstop>fake_dns</tabstop>
<tabstop>dns_remote</tabstop>
<tabstop>dns_direct</tabstop>
<tabstop>enhance_resolve_server_domain</tabstop>

View File

@@ -0,0 +1,45 @@
#include "dialog_vpn_settings.h"
#include "ui_dialog_vpn_settings.h"
#include "main/GuiUtils.hpp"
#include "main/NekoRay.hpp"
DialogVPNSettings::DialogVPNSettings(QWidget *parent) :
QDialog(parent), ui(new Ui::DialogVPNSettings) {
ui->setupUi(this);
ui->fake_dns->setVisible(!IS_NEKO_BOX);
ui->fake_dns->setChecked(NekoRay::dataStore->fake_dns);
//
ui->vpn_implementation->setCurrentIndex(NekoRay::dataStore->vpn_implementation);
ui->vpn_mtu->setCurrentText(Int2String(NekoRay::dataStore->vpn_mtu));
ui->vpn_ipv6->setChecked(NekoRay::dataStore->vpn_ipv6);
ui->hide_console->setChecked(NekoRay::dataStore->vpn_hide_consloe);
#ifndef Q_OS_WIN
ui->hide_console->setVisible(false);
#endif
//
D_LOAD_STRING(vpn_bypass_cidr)
D_LOAD_STRING(vpn_bypass_process)
}
DialogVPNSettings::~DialogVPNSettings() {
delete ui;
}
void DialogVPNSettings::accept() {
//
auto mtu = ui->vpn_mtu->currentText().toInt();
if (mtu > 10000 || mtu < 1000) mtu = 9000;
NekoRay::dataStore->vpn_implementation = ui->vpn_implementation->currentIndex();
NekoRay::dataStore->fake_dns = ui->fake_dns->isChecked();
NekoRay::dataStore->vpn_mtu = mtu;
NekoRay::dataStore->vpn_ipv6 = ui->vpn_ipv6->isChecked();
NekoRay::dataStore->vpn_hide_consloe = ui->hide_console->isChecked();
//
D_SAVE_STRING_QTEXTEDIT(vpn_bypass_cidr)
D_SAVE_STRING_QTEXTEDIT(vpn_bypass_process)
//
dialog_message("", "UpdateDataStore,VPNChanged");
QDialog::accept();
}

29
ui/dialog_vpn_settings.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef NEKORAY_DIALOG_VPN_SETTINGS_H
#define NEKORAY_DIALOG_VPN_SETTINGS_H
#include <QDialog>
QT_BEGIN_NAMESPACE
namespace Ui { class DialogVPNSettings; }
QT_END_NAMESPACE
class DialogVPNSettings : public QDialog {
Q_OBJECT
public:
explicit DialogVPNSettings(QWidget *parent = nullptr);
~DialogVPNSettings() override;
private:
Ui::DialogVPNSettings *ui;
public slots:
void accept() override;
};
#endif //NEKORAY_DIALOG_VPN_SETTINGS_H

194
ui/dialog_vpn_settings.ui Normal file
View File

@@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DialogVPNSettings</class>
<widget class="QDialog" name="DialogVPNSettings">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>600</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>VPN Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Maximum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_4">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>VPN Implementation</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="vpn_implementation">
<item>
<property name="text">
<string notr="true">gVisor</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">System</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string notr="true">MTU</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="vpn_mtu">
<property name="editable">
<bool>true</bool>
</property>
<item>
<property name="text">
<string notr="true">9000</string>
</property>
</item>
<item>
<property name="text">
<string notr="true">1500</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="horizontalGroupBox_2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="hide_console">
<property name="text">
<string>Hide Console</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="vpn_ipv6">
<property name="text">
<string>VPN Enable IPv6</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="fake_dns">
<property name="text">
<string notr="true">FakeDNS</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QGroupBox" name="verticalGroupBox">
<property name="title">
<string>Bypass CIDR</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTextEdit" name="vpn_bypass_cidr"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="verticalGroupBox_2">
<property name="title">
<string>Bypass Process Name</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTextEdit" name="vpn_bypass_process"/>
</item>
</layout>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>vpn_implementation</tabstop>
<tabstop>vpn_mtu</tabstop>
<tabstop>hide_console</tabstop>
<tabstop>vpn_ipv6</tabstop>
<tabstop>fake_dns</tabstop>
<tabstop>vpn_bypass_cidr</tabstop>
<tabstop>vpn_bypass_process</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>DialogVPNSettings</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>399</x>
<y>575</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>DialogVPNSettings</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>399</x>
<y>575</y>
</hint>
<hint type="destinationlabel">
<x>399</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -1,7 +1,6 @@
#include "./ui_mainwindow.h"
#include "mainwindow.h"
#include "fmt/Preset.hpp"
#include "db/ProfileFilter.hpp"
#include "db/ConfigBuilder.hpp"
#include "sub/GroupUpdater.hpp"
@@ -14,6 +13,7 @@
#include "ui/dialog_basic_settings.h"
#include "ui/dialog_manage_groups.h"
#include "ui/dialog_manage_routes.h"
#include "ui/dialog_vpn_settings.h"
#include "ui/dialog_hotkey.h"
#include "3rdparty/qrcodegen.hpp"
@@ -97,7 +97,6 @@ MainWindow::MainWindow(QWidget *parent)
if (IS_NEKO_BOX) {
software_name = "NekoBox";
software_core_name = "sing-box";
ui->menu_export_config->setText(ui->menu_export_config->text().arg(software_core_name));
}
// top bar
@@ -342,6 +341,16 @@ MainWindow::MainWindow(QWidget *parent)
connect(ui->menu_tcp_ping, &QAction::triggered, this, [=]() { speedtest_current_group(0); });
connect(ui->menu_url_test, &QAction::triggered, this, [=]() { speedtest_current_group(1); });
connect(ui->menu_full_test, &QAction::triggered, this, [=]() { speedtest_current_group(2); });
//
connect(ui->menu_share_item, &QMenu::aboutToShow, this, [=] {
auto name = software_core_name;
auto selected = get_now_selected();
if (!selected.isEmpty()) {
auto ent = selected.first();
name = ent->bean->DisplayCoreType();
}
ui->menu_export_config->setText(tr("Export %1 config").arg(name));
});
refresh_status();
// Prepare core
@@ -499,6 +508,10 @@ void MainWindow::on_menu_routing_settings_triggered() {
USE_DIALOG(DialogManageRoutes)
}
void MainWindow::on_menu_vpn_settings_triggered() {
USE_DIALOG(DialogVPNSettings)
}
void MainWindow::on_menu_hotkey_settings_triggered() {
USE_DIALOG(DialogHotkey)
}
@@ -987,8 +1000,16 @@ void MainWindow::on_menu_export_config_triggered() {
auto ents = get_now_selected();
if (ents.count() != 1) return;
auto ent = ents.first();
QString config_core;
if (ent->bean->NeedExternal()) {
auto result = ent->bean->BuildExternal(114514, MkPort());
config_core = result.config_export.replace("127.0.0.1:114514", ent->bean->DisplayAddress());
} else {
auto result = NekoRay::BuildConfig(ent, false);
auto config_core = QJsonObject2QString(result->coreConfig, true);
config_core = QJsonObject2QString(result->coreConfig, true);
}
QApplication::clipboard()->setText(config_core);
MessageBoxWarning(tr("Config copied"), config_core);
}
@@ -1406,39 +1427,10 @@ bool MainWindow::StartVPNProcess() {
if (vpn_pid != 0) {
return true;
}
// gen config
auto configFn = ":/nekoray/vpn/sing-box-vpn.json";
if (QFile::exists("vpn/sing-box-vpn.json")) configFn = "vpn/sing-box-vpn.json";
auto config = ReadFileText(configFn)
.replace("%IPV6_ADDRESS%",
NekoRay::dataStore->vpn_ipv6 ? R"("inet6_address": "fdfe:dcba:9876::1/126",)" : "")
.replace("%MTU%", Int2String(NekoRay::dataStore->vpn_mtu))
.replace("%STACK%", Preset::SingBox::VpnImplementation.value(NekoRay::dataStore->vpn_implementation))
.replace("%PORT%", Int2String(NekoRay::dataStore->inbound_socks_port));
// write config
QFile file;
file.setFileName(QFileInfo(configFn).fileName());
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
file.write(config.toUtf8());
file.close();
auto configPath = QFileInfo(file).absoluteFilePath();
// gen script
//
auto protectPath = QDir::currentPath() + "/protect";
auto scriptFn = ":/nekoray/vpn/vpn-run-root.sh";
if (QFile::exists("vpn/vpn-run-root.sh")) scriptFn = "vpn/vpn-run-root.sh";
auto script = ReadFileText(scriptFn)
.replace("$PORT", Int2String(NekoRay::dataStore->inbound_socks_port))
.replace("./nekobox_core", QApplication::applicationDirPath() + "/nekobox_core")
.replace("$PROTECT_LISTEN_PATH", protectPath)
.replace("$CONFIG_PATH", configPath)
.replace("$TABLE_FWMARK", "514");
// write script
QFile file2;
file2.setFileName(QFileInfo(scriptFn).fileName());
file2.open(QIODevice::ReadWrite | QIODevice::Truncate);
file2.write(script.toUtf8());
file2.close();
auto scriptPath = QFileInfo(file2).absoluteFilePath();
auto configPath = NekoRay::WriteVPNSingBoxConfig();
auto scriptPath = NekoRay::WriteVPNLinuxScript(protectPath, configPath);
//
#ifdef Q_OS_WIN
runOnNewThread([=] {

View File

@@ -70,6 +70,8 @@ private slots:
void on_menu_routing_settings_triggered();
void on_menu_vpn_settings_triggered();
void on_menu_hotkey_settings_triggered();
void on_menu_add_from_input_triggered();

View File

@@ -498,6 +498,7 @@
<addaction name="menu_manage_groups"/>
<addaction name="menu_basic_settings"/>
<addaction name="menu_routing_settings"/>
<addaction name="menu_vpn_settings"/>
<addaction name="menu_hotkey_settings"/>
</widget>
<widget class="QMenu" name="menu_server">
@@ -590,7 +591,7 @@
</action>
<action name="menu_routing_settings">
<property name="text">
<string>Routing VPN Settings</string>
<string>Routing Settings</string>
</property>
</action>
<action name="menu_add_from_clipboard">
@@ -822,6 +823,11 @@
<string>Resolve domain</string>
</property>
</action>
<action name="menu_vpn_settings">
<property name="text">
<string>VPN Settings</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>