mirror of
https://github.com/MatsuriDayo/nekoray.git
synced 2025-12-17 20:44:38 +03:00
v2ray format migration
This commit is contained in:
@@ -64,11 +64,7 @@ namespace NekoGui {
|
||||
if (customBean != nullptr && customBean->core == "internal-full") {
|
||||
result->coreConfig = QString2QJsonObject(customBean->config_simple);
|
||||
} else {
|
||||
if (IS_NEKO_BOX) {
|
||||
BuildConfigSingBox(status);
|
||||
} else {
|
||||
BuildConfigV2Ray(status);
|
||||
}
|
||||
BuildConfigSingBox(status);
|
||||
}
|
||||
|
||||
// apply custom config
|
||||
@@ -157,254 +153,6 @@ namespace NekoGui {
|
||||
status->ipListDirect += line; \
|
||||
}
|
||||
|
||||
// V2Ray
|
||||
|
||||
void BuildConfigV2Ray(const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
// Log
|
||||
auto logObj = QJsonObject{{"loglevel", dataStore->log_level}};
|
||||
status->result->coreConfig.insert("log", logObj);
|
||||
|
||||
// Inbounds
|
||||
QJsonObject sniffing{
|
||||
{"destOverride", QJsonArray{"http", "tls", "quic"}},
|
||||
{"enabled", true},
|
||||
{"metadataOnly", false},
|
||||
{"routeOnly", dataStore->routing->sniffing_mode == SniffingMode::FOR_ROUTING},
|
||||
};
|
||||
|
||||
// socks-in
|
||||
if (IsValidPort(dataStore->inbound_socks_port) && !status->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "socks-in";
|
||||
inboundObj["protocol"] = "socks";
|
||||
inboundObj["listen"] = dataStore->inbound_address;
|
||||
inboundObj["port"] = dataStore->inbound_socks_port;
|
||||
QJsonObject socksSettings = {{"udp", true}};
|
||||
if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE) {
|
||||
inboundObj["sniffing"] = sniffing;
|
||||
}
|
||||
if (dataStore->inbound_auth->NeedAuth()) {
|
||||
socksSettings["auth"] = "password";
|
||||
socksSettings["accounts"] = QJsonArray{
|
||||
QJsonObject{
|
||||
{"user", dataStore->inbound_auth->username},
|
||||
{"pass", dataStore->inbound_auth->password},
|
||||
},
|
||||
};
|
||||
}
|
||||
inboundObj["settings"] = socksSettings;
|
||||
status->inbounds += inboundObj;
|
||||
}
|
||||
// http-in
|
||||
if (IsValidPort(dataStore->inbound_http_port) && !status->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "http-in";
|
||||
inboundObj["protocol"] = "http";
|
||||
inboundObj["listen"] = dataStore->inbound_address;
|
||||
inboundObj["port"] = dataStore->inbound_http_port;
|
||||
if (dataStore->routing->sniffing_mode != SniffingMode::DISABLE) {
|
||||
inboundObj["sniffing"] = sniffing;
|
||||
}
|
||||
if (dataStore->inbound_auth->NeedAuth()) {
|
||||
inboundObj["settings"] = QJsonObject{
|
||||
{"accounts", QJsonArray{
|
||||
QJsonObject{
|
||||
{"user", dataStore->inbound_auth->username},
|
||||
{"pass", dataStore->inbound_auth->password},
|
||||
},
|
||||
}},
|
||||
};
|
||||
}
|
||||
status->inbounds += inboundObj;
|
||||
}
|
||||
|
||||
// Outbounds
|
||||
auto tagProxy = BuildChain(0, status);
|
||||
if (!status->result->error.isEmpty()) return;
|
||||
|
||||
// direct & bypass & block
|
||||
status->outbounds += QJsonObject{
|
||||
{"protocol", "freedom"},
|
||||
{"domainStrategy", dataStore->core_ray_freedom_domainStrategy},
|
||||
{"tag", "direct"},
|
||||
};
|
||||
status->outbounds += QJsonObject{
|
||||
{"protocol", "freedom"},
|
||||
{"domainStrategy", dataStore->core_ray_freedom_domainStrategy},
|
||||
{"tag", "bypass"},
|
||||
};
|
||||
status->outbounds += QJsonObject{
|
||||
{"protocol", "blackhole"},
|
||||
{"tag", "block"},
|
||||
};
|
||||
|
||||
// DNS out
|
||||
if (!status->forTest) {
|
||||
QJsonObject dnsOut;
|
||||
dnsOut["protocol"] = "dns";
|
||||
dnsOut["tag"] = "dns-out";
|
||||
QJsonObject dnsOut_settings;
|
||||
dnsOut_settings["network"] = "tcp";
|
||||
dnsOut_settings["port"] = 53;
|
||||
dnsOut_settings["address"] = "8.8.8.8";
|
||||
dnsOut_settings["userLevel"] = 1;
|
||||
dnsOut["settings"] = dnsOut_settings;
|
||||
dnsOut["proxySettings"] = QJsonObject{{"tag", tagProxy},
|
||||
{"transportLayer", true}};
|
||||
|
||||
status->outbounds += dnsOut;
|
||||
status->routingRules += QJsonObject{
|
||||
{"type", "field"},
|
||||
{"port", "53"},
|
||||
{"inboundTag", QJsonArray{"socks-in", "http-in"}},
|
||||
{"outboundTag", "dns-out"},
|
||||
};
|
||||
}
|
||||
|
||||
// custom inbound
|
||||
if (!status->forTest) QJSONARRAY_ADD(status->inbounds, QString2QJsonObject(dataStore->custom_inbound)["inbounds"].toArray())
|
||||
|
||||
status->result->coreConfig.insert("inbounds", status->inbounds);
|
||||
status->result->coreConfig.insert("outbounds", status->outbounds);
|
||||
|
||||
// user rule
|
||||
if (!status->forTest) {
|
||||
DOMAIN_USER_RULE
|
||||
IP_USER_RULE
|
||||
}
|
||||
|
||||
// final add DNS
|
||||
QJsonObject dns;
|
||||
QJsonArray dnsServers;
|
||||
|
||||
// Remote or FakeDNS
|
||||
QJsonObject dnsServerRemote;
|
||||
dnsServerRemote["address"] = dataStore->routing->remote_dns;
|
||||
dnsServerRemote["domains"] = QList2QJsonArray<QString>(status->domainListDNSRemote);
|
||||
dnsServerRemote["queryStrategy"] = dataStore->routing->remote_dns_strategy;
|
||||
if (!status->forTest) dnsServers += dnsServerRemote;
|
||||
|
||||
// Direct
|
||||
auto directDnsAddress = dataStore->routing->direct_dns;
|
||||
if (directDnsAddress.contains("://")) {
|
||||
auto directDnsIp = SubStrBefore(SubStrAfter(directDnsAddress, "://"), "/");
|
||||
if (IsIpAddress(directDnsIp)) {
|
||||
status->routingRules.push_front(QJsonObject{
|
||||
{"type", "field"},
|
||||
{"ip", QJsonArray{directDnsIp}},
|
||||
{"outboundTag", "direct"},
|
||||
});
|
||||
} else {
|
||||
status->routingRules.push_front(QJsonObject{
|
||||
{"type", "field"},
|
||||
{"domain", QJsonArray{directDnsIp}},
|
||||
{"outboundTag", "direct"},
|
||||
});
|
||||
}
|
||||
} else if (directDnsAddress != "localhost") {
|
||||
status->routingRules.push_front(QJsonObject{
|
||||
{"type", "field"},
|
||||
{"ip", QJsonArray{directDnsAddress}},
|
||||
{"outboundTag", "direct"},
|
||||
});
|
||||
}
|
||||
QJsonObject directObj{
|
||||
{"address", directDnsAddress.replace("https://", "https+local://")},
|
||||
{"queryStrategy", dataStore->routing->direct_dns_strategy},
|
||||
{"domains", QList2QJsonArray<QString>(status->domainListDNSDirect)},
|
||||
};
|
||||
if (dataStore->routing->dns_final_out == "bypass") {
|
||||
dnsServers.prepend(directObj);
|
||||
} else {
|
||||
dnsServers.append(directObj);
|
||||
}
|
||||
|
||||
dns["disableFallback"] = true;
|
||||
dns["servers"] = dnsServers;
|
||||
dns["tag"] = "dns";
|
||||
|
||||
if (dataStore->routing->use_dns_object) {
|
||||
dns = QString2QJsonObject(dataStore->routing->dns_object);
|
||||
}
|
||||
status->result->coreConfig.insert("dns", dns);
|
||||
|
||||
// Routing
|
||||
QJsonObject routing;
|
||||
routing["domainStrategy"] = dataStore->routing->domain_strategy;
|
||||
if (status->forTest) routing["domainStrategy"] = "AsIs";
|
||||
|
||||
// final add user rule (block)
|
||||
QJsonObject routingRule_tmp;
|
||||
routingRule_tmp["type"] = "field";
|
||||
routingRule_tmp["outboundTag"] = "block";
|
||||
if (!status->ipListBlock.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["ip"] = QList2QJsonArray<QString>(status->ipListBlock);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
if (!status->domainListBlock.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["domain"] = QList2QJsonArray<QString>(status->domainListBlock);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
|
||||
// final add user rule (proxy)
|
||||
routingRule_tmp["outboundTag"] = "proxy";
|
||||
if (!status->ipListRemote.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["ip"] = QList2QJsonArray<QString>(status->ipListRemote);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
if (!status->domainListRemote.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["domain"] = QList2QJsonArray<QString>(status->domainListRemote);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
|
||||
// final add user rule (bypass)
|
||||
routingRule_tmp["outboundTag"] = "bypass";
|
||||
if (!status->ipListDirect.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["ip"] = QList2QJsonArray<QString>(status->ipListDirect);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
if (!status->domainListDirect.isEmpty()) {
|
||||
auto tmp = routingRule_tmp;
|
||||
tmp["domain"] = QList2QJsonArray<QString>(status->domainListDirect);
|
||||
status->routingRules += tmp;
|
||||
}
|
||||
|
||||
// def_outbound
|
||||
if (!status->forTest) status->routingRules += QJsonObject{
|
||||
{"type", "field"},
|
||||
{"port", "0-65535"},
|
||||
{"outboundTag", dataStore->routing->def_outbound},
|
||||
};
|
||||
|
||||
// final add routing rule
|
||||
auto routingRules = QString2QJsonObject(dataStore->routing->custom)["rules"].toArray();
|
||||
if (status->forTest) routingRules = {};
|
||||
if (!status->forTest) QJSONARRAY_ADD(routingRules, QString2QJsonObject(dataStore->custom_route_global)["rules"].toArray())
|
||||
QJSONARRAY_ADD(routingRules, status->routingRules)
|
||||
routing["rules"] = routingRules;
|
||||
status->result->coreConfig.insert("routing", routing);
|
||||
|
||||
// Policy & stats
|
||||
QJsonObject policy;
|
||||
QJsonObject levels;
|
||||
QJsonObject level1;
|
||||
level1["connIdle"] = 30;
|
||||
levels["1"] = level1;
|
||||
policy["levels"] = levels;
|
||||
|
||||
QJsonObject policySystem;
|
||||
policySystem["statsOutboundDownlink"] = true;
|
||||
policySystem["statsOutboundUplink"] = true;
|
||||
policy["system"] = policySystem;
|
||||
status->result->coreConfig.insert("policy", policy);
|
||||
status->result->coreConfig.insert("stats", QJsonObject());
|
||||
}
|
||||
|
||||
QString BuildChainInternal(int chainId, const QList<std::shared_ptr<ProxyEntity>> &ents,
|
||||
const std::shared_ptr<BuildConfigStatus> &status) {
|
||||
QString chainTag = "c-" + Int2String(chainId);
|
||||
@@ -454,29 +202,14 @@ namespace NekoGui {
|
||||
// chain rules: past
|
||||
if (pastExternalStat == 0) {
|
||||
auto replaced = status->outbounds.last().toObject();
|
||||
if (IS_NEKO_BOX) {
|
||||
replaced["detour"] = tagOut;
|
||||
} else {
|
||||
replaced["proxySettings"] = QJsonObject{
|
||||
{"tag", tagOut},
|
||||
{"transportLayer", true},
|
||||
};
|
||||
}
|
||||
replaced["detour"] = tagOut;
|
||||
status->outbounds.removeLast();
|
||||
status->outbounds += replaced;
|
||||
} else {
|
||||
if (IS_NEKO_BOX) {
|
||||
status->routingRules += QJsonObject{
|
||||
{"inbound", QJsonArray{pastTag + "-mapping"}},
|
||||
{"outbound", tagOut},
|
||||
};
|
||||
} else {
|
||||
status->routingRules += QJsonObject{
|
||||
{"type", "field"},
|
||||
{"inboundTag", QJsonArray{pastTag + "-mapping"}},
|
||||
{"outboundTag", tagOut},
|
||||
};
|
||||
}
|
||||
status->routingRules += QJsonObject{
|
||||
{"inbound", QJsonArray{pastTag + "-mapping"}},
|
||||
{"outbound", tagOut},
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// index == 0 means last profile in chain / not chain
|
||||
@@ -515,43 +248,20 @@ namespace NekoGui {
|
||||
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"},
|
||||
{"listen", "127.0.0.1"},
|
||||
{"listen_port", ext_mapping_port},
|
||||
{"override_address", ent->bean->serverAddress},
|
||||
{"override_port", ent->bean->serverPort},
|
||||
};
|
||||
} else {
|
||||
status->inbounds += QJsonObject{
|
||||
{"protocol", "dokodemo-door"},
|
||||
{"tag", tagOut + "-mapping"},
|
||||
{"listen", "127.0.0.1"},
|
||||
{"port", ext_mapping_port},
|
||||
{"settings", QJsonObject{
|
||||
// to
|
||||
{"address", ent->bean->serverAddress},
|
||||
{"port", ent->bean->serverPort},
|
||||
{"network", "tcp,udp"},
|
||||
}},
|
||||
};
|
||||
}
|
||||
status->inbounds += QJsonObject{
|
||||
{"type", "direct"},
|
||||
{"tag", tagOut + "-mapping"},
|
||||
{"listen", "127.0.0.1"},
|
||||
{"listen_port", ext_mapping_port},
|
||||
{"override_address", ent->bean->serverAddress},
|
||||
{"override_port", ent->bean->serverPort},
|
||||
};
|
||||
// no chain rule and not outbound, so need to set to direct
|
||||
if (isFirstProfile) {
|
||||
if (IS_NEKO_BOX) {
|
||||
status->routingRules += QJsonObject{
|
||||
{"inbound", QJsonArray{tagOut + "-mapping"}},
|
||||
{"outbound", "direct"},
|
||||
};
|
||||
} else {
|
||||
status->routingRules += QJsonObject{
|
||||
{"type", "field"},
|
||||
{"inboundTag", QJsonArray{tagOut + "-mapping"}},
|
||||
{"outboundTag", "direct"},
|
||||
};
|
||||
}
|
||||
status->routingRules += QJsonObject{
|
||||
{"inbound", QJsonArray{tagOut + "-mapping"}},
|
||||
{"outbound", "direct"},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,23 +284,11 @@ namespace NekoGui {
|
||||
status->result->extRs.emplace_back(std::make_shared<NekoGui_fmt::ExternalBuildResult>(extR));
|
||||
|
||||
// SOCKS OUTBOUND
|
||||
if (IS_NEKO_BOX) {
|
||||
outbound["type"] = "socks";
|
||||
outbound["server"] = "127.0.0.1";
|
||||
outbound["server_port"] = ext_socks_port;
|
||||
} else {
|
||||
outbound["protocol"] = "socks";
|
||||
QJsonObject settings;
|
||||
QJsonArray servers;
|
||||
QJsonObject server;
|
||||
server["address"] = "127.0.0.1";
|
||||
server["port"] = ext_socks_port;
|
||||
servers.push_back(server);
|
||||
settings["servers"] = servers;
|
||||
outbound["settings"] = settings;
|
||||
}
|
||||
outbound["type"] = "socks";
|
||||
outbound["server"] = "127.0.0.1";
|
||||
outbound["server_port"] = ext_socks_port;
|
||||
} else {
|
||||
const auto coreR = IS_NEKO_BOX ? ent->bean->BuildCoreObjSingBox() : ent->bean->BuildCoreObjV2Ray();
|
||||
const auto coreR = ent->bean->BuildCoreObjSingBox();
|
||||
if (coreR.outbound.isEmpty()) {
|
||||
status->result->error = "unsupported outbound";
|
||||
return {};
|
||||
@@ -613,14 +311,8 @@ namespace NekoGui {
|
||||
needMux &= dataStore->mux_concurrency > 0;
|
||||
|
||||
if (stream != nullptr) {
|
||||
if (IS_NEKO_BOX) {
|
||||
if (stream->network == "grpc" || stream->network == "quic" || (stream->network == "http" && stream->security == "tls")) {
|
||||
needMux = false;
|
||||
}
|
||||
} else {
|
||||
if (stream->network == "grpc" || stream->network == "quic") {
|
||||
needMux = false;
|
||||
}
|
||||
if (stream->network == "grpc" || stream->network == "quic" || (stream->network == "http" && stream->security == "tls")) {
|
||||
needMux = false;
|
||||
}
|
||||
if (stream->multiplex_status == 0) {
|
||||
if (!dataStore->mux_default_on) needMux = false;
|
||||
@@ -635,32 +327,18 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
// common
|
||||
if (IS_NEKO_BOX) {
|
||||
// apply domain_strategy
|
||||
outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy;
|
||||
// apply mux
|
||||
if (!muxApplied && needMux) {
|
||||
auto muxObj = QJsonObject{
|
||||
{"enabled", true},
|
||||
{"protocol", dataStore->mux_protocol},
|
||||
{"padding", dataStore->mux_padding},
|
||||
{"max_streams", dataStore->mux_concurrency},
|
||||
};
|
||||
outbound["multiplex"] = muxObj;
|
||||
muxApplied = true;
|
||||
}
|
||||
} else {
|
||||
// apply domain_strategy
|
||||
if (!status->forTest) outbound["domainStrategy"] = dataStore->routing->outbound_domain_strategy;
|
||||
// apply mux
|
||||
if (!muxApplied && needMux) {
|
||||
auto muxObj = QJsonObject{
|
||||
{"enabled", true},
|
||||
{"concurrency", dataStore->mux_concurrency},
|
||||
};
|
||||
outbound["mux"] = muxObj;
|
||||
muxApplied = true;
|
||||
}
|
||||
// apply domain_strategy
|
||||
outbound["domain_strategy"] = dataStore->routing->outbound_domain_strategy;
|
||||
// apply mux
|
||||
if (!muxApplied && needMux) {
|
||||
auto muxObj = QJsonObject{
|
||||
{"enabled", true},
|
||||
{"protocol", dataStore->mux_protocol},
|
||||
{"padding", dataStore->mux_padding},
|
||||
{"max_streams", dataStore->mux_concurrency},
|
||||
};
|
||||
outbound["multiplex"] = muxObj;
|
||||
muxApplied = true;
|
||||
}
|
||||
|
||||
// apply custom outbound settings
|
||||
@@ -720,7 +398,7 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
// tun-in
|
||||
if (IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) {
|
||||
if (dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) {
|
||||
QJsonObject inboundObj;
|
||||
inboundObj["tag"] = "tun-in";
|
||||
inboundObj["type"] = "tun";
|
||||
@@ -869,7 +547,7 @@ namespace NekoGui {
|
||||
};
|
||||
|
||||
// Fakedns
|
||||
if (dataStore->fake_dns && IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) {
|
||||
if (dataStore->fake_dns && dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) {
|
||||
dnsServers += QJsonObject{
|
||||
{"tag", "dns-fake"},
|
||||
{"address", "fakeip"},
|
||||
@@ -911,7 +589,7 @@ namespace NekoGui {
|
||||
}
|
||||
|
||||
// fakedns rule
|
||||
if (dataStore->fake_dns && IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) {
|
||||
if (dataStore->fake_dns && dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) {
|
||||
dnsRules += QJsonObject{
|
||||
{"inbound", "tun-in"},
|
||||
{"server", "dns-fake"},
|
||||
@@ -969,7 +647,7 @@ namespace NekoGui {
|
||||
};
|
||||
|
||||
// tun user rule
|
||||
if (IS_NEKO_BOX_INTERNAL_TUN && dataStore->spmode_vpn && !status->forTest) {
|
||||
if (dataStore->vpn_internal_tun && dataStore->spmode_vpn && !status->forTest) {
|
||||
auto match_out = dataStore->vpn_rule_white ? "proxy" : "bypass";
|
||||
|
||||
QString process_name_rule = dataStore->vpn_rule_process.trimmed();
|
||||
|
||||
Reference in New Issue
Block a user