Skip to content

Commit c2235ca

Browse files
committed
fix: фильтрация xhttp аутбаундов вместо конвертации в Xray-core
Убрана конвертация xhttp→Xray через SOCKS-мост (вызывала ошибки при выборе прокси). Теперь xhttp/splithttp аутбаунды просто фильтруются наряду с naive и shadowsocksr — остальные прокси работают нормально.
1 parent aaa5e0b commit c2235ca

File tree

2 files changed

+15
-201
lines changed

2 files changed

+15
-201
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
### Исправлено / Fixed
1212

13-
- **Ошибка добавления подписок с xhttp транспортом** — Hiddify Manager `/singbox` эндпоинт возвращает аутбаунды (vless/vmess/trojan) с `transport.type: "xhttp"`, которые sing-box не поддерживает. Теперь такие аутбаунды автоматически конвертируются в Xray-core формат и направляются через SOCKS-мост, как и остальные xhttp-прокси
14-
- **Failed to add subscriptions with xhttp transport** — Hiddify Manager `/singbox` endpoint returns outbounds (vless/vmess/trojan) with `transport.type: "xhttp"` which sing-box doesn't support. These outbounds are now automatically converted to Xray-core format and routed through the SOCKS bridge, like other xhttp proxies
13+
- **Ошибка добавления подписок с xhttp транспортом** — Hiddify Manager `/singbox` эндпоинт возвращает аутбаунды (vless/vmess/trojan) с `transport.type: "xhttp"`, которые sing-box не поддерживает. Теперь такие аутбаунды фильтруются, остальные прокси работают нормально
14+
- **Failed to add subscriptions with xhttp transport** — Hiddify Manager `/singbox` endpoint returns outbounds (vless/vmess/trojan) with `transport.type: "xhttp"` which sing-box doesn't support. These outbounds are now filtered out, remaining proxies work normally
1515
- **Фильтрация неподдерживаемых аутбаундов** — убраны `naive` (только inbound в sing-box) и `shadowsocksr` (deprecated с sing-box 1.6.0) из списка поддерживаемых типов, теперь они корректно фильтруются вместо ошибки валидации
1616
- **Filter unsupported outbound types** — removed `naive` (inbound-only in sing-box) and `shadowsocksr` (deprecated since sing-box 1.6.0) from supported types list, they are now properly filtered instead of causing validation errors
1717

libcore/config/parser.go

Lines changed: 13 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,18 @@ func filterUnsupportedOutbounds(jsonObj map[string]interface{}) ([]string, int)
685685
continue
686686
}
687687

