mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-17 12:34:37 +03:00
feat: direct external
This commit is contained in:
@@ -325,6 +325,7 @@ namespace NekoRay {
|
||||
bool muxApplied = false;
|
||||
|
||||
QString pastTag;
|
||||
int pastExternalStat = 0;
|
||||
int index = 0;
|
||||
|
||||
for (const auto &ent: ents) {
|
||||
@@ -338,13 +339,15 @@ namespace NekoRay {
|
||||
bool needGlobal = false;
|
||||
|
||||
// first profile set as global
|
||||
if (index == ents.length() - 1) {
|
||||
auto isFirstProfile = index == ents.length() - 1;
|
||||
if (isFirstProfile) {
|
||||
needGlobal = true;
|
||||
tagOut = "g-" + Int2String(ent->id);
|
||||
}
|
||||
|
||||
// last profile set as "proxy"
|
||||
if (chainId == 0 && index == 0) {
|
||||
needGlobal = false;
|
||||
tagOut = "proxy";
|
||||
}
|
||||
|
||||
@@ -357,7 +360,7 @@ namespace NekoRay {
|
||||
|
||||
if (index > 0) {
|
||||
// chain rules: past
|
||||
if (!ents[index - 1]->bean->NeedExternal()) {
|
||||
if (pastExternalStat == 0) {
|
||||
auto replaced = status->outbounds.last().toObject();
|
||||
if (IS_NEKO_BOX) {
|
||||
replaced["detour"] = tagOut;
|
||||
@@ -385,7 +388,11 @@ namespace NekoRay {
|
||||
|
||||
// chain rules: this
|
||||
auto mapping_port = MkPort();
|
||||
if (ent->bean->NeedExternal()) {
|
||||
auto thisExternalStat = ent->bean->NeedExternal(isFirstProfile,
|
||||
dataStore->running_spmode == SystemProxyMode::VPN);
|
||||
if (thisExternalStat == 2) dataStore->need_keep_vpn_off = true;
|
||||
if (thisExternalStat == 1) {
|
||||
// mapping
|
||||
if (IS_NEKO_BOX) {
|
||||
status->inbounds += QJsonObject{{"type", "direct"},
|
||||
{"tag", tagOut + "-mapping"},
|
||||
@@ -404,7 +411,7 @@ namespace NekoRay {
|
||||
{"network", "tcp,udp"},}},};
|
||||
}
|
||||
// no chain rule and not outbound, so need to set to direct
|
||||
if (index == ents.length() - 1) {
|
||||
if (isFirstProfile) {
|
||||
if (IS_NEKO_BOX) {
|
||||
status->routingRules += QJsonObject{{"inbound", QJsonArray{tagOut + "-mapping"}},
|
||||
{"outbound", "direct"},};
|
||||
@@ -422,9 +429,9 @@ namespace NekoRay {
|
||||
fmt::CoreObjOutboundBuildResult coreR;
|
||||
fmt::ExternalBuildResult extR;
|
||||
|
||||
if (ent->bean->NeedExternal()) {
|
||||
if (thisExternalStat > 0) {
|
||||
auto ext_socks_port = MkPort();
|
||||
extR = ent->bean->BuildExternal(mapping_port, ext_socks_port);
|
||||
extR = ent->bean->BuildExternal(mapping_port, ext_socks_port, thisExternalStat);
|
||||
if (extR.program.isEmpty()) {
|
||||
status->result->error = QObject::tr("Core not found: %1").arg(ent->bean->DisplayType());
|
||||
return {};
|
||||
@@ -512,7 +519,7 @@ namespace NekoRay {
|
||||
}
|
||||
|
||||
// Bypass Lookup for the first profile
|
||||
if (index == ents.length() - 1 && !IsIpAddress(ent->bean->serverAddress)) {
|
||||
if (isFirstProfile && !IsIpAddress(ent->bean->serverAddress)) {
|
||||
if (dataStore->enhance_resolve_server_domain && !IS_NEKO_BOX) {
|
||||
status->result->tryDomains += ent->bean->serverAddress;
|
||||
} else {
|
||||
@@ -522,6 +529,7 @@ namespace NekoRay {
|
||||
|
||||
status->outbounds += outbound;
|
||||
pastTag = tagOut;
|
||||
pastExternalStat = thisExternalStat;
|
||||
index++;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,13 +47,13 @@ namespace NekoRay::fmt {
|
||||
|
||||
//
|
||||
|
||||
virtual bool NeedExternal() { return false; };
|
||||
virtual int NeedExternal(bool isFirstProfile, bool isVPN) { return 0; };
|
||||
|
||||
virtual CoreObjOutboundBuildResult BuildCoreObjV2Ray() { return {}; };
|
||||
|
||||
virtual CoreObjOutboundBuildResult BuildCoreObjSingBox() { return {}; };
|
||||
|
||||
virtual ExternalBuildResult BuildExternal(int mapping_port, int socks_port) { return {}; };
|
||||
virtual ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) { return {}; };
|
||||
|
||||
virtual QString ToShareLink() { return {}; };
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "db/ProxyEntity.hpp"
|
||||
#include "fmt/includes.h"
|
||||
#include "NaiveBean.hpp"
|
||||
|
||||
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
@@ -19,13 +21,37 @@ f.close(); \
|
||||
auto TempFile = QFileInfo(f).absoluteFilePath();
|
||||
|
||||
namespace NekoRay::fmt {
|
||||
ExternalBuildResult NaiveBean::BuildExternal(int mapping_port, int socks_port) {
|
||||
// 0: no external
|
||||
// 1: Mapping External
|
||||
// 2: Direct External
|
||||
|
||||
int NaiveBean::NeedExternal(bool isFirstProfile, bool isVPN) {
|
||||
if (isFirstProfile && !isVPN) {
|
||||
return 2;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int CustomBean::NeedExternal(bool isFirstProfile, bool isVPN) {
|
||||
if (core == "internal") return 0;
|
||||
if (IS_NEKO_BOX && core == "hysteria") return 0;
|
||||
if (core == "hysteria") {
|
||||
if (isFirstProfile && !isVPN) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
ExternalBuildResult NaiveBean::BuildExternal(int mapping_port, int socks_port, int external_stat) {
|
||||
ExternalBuildResult result{dataStore->extraCore->Get("naive")};
|
||||
|
||||
auto is_export = mapping_port == 114514;
|
||||
auto is_direct = external_stat == 2;
|
||||
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;
|
||||
auto connect_address = is_direct ? serverAddress : "127.0.0.1";
|
||||
auto connect_port = is_direct ? serverPort : mapping_port;
|
||||
domain_address = WrapIPV6Host(domain_address);
|
||||
connect_address = WrapIPV6Host(connect_address);
|
||||
|
||||
result.arguments += "--log";
|
||||
result.arguments += "--listen=socks://127.0.0.1:" + Int2String(socks_port);
|
||||
@@ -47,7 +73,7 @@ namespace NekoRay::fmt {
|
||||
return result;
|
||||
}
|
||||
|
||||
ExternalBuildResult CustomBean::BuildExternal(int mapping_port, int socks_port) {
|
||||
ExternalBuildResult CustomBean::BuildExternal(int mapping_port, int socks_port, int external_stat) {
|
||||
ExternalBuildResult result{dataStore->extraCore->Get(core)};
|
||||
|
||||
result.arguments = command; // TODO split?
|
||||
@@ -75,6 +101,15 @@ namespace NekoRay::fmt {
|
||||
suffix = ".json";
|
||||
}
|
||||
|
||||
// known core direct out
|
||||
if (external_stat == 2) {
|
||||
if (core == "hysteria") {
|
||||
config = config.replace(QString("\"127.0.0.1:%1\"").arg(mapping_port),
|
||||
"\"" + DisplayAddress() + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
// write config
|
||||
WriteTempFile("custom_" + GetRandomString(10) + suffix, config.toUtf8());
|
||||
for (int i = 0; i < result.arguments.count(); i++) {
|
||||
result.arguments[i] = result.arguments[i].replace("%config%", TempFile);
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace NekoRay::fmt {
|
||||
return core;
|
||||
};
|
||||
|
||||
QString DisplayCoreType() override { return NeedExternal() ? core : software_core_name; };
|
||||
QString DisplayCoreType() override { return NeedExternal(false, false) ? core : software_core_name; };
|
||||
|
||||
QString DisplayAddress() override {
|
||||
if (core == "internal") {
|
||||
@@ -39,13 +39,9 @@ namespace NekoRay::fmt {
|
||||
return AbstractBean::DisplayAddress();
|
||||
};
|
||||
|
||||
bool NeedExternal() override {
|
||||
if (core == "internal") return false;
|
||||
if (IS_NEKO_BOX && core == "hysteria") return false;
|
||||
return true;
|
||||
};
|
||||
int NeedExternal(bool isFirstProfile, bool isVPN) override;
|
||||
|
||||
ExternalBuildResult BuildExternal(int mapping_port, int socks_port) override;
|
||||
ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override;
|
||||
|
||||
CoreObjOutboundBuildResult BuildCoreObjSingBox() override;
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ namespace NekoRay::fmt {
|
||||
|
||||
QString DisplayType() override { return "Naive"; };
|
||||
|
||||
bool NeedExternal() override { return true; };
|
||||
int NeedExternal(bool isFirstProfile, bool isVPN) override;
|
||||
|
||||
ExternalBuildResult BuildExternal(int mapping_port, int socks_port) override;
|
||||
ExternalBuildResult BuildExternal(int mapping_port, int socks_port, int external_stat) override;
|
||||
|
||||
bool TryParseLink(const QString &link);
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ func (s *server) Start(ctx context.Context, in *gen.LoadConfigReq) (out *gen.Err
|
||||
}()
|
||||
|
||||
if neko_common.Debug {
|
||||
logrus.Println("Start:", in)
|
||||
logrus.Println("Start:", in.CoreConfig, in.TryDomains)
|
||||
}
|
||||
|
||||
if instance != nil {
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace NekoRay {
|
||||
_add(new configItem("remember_enable", &remember_enable, itemType::boolean));
|
||||
_add(new configItem("start_minimal", &start_minimal, itemType::boolean));
|
||||
_add(new configItem("language", &language, itemType::integer));
|
||||
_add(new configItem("spmode", &system_proxy_mode, itemType::integer));
|
||||
_add(new configItem("spmode", &remember_spmode, itemType::integer));
|
||||
_add(new configItem("insecure_hint", &insecure_hint, itemType::boolean));
|
||||
_add(new configItem("skip_cert", &skip_cert, itemType::boolean));
|
||||
_add(new configItem("hk_mw", &hotkey_mainwindow, itemType::string));
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace NekoRay {
|
||||
int started_id = -1919;
|
||||
bool core_running = false;
|
||||
bool core_prepare_exit = false;
|
||||
int running_spmode = NekoRay::SystemProxyMode::DISABLE;
|
||||
bool need_keep_vpn_off = false;
|
||||
|
||||
Routing *routing = new Routing;
|
||||
int imported_count = 0;
|
||||
@@ -82,7 +84,7 @@ namespace NekoRay {
|
||||
bool skip_cert = false;
|
||||
|
||||
// Remember
|
||||
int system_proxy_mode = NekoRay::SystemProxyMode::DISABLE;
|
||||
int remember_spmode = NekoRay::SystemProxyMode::DISABLE;
|
||||
int remember_id = -1919;
|
||||
bool remember_enable = false;
|
||||
bool start_minimal = false;
|
||||
|
||||
@@ -1185,6 +1185,10 @@ End: %2</source>
|
||||
<source>Not Running</source>
|
||||
<translation>未启动</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Current server is incompatible with VPN. Please stop the server first, enable VPN mode, and then restart.</source>
|
||||
<translation>当前服务器与 VPN 不兼容。请先停止服务器,打开 VPN 模式后再启动。</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ProxyItem</name>
|
||||
|
||||
@@ -338,9 +338,10 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
neko_set_spmode(checked ? NekoRay::SystemProxyMode::SYSTEM_PROXY : NekoRay::SystemProxyMode::DISABLE);
|
||||
});
|
||||
connect(ui->menu_spmode, &QMenu::aboutToShow, this, [=]() {
|
||||
ui->menu_spmode_disabled->setChecked(title_spmode == NekoRay::SystemProxyMode::DISABLE);
|
||||
ui->menu_spmode_system_proxy->setChecked(title_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY);
|
||||
ui->menu_spmode_vpn->setChecked(title_spmode == NekoRay::SystemProxyMode::VPN);
|
||||
ui->menu_spmode_disabled->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::DISABLE);
|
||||
ui->menu_spmode_system_proxy->setChecked(
|
||||
NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY);
|
||||
ui->menu_spmode_vpn->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN);
|
||||
});
|
||||
connect(ui->menu_spmode_system_proxy, &QAction::triggered, this,
|
||||
[=]() { neko_set_spmode(NekoRay::SystemProxyMode::SYSTEM_PROXY); });
|
||||
@@ -392,8 +393,8 @@ MainWindow::MainWindow(QWidget *parent)
|
||||
|
||||
// Start last
|
||||
if (NekoRay::dataStore->remember_enable) {
|
||||
if (NekoRay::dataStore->system_proxy_mode == NekoRay::SystemProxyMode::SYSTEM_PROXY) {
|
||||
neko_set_spmode(NekoRay::dataStore->system_proxy_mode, false);
|
||||
if (NekoRay::dataStore->remember_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) {
|
||||
neko_set_spmode(NekoRay::dataStore->remember_spmode, false);
|
||||
}
|
||||
if (NekoRay::dataStore->remember_id >= 0) {
|
||||
runOnUiThread([=] { neko_start(NekoRay::dataStore->remember_id); });
|
||||
@@ -465,7 +466,7 @@ void MainWindow::dialog_message_impl(const QString &sender, const QString &info)
|
||||
auto changed = NekoRay::dataStore->Save();
|
||||
if (info.contains("RouteChanged")) changed = true;
|
||||
refresh_proxy_list();
|
||||
if (info.contains("VPNChanged") && title_spmode == NekoRay::SystemProxyMode::VPN) {
|
||||
if (info.contains("VPNChanged") && NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) {
|
||||
MessageBoxWarning(tr("VPN settings changed"), tr("Restart VPN to take effect."));
|
||||
} else if (changed && NekoRay::dataStore->started_id >= 0 &&
|
||||
QMessageBox::question(GetMessageBoxParent(), tr("Confirmation"),
|
||||
@@ -554,7 +555,7 @@ void MainWindow::on_commitDataRequest() {
|
||||
|
||||
void MainWindow::on_menu_exit_triggered() {
|
||||
neko_set_spmode(NekoRay::SystemProxyMode::DISABLE, false);
|
||||
if (title_spmode == NekoRay::SystemProxyMode::VPN) return;
|
||||
if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) return;
|
||||
RegisterHotkey(true);
|
||||
//
|
||||
on_commitDataRequest();
|
||||
@@ -574,16 +575,17 @@ void MainWindow::on_menu_exit_triggered() {
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
#define neko_set_spmode_FAILED refresh_status(); return;
|
||||
|
||||
void MainWindow::neko_set_spmode(int mode, bool save) {
|
||||
if (mode != title_spmode) {
|
||||
if (mode != NekoRay::dataStore->running_spmode) {
|
||||
// DISABLE
|
||||
|
||||
if (title_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) {
|
||||
if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) {
|
||||
ClearSystemProxy();
|
||||
} else if (title_spmode == NekoRay::SystemProxyMode::VPN) {
|
||||
} else if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) {
|
||||
if (!StopVPNProcess()) {
|
||||
refresh_status();
|
||||
return;
|
||||
neko_set_spmode_FAILED
|
||||
}
|
||||
}
|
||||
|
||||
@@ -609,19 +611,22 @@ void MainWindow::neko_set_spmode(int mode, bool save) {
|
||||
}
|
||||
SetSystemProxy(http_port, socks_port);
|
||||
} else if (mode == NekoRay::SystemProxyMode::VPN) {
|
||||
if (NekoRay::dataStore->need_keep_vpn_off) {
|
||||
MessageBoxWarning(software_name, tr("Current server is incompatible with VPN. Please stop the server first, enable VPN mode, and then restart."));
|
||||
neko_set_spmode_FAILED
|
||||
}
|
||||
if (!StartVPNProcess()) {
|
||||
refresh_status();
|
||||
return;
|
||||
neko_set_spmode_FAILED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (save) {
|
||||
NekoRay::dataStore->system_proxy_mode = mode;
|
||||
NekoRay::dataStore->remember_spmode = mode;
|
||||
NekoRay::dataStore->Save();
|
||||
}
|
||||
|
||||
title_spmode = mode;
|
||||
NekoRay::dataStore->running_spmode = mode;
|
||||
refresh_status();
|
||||
}
|
||||
|
||||
@@ -654,17 +659,17 @@ void MainWindow::refresh_status(const QString &traffic_update) {
|
||||
if (IS_NEKO_BOX) inbound_txt = QString("Mixed: %1").arg(display_socks);
|
||||
ui->label_inbound->setText(inbound_txt);
|
||||
//
|
||||
ui->checkBox_VPN->setChecked(title_spmode == NekoRay::SystemProxyMode::VPN);
|
||||
ui->checkBox_SystemProxy->setChecked(title_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY);
|
||||
ui->checkBox_VPN->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN);
|
||||
ui->checkBox_SystemProxy->setChecked(NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY);
|
||||
if (select_mode) ui->label_running->setText("[" + tr("Select") + "]");
|
||||
|
||||
auto make_title = [=](bool isTray) {
|
||||
QStringList tt;
|
||||
if (select_mode) tt << "[" + tr("Select") + "]";
|
||||
if (!title_error.isEmpty()) tt << "[" + title_error + "]";
|
||||
if (title_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) {
|
||||
if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) {
|
||||
tt << "[" + tr("System Proxy") + "]";
|
||||
} else if (title_spmode == NekoRay::SystemProxyMode::VPN) {
|
||||
} else if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) {
|
||||
tt << "[" + tr("VPN Mode") + "]";
|
||||
}
|
||||
tt << software_name;
|
||||
@@ -677,9 +682,9 @@ void MainWindow::refresh_status(const QString &traffic_update) {
|
||||
};
|
||||
|
||||
auto icon_status_new = TrayIcon::NONE;
|
||||
if (title_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) {
|
||||
if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::SYSTEM_PROXY) {
|
||||
icon_status_new = TrayIcon::SYSTEM_PROXY;
|
||||
} else if (title_spmode == NekoRay::SystemProxyMode::VPN) {
|
||||
} else if (NekoRay::dataStore->running_spmode == NekoRay::SystemProxyMode::VPN) {
|
||||
icon_status_new = TrayIcon::VPN;
|
||||
} else if (!running.isNull()) {
|
||||
icon_status_new = TrayIcon::RUNNING;
|
||||
|
||||
@@ -139,7 +139,6 @@ private:
|
||||
QTextDocument *qvLogDocument = new QTextDocument(this);
|
||||
//
|
||||
QString title_error;
|
||||
int title_spmode = NekoRay::SystemProxyMode::DISABLE;
|
||||
int icon_status = -1;
|
||||
QSharedPointer<NekoRay::ProxyEntity> running;
|
||||
QString traffic_update_cache;
|
||||
|
||||
@@ -292,6 +292,7 @@ void MainWindow::neko_stop(bool crash) {
|
||||
#endif
|
||||
|
||||
NekoRay::dataStore->UpdateStartedId(-1919);
|
||||
NekoRay::dataStore->need_keep_vpn_off = false;
|
||||
running = nullptr;
|
||||
refresh_status();
|
||||
refresh_proxy_list(id);
|
||||
|
||||
Reference in New Issue
Block a user