diff --git a/src/generator/config/subexport.cpp b/src/generator/config/subexport.cpp index 9fb3bb5003..a63967d907 100644 --- a/src/generator/config/subexport.cpp +++ b/src/generator/config/subexport.cpp @@ -534,6 +534,8 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr singleproxy["type"] = "hysteria2"; if (!x.Ports.empty()) singleproxy["ports"] = x.Ports; + if (!x.Mport.empty()) + singleproxy["mport"] = x.Mport; if (!x.Up.empty()) singleproxy["up"] = x.UpSpeed; if (!x.Down.empty()) @@ -612,8 +614,13 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr case ProxyType::VLESS: singleproxy["type"] = "vless"; singleproxy["tls"] = true; - if (udp) - singleproxy["packet-encoding"] = "xudp"; + // Output packet-encoding, xudp, packet-addr only if explicitly set + if (!x.PacketEncoding.empty()) + singleproxy["packet-encoding"] = x.PacketEncoding; + if (!x.XUDP.is_undef()) + singleproxy["xudp"] = x.XUDP.get(); + if (!x.PacketAddr.is_undef()) + singleproxy["packet-addr"] = x.PacketAddr.get(); if (!x.UUID.empty()) singleproxy["uuid"] = x.UUID; if (!x.SNI.empty()) @@ -633,7 +640,8 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr singleproxy["network"] = x.TransferProtocol; if(ext.clash_new_field_name) { - singleproxy["ws-opts"]["path"] = x.Path.empty() ? "/" : x.Path; + if(!x.Path.empty()) + singleproxy["ws-opts"]["path"] = x.Path; if(!x.Host.empty()) singleproxy["ws-opts"]["headers"]["Host"] = x.Host; if(!x.Edge.empty()) @@ -641,7 +649,8 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr } else { - singleproxy["ws-path"] = x.Path.empty() ? "/" : x.Path; + if(!x.Path.empty()) + singleproxy["ws-path"] = x.Path; if(!x.Host.empty()) singleproxy["ws-headers"]["Host"] = x.Host; if(!x.Edge.empty()) @@ -651,7 +660,8 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr case "http"_hash: singleproxy["network"] = x.TransferProtocol; singleproxy["http-opts"]["method"] = "GET"; - singleproxy["http-opts"]["path"].push_back(x.Path.empty() ? "/" : x.Path); + if(!x.Path.empty()) + singleproxy["http-opts"]["path"].push_back(x.Path); if(!x.Host.empty()) singleproxy["http-opts"]["headers"]["Host"].push_back(x.Host); if(!x.Edge.empty()) @@ -659,7 +669,8 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr break; case "h2"_hash: singleproxy["network"] = x.TransferProtocol; - singleproxy["h2-opts"]["path"] = x.Path.empty() ? "/" : x.Path; + if(!x.Path.empty()) + singleproxy["h2-opts"]["path"] = x.Path; if(!x.Host.empty()) singleproxy["h2-opts"]["host"].push_back(x.Host); break; @@ -669,20 +680,25 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr singleproxy["grpc-opts"]["grpc-service-name"] = x.GRPCServiceName; break; default: - continue; + break; } if (!x.Fingerprint.empty()) singleproxy["fingerprint"] = x.Fingerprint; + if (!x.ClientFingerprint.empty()) + singleproxy["client-fingerprint"] = x.ClientFingerprint; if (x.XTLS == 2) { singleproxy["flow"] = "xtls-rprx-vision"; + } else if (!x.FlowSet.is_undef() && x.FlowSet.get()) { + // Output flow field if it was explicitly set (even if empty) + singleproxy["flow"] = x.Flow; } else if (!x.Flow.empty()) { + // Fallback for backward compatibility singleproxy["flow"] = x.Flow; } if (!x.PublicKey.empty() && !x.ShortID.empty()) { singleproxy["reality-opts"]["public-key"] = x.PublicKey; singleproxy["reality-opts"]["short-id"] = x.ShortID; - singleproxy["client-fingerprint"] = "random"; } if (!scv.is_undef()) singleproxy["skip-cert-verify"] = scv.get(); @@ -693,8 +709,9 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGr // UDP is not supported yet in clash using snell // sees in https://dreamacro.github.io/clash/configuration/outbound.html#snell - if(udp && x.Type != ProxyType::Snell) - singleproxy["udp"] = true; + // Output UDP field when explicitly provided (true or false) + if(!x.UDP.is_undef() && x.Type != ProxyType::Snell) + singleproxy["udp"] = x.UDP.get(); if(!tfo.is_undef()) singleproxy["tfo"] = tfo.get(); if(proxy_block) diff --git a/src/parser/config/proxy.h b/src/parser/config/proxy.h index 299969fd09..62d95cd98d 100644 --- a/src/parser/config/proxy.h +++ b/src/parser/config/proxy.h @@ -105,6 +105,10 @@ struct Proxy tribool AllowInsecure; tribool TLS13; + // Shadowsocks UDP-over-TCP options + tribool UdpOverTcp; + uint32_t UdpOverTcpVersion = 0; + String UnderlyingProxy; uint16_t SnellVersion = 0; @@ -123,6 +127,7 @@ struct Proxy String ClientId; String Ports; + String Mport; // Alternative to Ports for Hysteria2 String Up; uint32_t UpSpeed; String Down; @@ -134,6 +139,12 @@ struct Proxy String CaStr; uint32_t RecvWindowConn; uint32_t RecvWindow; + // Hysteria2 additional receive window configs + uint64_t InitialStreamReceiveWindow = 0; + uint64_t MaxStreamReceiveWindow = 0; + uint64_t InitialConnectionReceiveWindow = 0; + uint64_t MaxConnectionReceiveWindow = 0; + uint32_t UdpMTU = 0; tribool DisableMtuDiscovery; uint32_t HopInterval; StringArray Alpn; @@ -149,6 +160,7 @@ struct Proxy String UdpRelayMode; String CongestionController; uint32_t MaxUdpRelayPacketSize; + uint32_t MaxDatagramFrameSize = 0; // TUIC tribool FastOpen; uint32_t MaxOpenStreams; @@ -157,10 +169,57 @@ struct Proxy uint32_t MinIdleSession; String Flow; + tribool FlowSet; // Flag to indicate if Flow field was explicitly set (even if empty) uint32_t XTLS; String PacketEncoding; + tribool PacketAddr; + tribool GlobalPadding; + tribool AuthenticatedLength; + tribool XUDP; String ShortID; + // New parameters from mihomo + String IpVersion; // ip-version: ipv4/ipv6/dual/ipv4-prefer/ipv6-prefer + String ClientFingerprint; // client-fingerprint: chrome/firefox/safari/ios/random/none + + // ECH (Encrypted Client Hello) options + tribool EchEnable; + String EchConfig; + + // SMUX (multiplexing) options for Shadowsocks + tribool SmuxEnabled; + String SmuxProtocol; // smux/yamux/h2mux + uint32_t SmuxMaxConnections; + uint32_t SmuxMinStreams; + uint32_t SmuxMaxStreams; + tribool SmuxPadding; + tribool SmuxStatistic; + tribool SmuxOnlyTcp; + + // mTLS options + String Certificate; // Certificate for mTLS + String PrivateKeyPem; // Private key for mTLS + + // VLESS encryption + String VlessEncryption; // mlkem768x25519plus.native/xorpub/random.1rtt/0rtt + + // WebSocket additional options + uint32_t WsMaxEarlyData; + String WsEarlyDataHeaderName; + tribool V2rayHttpUpgrade; + tribool V2rayHttpUpgradeFastOpen; + + // HTTP options for VLESS + String HttpMethod; + StringArray HttpPath; + + // Hysteria2 additional options + tribool HopIntervalTribool; // Already have HopInterval as uint32_t, adding tribool version for better support + + // Trojan SS options + String TrojanSsMethod; + String TrojanSsPassword; + }; #define SS_DEFAULT_GROUP "SSProvider" diff --git a/src/parser/subparser.cpp b/src/parser/subparser.cpp index bc0618470b..e39c03d0ca 100644 --- a/src/parser/subparser.cpp +++ b/src/parser/subparser.cpp @@ -52,12 +52,15 @@ void vmessConstruct(Proxy &node, const std::string &group, const std::string &re if(net == "quic") { node.QUICSecure = host; - node.QUICSecret = path; + if (!path.empty()) + node.QUICSecret = path; } else { - node.Host = (host.empty() && !isIPv4(add) && !isIPv6(add)) ? add.data() : trim(host); - node.Path = path.empty() ? "/" : trim(path); + if (!host.empty()) + node.Host = trim(host); + if (!path.empty()) + node.Path = trim(path); } node.FakeType = type; node.TLSSecure = tls == "tls"; @@ -227,11 +230,12 @@ void hysteria2Construct( const std::string &ca_str, const std::string &cwnd, const std::string &hop_interval, + tribool udp, tribool tfo, tribool scv, const std::string &underlying_proxy ) { - commonConstruct(node, ProxyType::Hysteria2, group, remarks, server, port, tribool(), tfo, scv, tribool(), underlying_proxy); + commonConstruct(node, ProxyType::Hysteria2, group, remarks, server, port, udp, tfo, scv, tribool(), underlying_proxy); node.UpSpeed = to_int(up); node.DownSpeed = to_int(down); node.Ports = ports; @@ -342,11 +346,13 @@ void vlessConstruct( const std::string &xtls, const std::string &public_key, const std::string &short_id, + const std::string &client_fingerprint, + tribool udp, tribool tfo, tribool scv, const std::string &underlying_proxy ) { - commonConstruct(node, ProxyType::VLESS, group, remarks, server, port, tribool(), tfo, scv, tribool(), underlying_proxy); + commonConstruct(node, ProxyType::VLESS, group, remarks, server, port, udp, tfo, scv, tribool(), underlying_proxy); node.UUID = uuid; node.SNI = sni; node.TransferProtocol = net.empty() ? "tcp" : net; @@ -355,15 +361,19 @@ void vlessConstruct( case "grpc"_hash: node.Host = host; node.GRPCMode = mode.empty() ? "gun" : mode; - node.GRPCServiceName = path.empty() ? "/" : urlEncode(urlDecode(trim(path))); + if (!path.empty()) + node.GRPCServiceName = urlEncode(urlDecode(trim(path))); break; case "quic"_hash: node.QUICSecure = host; - node.QUICSecret = path.empty() ? "/" : trim(path); + if (!path.empty()) + node.QUICSecret = trim(path); break; default: - node.Host = (host.empty() && !isIPv4(server) && !isIPv6(server)) ? server.data() : trim(host); - node.Path = path.empty() ? "/" : urlDecode(trim(path)); + if (!host.empty()) + node.Host = trim(host); + if (!path.empty()) + node.Path = urlDecode(trim(path)); break; } if (!alpn.empty()) { @@ -371,6 +381,7 @@ void vlessConstruct( } node.Fingerprint = fingerprint; node.Flow = flow; + node.ClientFingerprint = client_fingerprint; node.XTLS = to_int(xtls); node.PublicKey = public_key; node.ShortID = short_id; @@ -1236,7 +1247,13 @@ void explodeClash(Node yamlnode, std::vector &nodes) std::string uuid, heartbeat_interval, disable_sni, reduce_rtt, request_timeout, udp_relay_mode, congestion_controller, max_udp_relay_packet_size, max_open_streams, fast_open; //TUIC std::string idle_session_check_interval, idle_session_timeout, min_idle_session; std::string flow, xtls, short_id; - string_array dns_server; + // New parameters from mihomo + std::string ip_version, client_fingerprint, ech_config, certificate, private_key_pem, vless_encryption; + std::string smux_protocol, ws_early_data_header_name, http_method, trojan_ss_method, trojan_ss_password; + std::string ws_max_early_data; + tribool ech_enable, smux_enabled, smux_padding, smux_statistic, smux_only_tcp, v2ray_http_upgrade, v2ray_http_upgrade_fast_open; + uint32_t smux_max_connections = 0, smux_min_streams = 0, smux_max_streams = 0; + string_array dns_server, http_path; tribool udp, tfo, scv; singleproxy["type"] >>= proxytype; singleproxy["name"] >>= ps; @@ -1248,6 +1265,18 @@ void explodeClash(Node yamlnode, std::vector &nodes) udp = safe_as(singleproxy["udp"]); tfo = safe_as(singleproxy["fast-open"]); scv = safe_as(singleproxy["skip-cert-verify"]); + + // Read common new parameters + singleproxy["ip-version"] >>= ip_version; + singleproxy["client-fingerprint"] >>= client_fingerprint; + singleproxy["certificate"] >>= certificate; + singleproxy["private-key"] >>= private_key_pem; + if(singleproxy["ech-opts"].IsDefined()) + { + ech_enable = safe_as(singleproxy["ech-opts"]["enable"]); + singleproxy["ech-opts"]["config"] >>= ech_config; + } + switch(hash_(proxytype)) { case "vmess"_hash: @@ -1293,6 +1322,25 @@ void explodeClash(Node yamlnode, std::vector &nodes) tls = safe_as(singleproxy["tls"]) == "true" ? "tls" : ""; vmessConstruct(node, group, ps, server, port, "", id, aid, net, cipher, path, host, edge, tls, sni, udp, tfo, scv, tribool(), underlying_proxy); + + // Assign new parameters to node for Vmess + node.IpVersion = ip_version; + node.ClientFingerprint = client_fingerprint; + node.EchEnable = ech_enable; + node.EchConfig = ech_config; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; + if(singleproxy["ws-opts"].IsDefined()) + { + singleproxy["ws-opts"]["max-early-data"] >>= ws_max_early_data; + singleproxy["ws-opts"]["early-data-header-name"] >>= ws_early_data_header_name; + } + v2ray_http_upgrade = safe_as(singleproxy["v2ray-http-upgrade"]); + v2ray_http_upgrade_fast_open = safe_as(singleproxy["v2ray-http-upgrade-fast-open"]); + node.WsMaxEarlyData = to_int(ws_max_early_data); + node.WsEarlyDataHeaderName = ws_early_data_header_name; + node.V2rayHttpUpgrade = v2ray_http_upgrade; + node.V2rayHttpUpgradeFastOpen = v2ray_http_upgrade_fast_open; break; case "vless"_hash: { group = VLESS_DEFAULT_GROUP; @@ -1305,6 +1353,8 @@ void explodeClash(Node yamlnode, std::vector &nodes) singleproxy["alpn"] >>= alpn; singleproxy["fingerprint"] >>= fingerprint; singleproxy["flow"] >>= flow; + if (singleproxy["flow"].IsDefined()) + node.FlowSet = true; if (singleproxy["reality-opts"].IsDefined()) { singleproxy["reality-opts"]["public-key"] >>= public_key; singleproxy["reality-opts"]["short-id"] >>= short_id; @@ -1314,17 +1364,20 @@ void explodeClash(Node yamlnode, std::vector &nodes) case "ws"_hash: if (singleproxy["ws-opts"].IsDefined()) { - path = singleproxy["ws-opts"]["path"].IsDefined() ? safe_as(singleproxy["ws-opts"]["path"]) : "/"; + if (singleproxy["ws-opts"]["path"].IsDefined()) + path = safe_as(singleproxy["ws-opts"]["path"]); singleproxy["ws-opts"]["headers"]["Host"] >>= host; } else { - path = singleproxy["ws-path"].IsDefined() ? safe_as(singleproxy["ws-path"]) : "/"; + if (singleproxy["ws-path"].IsDefined()) + path = safe_as(singleproxy["ws-path"]); singleproxy["ws-headers"]["Host"] >>= host; } break; case "http"_hash: - path = singleproxy["http-opts"]["path"][0].IsDefined() ? safe_as(singleproxy["http-opts"]["path"][0]) : "/"; + if (singleproxy["http-opts"]["path"][0].IsDefined()) + path = safe_as(singleproxy["http-opts"]["path"][0]); if (singleproxy["http-opts"]["headers"]["Host"][0].IsDefined()) singleproxy["http-opts"]["headers"]["Host"][0] >>= host; break; @@ -1346,7 +1399,41 @@ void explodeClash(Node yamlnode, std::vector &nodes) singleproxy["path"] >>= path; break; } - vlessConstruct(node, group, ps, server, port, uuid, sni, alpn, type, net, mode, host, path, fingerprint, flow, xtls, public_key, short_id, tfo, scv, underlying_proxy); + vlessConstruct(node, group, ps, server, port, uuid, sni, alpn, type, net, mode, host, path, fingerprint, flow, xtls, public_key, short_id, client_fingerprint, udp, tfo, scv, underlying_proxy); + + // Assign new parameters to node for VLESS + node.IpVersion = ip_version; + node.EchEnable = ech_enable; + node.EchConfig = ech_config; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; + singleproxy["encryption"] >>= vless_encryption; + node.VlessEncryption = vless_encryption; + // packet-encoding and xudp support - only assign if explicitly provided + if(singleproxy["packet-encoding"].IsDefined()) + { + std::string packet_encoding; + singleproxy["packet-encoding"] >>= packet_encoding; + node.PacketEncoding = packet_encoding; + } + node.XUDP = safe_as(singleproxy["xudp"]); + node.PacketAddr = safe_as(singleproxy["packet-addr"]); + + // Add WebSocket enhanced parameters support for VLESS + if(net == "ws") + { + if(singleproxy["ws-opts"].IsDefined()) + { + singleproxy["ws-opts"]["max-early-data"] >>= ws_max_early_data; + singleproxy["ws-opts"]["early-data-header-name"] >>= ws_early_data_header_name; + node.WsMaxEarlyData = to_int(ws_max_early_data); + node.WsEarlyDataHeaderName = ws_early_data_header_name; + } + v2ray_http_upgrade = safe_as(singleproxy["v2ray-http-upgrade"]); + v2ray_http_upgrade_fast_open = safe_as(singleproxy["v2ray-http-upgrade-fast-open"]); + node.V2rayHttpUpgrade = v2ray_http_upgrade; + node.V2rayHttpUpgradeFastOpen = v2ray_http_upgrade_fast_open; + } break; } case "ss"_hash: @@ -1354,6 +1441,28 @@ void explodeClash(Node yamlnode, std::vector &nodes) singleproxy["cipher"] >>= cipher; singleproxy["password"] >>= password; + + // Shadowsocks UDP-over-TCP options - only assign if explicitly provided + node.UdpOverTcp = safe_as(singleproxy["udp-over-tcp"]); + if(singleproxy["udp-over-tcp-version"].IsDefined()) + node.UdpOverTcpVersion = to_int(safe_as(singleproxy["udp-over-tcp-version"])); + + // Read SMUX configuration for Shadowsocks + if(singleproxy["smux"].IsDefined()) + { + smux_enabled = safe_as(singleproxy["smux"]["enabled"]); + singleproxy["smux"]["protocol"] >>= smux_protocol; + if(singleproxy["smux"]["max-connections"].IsDefined()) + smux_max_connections = to_int(safe_as(singleproxy["smux"]["max-connections"])); + if(singleproxy["smux"]["min-streams"].IsDefined()) + smux_min_streams = to_int(safe_as(singleproxy["smux"]["min-streams"])); + if(singleproxy["smux"]["max-streams"].IsDefined()) + smux_max_streams = to_int(safe_as(singleproxy["smux"]["max-streams"])); + smux_padding = safe_as(singleproxy["smux"]["padding"]); + smux_statistic = safe_as(singleproxy["smux"]["statistic"]); + smux_only_tcp = safe_as(singleproxy["smux"]["only-tcp"]); + } + if(singleproxy["plugin"].IsDefined()) { switch(hash_(safe_as(singleproxy["plugin"]))) @@ -1418,6 +1527,22 @@ void explodeClash(Node yamlnode, std::vector &nodes) } ssConstruct(node, group, ps, server, port, password, cipher, plugin, pluginopts, udp, tfo, scv, tribool(), underlying_proxy); + + // Assign new parameters to node + node.IpVersion = ip_version; + node.ClientFingerprint = client_fingerprint; + node.EchEnable = ech_enable; + node.EchConfig = ech_config; + node.SmuxEnabled = smux_enabled; + node.SmuxProtocol = smux_protocol; + node.SmuxMaxConnections = smux_max_connections; + node.SmuxMinStreams = smux_min_streams; + node.SmuxMaxStreams = smux_max_streams; + node.SmuxPadding = smux_padding; + node.SmuxStatistic = smux_statistic; + node.SmuxOnlyTcp = smux_only_tcp; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; break; case "socks5"_hash: group = SOCKS_DEFAULT_GROUP; @@ -1454,6 +1579,12 @@ void explodeClash(Node yamlnode, std::vector &nodes) singleproxy["tls"] >>= tls; httpConstruct(node, group, ps, server, port, user, password, tls == "true", tfo, scv, tribool(), underlying_proxy); + + // Assign new parameters to node for HTTP + node.IpVersion = ip_version; + node.ClientFingerprint = client_fingerprint; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; break; case "trojan"_hash: group = TROJAN_DEFAULT_GROUP; @@ -1475,6 +1606,37 @@ void explodeClash(Node yamlnode, std::vector &nodes) } trojanConstruct(node, group, ps, server, port, password, net, host, path, true, udp, tfo, scv, tribool(), underlying_proxy); + + // Assign new parameters to node for Trojan + node.IpVersion = ip_version; + node.ClientFingerprint = client_fingerprint; + node.EchEnable = ech_enable; + node.EchConfig = ech_config; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; + if(singleproxy["ss-opts"].IsDefined()) + { + singleproxy["ss-opts"]["method"] >>= trojan_ss_method; + singleproxy["ss-opts"]["password"] >>= trojan_ss_password; + node.TrojanSsMethod = trojan_ss_method; + node.TrojanSsPassword = trojan_ss_password; + } + + // Add WebSocket enhanced parameters support for Trojan + if(net == "ws") + { + if(singleproxy["ws-opts"].IsDefined()) + { + singleproxy["ws-opts"]["max-early-data"] >>= ws_max_early_data; + singleproxy["ws-opts"]["early-data-header-name"] >>= ws_early_data_header_name; + node.WsMaxEarlyData = to_int(ws_max_early_data); + node.WsEarlyDataHeaderName = ws_early_data_header_name; + } + v2ray_http_upgrade = safe_as(singleproxy["v2ray-http-upgrade"]); + v2ray_http_upgrade_fast_open = safe_as(singleproxy["v2ray-http-upgrade-fast-open"]); + node.V2rayHttpUpgrade = v2ray_http_upgrade; + node.V2rayHttpUpgradeFastOpen = v2ray_http_upgrade_fast_open; + } break; case "snell"_hash: group = SNELL_DEFAULT_GROUP; @@ -1527,10 +1689,20 @@ void explodeClash(Node yamlnode, std::vector &nodes) singleproxy["hop-interval"] >>= hop_interval; hysteriaConstruct(node, group, ps, server, port, ports, protocol, obfs_protocol, up, up_speed, down, down_speed, auth, auth_str, obfs, sni, fingerprint, ca, ca_str, recv_window_conn, recv_window, disable_mtu_discovery, hop_interval, alpn, tfo, scv, underlying_proxy); + + // Assign new parameters to node for Hysteria + node.IpVersion = ip_version; + node.ClientFingerprint = client_fingerprint; + node.EchEnable = ech_enable; + node.EchConfig = ech_config; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; + node.FastOpen = safe_as(singleproxy["fast-open"]); break; case "hysteria2"_hash: group = HYSTERIA2_DEFAULT_GROUP; singleproxy["ports"] >>= ports; + singleproxy["mport"] >>= node.Mport; singleproxy["up"] >>= up; singleproxy["down"] >>= down; singleproxy["password"] >>= password; @@ -1548,8 +1720,26 @@ void explodeClash(Node yamlnode, std::vector &nodes) singleproxy["ca-str"] >>= ca_str; singleproxy["cwnd"] >>= cwnd; singleproxy["hop-interval"] >>= hop_interval; - - hysteria2Construct(node, group, ps, server, port, ports, up, down, password, obfs, obfs_password, sni, fingerprint, alpn, ca, ca_str, cwnd, hop_interval, tfo, scv, underlying_proxy); + hysteria2Construct(node, group, ps, server, port, ports, up, down, password, obfs, obfs_password, sni, fingerprint, alpn, ca, ca_str, cwnd, hop_interval, udp, tfo, scv, underlying_proxy); + + // Assign new parameters to node for Hysteria2 + node.IpVersion = ip_version; + node.ClientFingerprint = client_fingerprint; + node.EchEnable = ech_enable; + node.EchConfig = ech_config; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; + // Hysteria2 additional windows and udp mtu - only assign if explicitly provided + if(singleproxy["initial-stream-receive-window"].IsDefined()) + node.InitialStreamReceiveWindow = to_int(safe_as(singleproxy["initial-stream-receive-window"])); + if(singleproxy["max-stream-receive-window"].IsDefined()) + node.MaxStreamReceiveWindow = to_int(safe_as(singleproxy["max-stream-receive-window"])); + if(singleproxy["initial-connection-receive-window"].IsDefined()) + node.InitialConnectionReceiveWindow = to_int(safe_as(singleproxy["initial-connection-receive-window"])); + if(singleproxy["max-connection-receive-window"].IsDefined()) + node.MaxConnectionReceiveWindow = to_int(safe_as(singleproxy["max-connection-receive-window"])); + if(singleproxy["udp-mtu"].IsDefined()) + node.UdpMTU = to_int(safe_as(singleproxy["udp-mtu"])); break; case "tuic"_hash: group = TUIC_DEFAULT_GROUP; @@ -1570,6 +1760,18 @@ void explodeClash(Node yamlnode, std::vector &nodes) singleproxy["max-open-streams"] >>= max_open_streams; singleproxy["fast-open"] >>= fast_open; tuicConstruct(node, group, ps, server, port, uuid, password, ip, heartbeat_interval, alpn, disable_sni, reduce_rtt, request_timeout, udp_relay_mode, congestion_controller, max_udp_relay_packet_size, max_open_streams, sni, fast_open, tfo, scv, underlying_proxy); + + // Assign new parameters to node for TUIC + node.IpVersion = ip_version; + node.ClientFingerprint = client_fingerprint; + node.EchEnable = ech_enable; + node.EchConfig = ech_config; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; + node.SNI = sni; // Ensure SNI is set + // max-datagram-frame-size - only assign if explicitly provided + if(singleproxy["max-datagram-frame-size"].IsDefined()) + node.MaxDatagramFrameSize = to_int(safe_as(singleproxy["max-datagram-frame-size"])); break; case "anytls"_hash: { group = ANYTLS_DEFAULT_GROUP; @@ -1581,6 +1783,12 @@ void explodeClash(Node yamlnode, std::vector &nodes) singleproxy["alpn"] >>= alpn; singleproxy["fingerprint"] >>= fingerprint; anytlsConstruct(node, group, ps, server, port, password, sni, alpn, fingerprint, idle_session_check_interval, idle_session_timeout, min_idle_session, tfo, scv, underlying_proxy); + + // Assign new parameters to node for AnyTLS + node.IpVersion = ip_version; + node.ClientFingerprint = client_fingerprint; + node.Certificate = certificate; + node.PrivateKeyPem = private_key_pem; break; } default: @@ -1766,7 +1974,7 @@ void explodeStdHysteria2(std::string hysteria2, Proxy &node) { if (remarks.empty()) remarks = add + ":" + port; - hysteria2Construct(node, HYSTERIA2_DEFAULT_GROUP, remarks, add, port, port, up, down, password, obfs, obfs_password, sni, fingerprint, "", "", "", "", "", tribool(), scv, ""); + hysteria2Construct(node, HYSTERIA2_DEFAULT_GROUP, remarks, add, port, port, up, down, password, obfs, obfs_password, sni, fingerprint, "", "", "", "", "", tribool(), tribool(), scv, ""); return; } @@ -2010,7 +2218,7 @@ void explodeStdVLESS(std::string vless, Proxy &node) { if (remarks.empty()) remarks = add + ":" + port; - vlessConstruct(node, VLESS_DEFAULT_GROUP, remarks, add, port, uuid, sni, alpn, type, net, mode, host, path, fingerprint, flow, xtls, public_key, short_id, tfo, scv, ""); + vlessConstruct(node, VLESS_DEFAULT_GROUP, remarks, add, port, uuid, sni, alpn, type, net, mode, host, path, fingerprint, flow, xtls, public_key, short_id, "", tribool(), tfo, scv, ""); } void explodeVLESS(std::string vless, Proxy &node) { diff --git a/src/parser/subparser.h b/src/parser/subparser.h index 0f7c9dd988..6273a4b0cf 100644 --- a/src/parser/subparser.h +++ b/src/parser/subparser.h @@ -77,7 +77,8 @@ void hysteria2Construct( const std::string &ca, const std::string &caStr, const std::string &cwnd, - const std::string &hop_interval, + const std::string &hop_interval, + tribool udp, tribool tfo, tribool scv, const std::string &underlying_proxy = "" @@ -145,6 +146,8 @@ void vlessConstruct( const std::string &xtls, const std::string &public_key, const std::string &short_id, + const std::string &client_fingerprint, + tribool udp, tribool tfo, tribool scv, const std::string &underlying_proxy = ""