688+
// Check transport type — filter xhttp/splithttp (not supported by sing-box)
688689
if supportedOutboundTypes[outboundType] {
690+
if transport, ok := outboundMap["transport"].(map[string]interface{}); ok {
691+
if transportType, _ := transport["type"].(string); transportType == "xhttp" || transportType == "splithttp" {
692+
removedTypes = append(removedTypes, outboundType+"/"+transportType)
693+
removedCount++
694+
if tag, ok := outboundMap["tag"].(string); ok {
695+
removedTags = append(removedTags, tag)
696+
}
697+
continue
698+
}
699+
}
689700
filteredOutbounds = append(filteredOutbounds, outbound)
690701
} else {
691702
removedTypes = append(removedTypes, outboundType)
@@ -747,171 +758,9 @@ func filterUnsupportedOutbounds(jsonObj map[string]interface{}) ([]string, int)
747758
// Must match the value in xray/core_selector.go and ray2sing/convert.go
748759
const XrayFixedPort uint16 = 12380
749760

750-
// hasXHTTPTransport checks if a sing-box outbound uses xhttp/splithttp transport
751-
func hasXHTTPTransport(outboundMap map[string]interface{}) bool {
752-
transport, ok := outboundMap["transport"].(map[string]interface{})
753-
if !ok {
754-
return false
755-
}
756-
transportType, _ := transport["type"].(string)
757-
return transportType == "xhttp" || transportType == "splithttp"
758-
}
759-
760-
// singboxOutboundToXrayRaw converts a sing-box JSON outbound with xhttp transport
761-
// to an Xray-core raw outbound format for use with StartXrayForRawOutbound
762-
func singboxOutboundToXrayRaw(outboundMap map[string]interface{}, debug bool) map[string]interface{} {
763-
outboundType, _ := outboundMap["type"].(string)
764-
tag, _ := outboundMap["tag"].(string)
765-
server, _ := outboundMap["server"].(string)
766-
767-
// server_port can be float64 (JSON number) or int
768-
var serverPort float64
769-
switch p := outboundMap["server_port"].(type) {
770-
case float64:
771-
serverPort = p
772-
case int:
773-
serverPort = float64(p)
774-
}
775-
776-
// Map sing-box type to Xray protocol
777-
protocol := outboundType // vless, vmess, trojan
778-
if protocol == "shadowsocks" {
779-
protocol = "shadowsocks" // same name
780-
}
781-
782-
// Build Xray outbound
783-
xrayOutbound := map[string]interface{}{
784-
"tag": tag,
785-
"protocol": protocol,
786-
}
787-
788-
// Build protocol-specific settings
789-
switch outboundType {
790-
case "vless":
791-
uuid, _ := outboundMap["uuid"].(string)
792-
flow, _ := outboundMap["flow"].(string)
793-
user := map[string]interface{}{
794-
"id": uuid,
795-
"encryption": "none",
796-
}
797-
if flow != "" {
798-
user["flow"] = flow
799-
}
800-
xrayOutbound["settings"] = map[string]interface{}{
801-
"vnext": []interface{}{
802-
map[string]interface{}{
803-
"address": server,
804-
"port": serverPort,
805-
"users": []interface{}{user},
806-
},
807-
},
808-
}
809-
case "vmess":
810-
uuid, _ := outboundMap["uuid"].(string)
811-
xrayOutbound["settings"] = map[string]interface{}{
812-
"vnext": []interface{}{
813-
map[string]interface{}{
814-
"address": server,
815-
"port": serverPort,
816-
"users": []interface{}{
817-
map[string]interface{}{
818-
"id": uuid,
819-
"security": "auto",
820-
},
821-
},
822-
},
823-
},
824-
}
825-
case "trojan":
826-
password, _ := outboundMap["password"].(string)
827-
xrayOutbound["settings"] = map[string]interface{}{
828-
"servers": []interface{}{
829-
map[string]interface{}{
830-
"address": server,
831-
"port": serverPort,
832-
"password": password,
833-
},
834-
},
835-
}
836-
}
837-
838-
// Build streamSettings
839-
streamSettings := map[string]interface{}{
840-
"network": "xhttp",
841-
}
842-
843-
// TLS settings
844-
if tlsObj, ok := outboundMap["tls"].(map[string]interface{}); ok {
845-
enabled, _ := tlsObj["enabled"].(bool)
846-
if enabled {
847-
streamSettings["security"] = "tls"
848-
tlsSettings := map[string]interface{}{}
849-
if sni, ok := tlsObj["server_name"].(string); ok && sni != "" {
850-
tlsSettings["serverName"] = sni
851-
}
852-
if insecure, ok := tlsObj["insecure"].(bool); ok {
853-
tlsSettings["allowInsecure"] = insecure
854-
}
855-
if alpn, ok := tlsObj["alpn"].([]interface{}); ok {
856-
var alpnStrings []string
857-
for _, a := range alpn {
858-
if s, ok := a.(string); ok {
859-
alpnStrings = append(alpnStrings, s)
860-
}
861-
}
862-
if len(alpnStrings) > 0 {
863-
tlsSettings["alpn"] = alpnStrings
864-
}
865-
}
866-
// Use utls fingerprint if available
867-
if utls, ok := tlsObj["utls"].(map[string]interface{}); ok {
868-
if fp, ok := utls["fingerprint"].(string); ok && fp != "" {
869-
tlsSettings["fingerprint"] = fp
870-
}
871-
}
872-
if len(tlsSettings) > 0 {
873-
streamSettings["tlsSettings"] = tlsSettings
874-
}
875-
}
876-
}
877-
878-
// Transport (xhttp) settings
879-
if transport, ok := outboundMap["transport"].(map[string]interface{}); ok {
880-
xhttpSettings := map[string]interface{}{}
881-
if path, ok := transport["path"].(string); ok && path != "" {
882-
xhttpSettings["path"] = path
883-
}
884-
// host can be string or []string in sing-box
885-
if host, ok := transport["host"].(string); ok && host != "" {
886-
xhttpSettings["host"] = host
887-
} else if hosts, ok := transport["host"].([]interface{}); ok && len(hosts) > 0 {
888-
if h, ok := hosts[0].(string); ok {
889-
xhttpSettings["host"] = h
890-
}
891-
}
892-
if mode, ok := transport["mode"].(string); ok && mode != "" {
893-
xhttpSettings["mode"] = mode
894-
}
895-
if len(xhttpSettings) > 0 {
896-
streamSettings["xhttpSettings"] = xhttpSettings
897-
}
898-
}
899-
900-
xrayOutbound["streamSettings"] = streamSettings
901-
902-
if debug {
903-
fmt.Printf("[SingboxParser] Converted sing-box %s outbound to Xray raw: tag=%s, server=%s:%v\n",
904-
outboundType, tag, server, serverPort)
905-
}
906-
907-
return xrayOutbound
908-
}
909-
910-
// convertXrayOutboundsToSocks finds outbounds with type "xray" or xhttp transport and converts them to socks outbounds
761+
// convertXrayOutboundsToSocks finds outbounds with type "xray" and converts them to socks outbounds
911762
// Returns the xray links and info for later Xray startup
912-
// Supports:
913-
// 1. type "xray" with "link" field (vless://...) or "xray_outbound_raw" (ready Xray JSON from Hiddify Manager)
914-
// 2. Regular outbounds (vless/vmess/trojan) with transport.type "xhttp"/"splithttp" — converted to Xray raw format
763+
// Supports type "xray" with "link" field (vless://...) or "xray_outbound_raw" (ready Xray JSON from Hiddify Manager)
915764
func convertXrayOutboundsToSocks(jsonObj map[string]interface{}, debug bool) ([]string, []XrayProxyInfo) {
916765
outbounds, ok := jsonObj["outbounds"].([]interface{})
917766
if !ok {
@@ -983,41 +832,6 @@ func convertXrayOutboundsToSocks(jsonObj map[string]interface{}, debug bool) ([]
983832
continue
984833
}
985834

986-
// Case 2: Regular outbound (vless/vmess/trojan) with xhttp/splithttp transport
987-
// Hiddify Manager /singbox endpoint with sing-box UA returns these
988-
if hasXHTTPTransport(outboundMap) {
989-
originalTag, _ := outboundMap["tag"].(string)
990-
newTag := fmt.Sprintf("%s [xray:%d]", originalTag, xrayIndex)
991-
992-
// Convert sing-box outbound to Xray raw format
993-
xrayRaw := singboxOutboundToXrayRaw(outboundMap, debug)
994-
995-
socksOutbound := map[string]interface{}{
996-
"type": "socks",
997-
"tag": newTag,
998-
"server": "127.0.0.1",
999-
"server_port": XrayFixedPort,
1000-
}
1001-
1002-
if debug {
1003-
fmt.Printf("[SingboxParser] Converting xhttp outbound to Xray socks: %s -> %s\n", originalTag, newTag)
1004-
}
1005-
1006-
info := XrayProxyInfo{
1007-
Tag: newTag,
1008-
Link: "", // No link, using raw outbound
1009-
Index: xrayIndex,
1010-
ProxyName: originalTag,
1011-
RawOutbound: xrayRaw,
1012-
}
1013-
1014-
xrayLinks = append(xrayLinks, "") // Placeholder for backward compatibility
1015-
xrayInfos = append(xrayInfos, info)
1016-
newOutbounds = append(newOutbounds, socksOutbound)
1017-
xrayIndex++
1018-
continue
1019-
}
1020-
1021835
// Regular outbound — pass through
1022836
newOutbounds = append(newOutbounds, outbound)
1023837
}

0 commit comments

Comments
 (0)