mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-04-16 20:45:50 +00:00
1. **Fixed XPadding Placement Dropdown**: - Added the missing `cookie` and `query` options to `xPaddingPlacement` (`stream_xhttp.html`). - *Why:* Previously, users wanting `cookie` obfuscation were forced to use the `header` placement string. This caused Xray-core to blindly intercept the entire monolithic HTTP Cookie header, failing internal padding-length validations and causing the inbound to silently drop the connection. 2. **Fixed Uplink Data Placement Validation**: - Replaced the unsupported `query` option with `cookie` in `uplinkDataPlacement`. - *Why:* Xray-core's `transport_internet.go` explicitly forbids `query` as an uplink placement option. Selecting it from the UI previously sent a payload that would cause Xray-core to instantly throw an `unsupported uplink data placement: query` panic. Adding `cookie` perfectly aligns the UI with Xray-core restrictions. ### Related Issues - Resolves #3992
119 lines
4.2 KiB
Go
119 lines
4.2 KiB
Go
package command
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"github.com/xtls/xray-core/common/net"
|
|
"github.com/xtls/xray-core/features/routing"
|
|
)
|
|
|
|
// routingContext is an wrapper of protobuf RoutingContext as implementation of routing.Context and routing.Route.
|
|
type routingContext struct {
|
|
*RoutingContext
|
|
}
|
|
|
|
func (c routingContext) GetSourceIPs() []net.IP {
|
|
return mapBytesToIPs(c.RoutingContext.GetSourceIPs())
|
|
}
|
|
|
|
func (c routingContext) GetSourcePort() net.Port {
|
|
return net.Port(c.RoutingContext.GetSourcePort())
|
|
}
|
|
|
|
func (c routingContext) GetTargetIPs() []net.IP {
|
|
return mapBytesToIPs(c.RoutingContext.GetTargetIPs())
|
|
}
|
|
|
|
func (c routingContext) GetTargetPort() net.Port {
|
|
return net.Port(c.RoutingContext.GetTargetPort())
|
|
}
|
|
|
|
func (c routingContext) GetLocalIPs() []net.IP {
|
|
return mapBytesToIPs(c.RoutingContext.GetLocalIPs())
|
|
}
|
|
|
|
func (c routingContext) GetLocalPort() net.Port {
|
|
return net.Port(c.RoutingContext.GetLocalPort())
|
|
}
|
|
|
|
func (c routingContext) GetVlessRoute() net.Port {
|
|
return net.Port(c.RoutingContext.GetVlessRoute())
|
|
}
|
|
|
|
func (c routingContext) GetRuleTag() string {
|
|
return ""
|
|
}
|
|
|
|
// GetSkipDNSResolve is a mock implementation here to match the interface,
|
|
// SkipDNSResolve is set from dns module, no use if coming from a protobuf object?
|
|
// TODO: please confirm @Vigilans
|
|
func (c routingContext) GetSkipDNSResolve() bool {
|
|
return false
|
|
}
|
|
|
|
// AsRoutingContext converts a protobuf RoutingContext into an implementation of routing.Context.
|
|
func AsRoutingContext(r *RoutingContext) routing.Context {
|
|
return routingContext{r}
|
|
}
|
|
|
|
// AsRoutingRoute converts a protobuf RoutingContext into an implementation of routing.Route.
|
|
func AsRoutingRoute(r *RoutingContext) routing.Route {
|
|
return routingContext{r}
|
|
}
|
|
|
|
var fieldMap = map[string]func(*RoutingContext, routing.Route){
|
|
"inbound": func(s *RoutingContext, r routing.Route) { s.InboundTag = r.GetInboundTag() },
|
|
"network": func(s *RoutingContext, r routing.Route) { s.Network = r.GetNetwork() },
|
|
"ip_source": func(s *RoutingContext, r routing.Route) { s.SourceIPs = mapIPsToBytes(r.GetSourceIPs()) },
|
|
"ip_target": func(s *RoutingContext, r routing.Route) { s.TargetIPs = mapIPsToBytes(r.GetTargetIPs()) },
|
|
"ip_local": func(s *RoutingContext, r routing.Route) { s.LocalIPs = mapIPsToBytes(r.GetLocalIPs()) },
|
|
"port_source": func(s *RoutingContext, r routing.Route) { s.SourcePort = uint32(r.GetSourcePort()) },
|
|
"port_target": func(s *RoutingContext, r routing.Route) { s.TargetPort = uint32(r.GetTargetPort()) },
|
|
"port_local": func(s *RoutingContext, r routing.Route) { s.LocalPort = uint32(r.GetLocalPort()) },
|
|
"domain": func(s *RoutingContext, r routing.Route) { s.TargetDomain = r.GetTargetDomain() },
|
|
"protocol": func(s *RoutingContext, r routing.Route) { s.Protocol = r.GetProtocol() },
|
|
"user": func(s *RoutingContext, r routing.Route) { s.User = r.GetUser() },
|
|
"attributes": func(s *RoutingContext, r routing.Route) { s.Attributes = r.GetAttributes() },
|
|
"outbound_group": func(s *RoutingContext, r routing.Route) { s.OutboundGroupTags = r.GetOutboundGroupTags() },
|
|
"outbound": func(s *RoutingContext, r routing.Route) { s.OutboundTag = r.GetOutboundTag() },
|
|
}
|
|
|
|
// AsProtobufMessage takes selectors of fields and returns a function to convert routing.Route to protobuf RoutingContext.
|
|
func AsProtobufMessage(fieldSelectors []string) func(routing.Route) *RoutingContext {
|
|
initializers := []func(*RoutingContext, routing.Route){}
|
|
for field, init := range fieldMap {
|
|
if len(fieldSelectors) == 0 { // If selectors not set, retrieve all fields
|
|
initializers = append(initializers, init)
|
|
continue
|
|
}
|
|
for _, selector := range fieldSelectors {
|
|
if strings.HasPrefix(field, selector) {
|
|
initializers = append(initializers, init)
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return func(ctx routing.Route) *RoutingContext {
|
|
message := new(RoutingContext)
|
|
for _, init := range initializers {
|
|
init(message, ctx)
|
|
}
|
|
return message
|
|
}
|
|
}
|
|
|
|
func mapBytesToIPs(bytes [][]byte) []net.IP {
|
|
var ips []net.IP
|
|
for _, rawIP := range bytes {
|
|
ips = append(ips, net.IP(rawIP))
|
|
}
|
|
return ips
|
|
}
|
|
|
|
func mapIPsToBytes(ips []net.IP) [][]byte {
|
|
var bytes [][]byte
|
|
for _, ip := range ips {
|
|
bytes = append(bytes, []byte(ip))
|
|
}
|
|
return bytes
|
|
}
|