#include "db/ProxyEntity.hpp" #include "fmt/includes.h" namespace NekoGui_fmt { void V2rayStreamSettings::BuildStreamSettingsSingBox(QJsonObject *outbound) { // https://sing-box.sagernet.org/configuration/shared/v2ray-transport if (network != "tcp") { QJsonObject transport{{"type", network}}; if (network == "ws") { if (!host.isEmpty()) transport["headers"] = QJsonObject{{"Host", host}}; // ws path & ed auto pathWithoutEd = SubStrBefore(path, "?ed="); if (!pathWithoutEd.isEmpty()) transport["path"] = pathWithoutEd; if (pathWithoutEd != path) { auto ed = SubStrAfter(path, "?ed=").toInt(); if (ed > 0) { transport["max_early_data"] = ed; transport["early_data_header_name"] = "Sec-WebSocket-Protocol"; } } if (ws_early_data_length > 0) { transport["max_early_data"] = ws_early_data_length; transport["early_data_header_name"] = ws_early_data_name; } } else if (network == "http") { if (!path.isEmpty()) transport["path"] = path; if (!host.isEmpty()) transport["host"] = QList2QJsonArray(host.split(",")); } else if (network == "grpc") { if (!path.isEmpty()) transport["service_name"] = path; } else if (network == "httpupgrade") { if (!path.isEmpty()) transport["path"] = path; if (!host.isEmpty()) transport["host"] = host; } outbound->insert("transport", transport); } else if (header_type == "http") { // TCP + headerType QJsonObject transport{ {"type", "http"}, {"method", "GET"}, {"path", path}, {"headers", QJsonObject{{"Host", QList2QJsonArray(host.split(","))}}}, }; outbound->insert("transport", transport); } // 对应字段 tls if (security == "tls") { QJsonObject tls{{"enabled", true}}; if (allow_insecure || NekoGui::dataStore->skip_cert) tls["insecure"] = true; if (!sni.trimmed().isEmpty()) tls["server_name"] = sni; if (!certificate.trimmed().isEmpty()) { tls["certificate"] = certificate.trimmed(); } if (!alpn.trimmed().isEmpty()) { tls["alpn"] = QList2QJsonArray(alpn.split(",")); } QString fp = utlsFingerprint; if (!reality_pbk.trimmed().isEmpty()) { tls["reality"] = QJsonObject{ {"enabled", true}, {"public_key", reality_pbk}, {"short_id", reality_sid.split(",")[0]}, }; if (fp.isEmpty()) fp = "random"; } if (!fp.isEmpty()) { tls["utls"] = QJsonObject{ {"enabled", true}, {"fingerprint", fp}, }; } outbound->insert("tls", tls); } if (outbound->value("type").toString() == "vmess" || outbound->value("type").toString() == "vless") { outbound->insert("packet_encoding", packet_encoding); } } CoreObjOutboundBuildResult SocksHttpBean::BuildCoreObjSingBox() { CoreObjOutboundBuildResult result; QJsonObject outbound; outbound["type"] = socks_http_type == type_HTTP ? "http" : "socks"; if (socks_http_type == type_Socks4) outbound["version"] = "4"; outbound["server"] = serverAddress; outbound["server_port"] = serverPort; if (!username.isEmpty() && !password.isEmpty()) { outbound["username"] = username; outbound["password"] = password; } stream->BuildStreamSettingsSingBox(&outbound); result.outbound = outbound; return result; } CoreObjOutboundBuildResult ShadowSocksBean::BuildCoreObjSingBox() { CoreObjOutboundBuildResult result; QJsonObject outbound{{"type", "shadowsocks"}}; outbound["server"] = serverAddress; outbound["server_port"] = serverPort; outbound["method"] = method; outbound["password"] = password; if (uot != 0) { QJsonObject udp_over_tcp{ {"enabled", true}, {"version", uot}, }; outbound["udp_over_tcp"] = udp_over_tcp; } else { outbound["udp_over_tcp"] = false; } if (!plugin.trimmed().isEmpty()) { outbound["plugin"] = SubStrBefore(plugin, ";"); outbound["plugin_opts"] = SubStrAfter(plugin, ";"); } stream->BuildStreamSettingsSingBox(&outbound); result.outbound = outbound; return result; } CoreObjOutboundBuildResult VMessBean::BuildCoreObjSingBox() { CoreObjOutboundBuildResult result; QJsonObject outbound{ {"type", "vmess"}, {"server", serverAddress}, {"server_port", serverPort}, {"uuid", uuid.trimmed()}, {"alter_id", aid}, {"security", security}, }; stream->BuildStreamSettingsSingBox(&outbound); result.outbound = outbound; return result; } CoreObjOutboundBuildResult TrojanVLESSBean::BuildCoreObjSingBox() { CoreObjOutboundBuildResult result; QJsonObject outbound{ {"type", proxy_type == proxy_VLESS ? "vless" : "trojan"}, {"server", serverAddress}, {"server_port", serverPort}, }; QJsonObject settings; if (proxy_type == proxy_VLESS) { if (flow.right(7) == "-udp443") { // 检查末尾是否包含"-udp443",如果是,则删去 flow.chop(7); } else if (flow == "none") { // 不使用 flow flow = ""; } outbound["uuid"] = password.trimmed(); outbound["flow"] = flow; } else { outbound["password"] = password; } stream->BuildStreamSettingsSingBox(&outbound); result.outbound = outbound; return result; } CoreObjOutboundBuildResult QUICBean::BuildCoreObjSingBox() { CoreObjOutboundBuildResult result; QJsonObject coreTlsObj{ {"enabled", true}, {"disable_sni", disableSni}, {"insecure", allowInsecure}, {"certificate", caText.trimmed()}, {"server_name", sni}, }; if (!alpn.trimmed().isEmpty()) coreTlsObj["alpn"] = QList2QJsonArray(alpn.split(",")); if (proxy_type == proxy_Hysteria2) coreTlsObj["alpn"] = "h3"; QJsonObject outbound{ {"server", serverAddress}, {"server_port", serverPort}, {"tls", coreTlsObj}, }; if (proxy_type == proxy_Hysteria2) { outbound["type"] = "hysteria2"; outbound["password"] = password; outbound["up_mbps"] = uploadMbps; outbound["down_mbps"] = downloadMbps; if (!hopPort.trimmed().isEmpty()) { outbound["hop_ports"] = hopPort; outbound["hop_interval"] = hopInterval; } if (!obfsPassword.isEmpty()) { outbound["obfs"] = QJsonObject{ {"type", "salamander"}, {"password", obfsPassword}, }; } } else if (proxy_type == proxy_TUIC) { outbound["type"] = "tuic"; outbound["uuid"] = uuid; outbound["password"] = password; outbound["congestion_control"] = congestionControl; if (uos) { outbound["udp_over_stream"] = true; } else { outbound["udp_relay_mode"] = udpRelayMode; } outbound["zero_rtt_handshake"] = zeroRttHandshake; if (!heartbeat.trimmed().isEmpty()) outbound["heartbeat"] = heartbeat; } result.outbound = outbound; return result; } CoreObjOutboundBuildResult CustomBean::BuildCoreObjSingBox() { CoreObjOutboundBuildResult result; if (core == "internal") { result.outbound = QString2QJsonObject(config_simple); } return result; } } // namespace NekoGui_fmt