diff --git a/proxy/proxy.go b/proxy/proxy.go index 5cb9d7a11..2e5b4780f 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -180,14 +180,14 @@ type VisionReader struct { isUplink bool conn net.Conn input *bytes.Reader - rawInput *bytes.Buffer + rawInput **bytes.Buffer ob *session.Outbound // internal directReadCounter stats.Counter } -func NewVisionReader(reader buf.Reader, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, input *bytes.Reader, rawInput *bytes.Buffer, ob *session.Outbound) *VisionReader { +func NewVisionReader(reader buf.Reader, trafficState *TrafficState, isUplink bool, ctx context.Context, conn net.Conn, input *bytes.Reader, rawInput **bytes.Buffer, ob *session.Outbound) *VisionReader { return &VisionReader{ Reader: reader, trafficState: trafficState, @@ -261,12 +261,15 @@ func (w *VisionReader) ReadMultiBuffer() (buf.MultiBuffer, error) { if inputBuffer, err := buf.ReadFrom(w.input); err == nil && !inputBuffer.IsEmpty() { buffer, _ = buf.MergeMulti(buffer, inputBuffer) } - if rawInputBuffer, err := buf.ReadFrom(w.rawInput); err == nil && !rawInputBuffer.IsEmpty() { - buffer, _ = buf.MergeMulti(buffer, rawInputBuffer) - } *w.input = bytes.Reader{} // release memory w.input = nil - *w.rawInput = bytes.Buffer{} // release memory + + if raw := *w.rawInput; raw != nil { + if rawInputBuffer, err := buf.ReadFrom(raw); err == nil && !rawInputBuffer.IsEmpty() { + buffer, _ = buf.MergeMulti(buffer, rawInputBuffer) + } + *raw = bytes.Buffer{} // release memory + } w.rawInput = nil if inbound := session.InboundFromContext(w.ctx); inbound != nil && inbound.Conn != nil { diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index d6a9d8efa..569287404 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -6,11 +6,9 @@ import ( gotls "crypto/tls" "encoding/base64" "io" - "reflect" "strconv" "strings" "time" - "unsafe" "github.com/xtls/xray-core/app/dispatcher" "github.com/xtls/xray-core/app/reverse" @@ -548,7 +546,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s } var input *bytes.Reader - var rawInput *bytes.Buffer + var rawInput **bytes.Buffer switch requestAddons.Flow { case vless.XRV: if account.Flow == requestAddons.Flow { @@ -560,30 +558,23 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s inbound.CanSpliceCopy = 3 fallthrough // we will break Mux connections that contain TCP requests case protocol.RequestCommandTCP: - var t reflect.Type - var p uintptr + var target net.Conn if commonConn, ok := connection.(*encryption.CommonConn); ok { if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransportWithoutSecurity(iConn) { inbound.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport / another securityConn should not be penetrated } - t = reflect.TypeOf(commonConn).Elem() - p = uintptr(unsafe.Pointer(commonConn)) + target = commonConn } else if tlsConn, ok := iConn.(*tls.Conn); ok { if tlsConn.ConnectionState().Version != gotls.VersionTLS13 { return errors.New(`failed to use `+requestAddons.Flow+`, found outer tls version `, tlsConn.ConnectionState().Version) } - t = reflect.TypeOf(tlsConn.Conn).Elem() - p = uintptr(unsafe.Pointer(tlsConn.Conn)) + target = tlsConn.Conn } else if realityConn, ok := iConn.(*reality.Conn); ok { - t = reflect.TypeOf(realityConn.Conn).Elem() - p = uintptr(unsafe.Pointer(realityConn.Conn)) + target = realityConn.Conn } else { return errors.New("XTLS only supports TLS and REALITY directly for now.") } - i, _ := t.FieldByName("input") - r, _ := t.FieldByName("rawInput") - input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset)) - rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset)) + input, rawInput = vless.AccessTLSBuffers(target) } } else { return errors.New("account " + account.ID.String() + " is not able to use the flow " + requestAddons.Flow) diff --git a/proxy/vless/outbound/outbound.go b/proxy/vless/outbound/outbound.go index 56a2fd04e..3f6b4b191 100644 --- a/proxy/vless/outbound/outbound.go +++ b/proxy/vless/outbound/outbound.go @@ -5,11 +5,9 @@ import ( "context" gotls "crypto/tls" "encoding/base64" - "reflect" "strings" "sync" "time" - "unsafe" utls "github.com/refraction-networking/utls" proxymanConfig "github.com/xtls/xray-core/app/proxyman" @@ -244,7 +242,7 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte } var input *bytes.Reader - var rawInput *bytes.Buffer + var rawInput **bytes.Buffer allowUDP443 := false switch requestAddons.Flow { case vless.XRV + "-udp443": @@ -261,30 +259,22 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte case protocol.RequestCommandMux: fallthrough // let server break Mux connections that contain TCP requests case protocol.RequestCommandTCP, protocol.RequestCommandRvs: - var t reflect.Type - var p uintptr + var target net.Conn if commonConn, ok := conn.(*encryption.CommonConn); ok { if _, ok := commonConn.Conn.(*encryption.XorConn); ok || !proxy.IsRAWTransportWithoutSecurity(iConn) { ob.CanSpliceCopy = 3 // full-random xorConn / non-RAW transport / another securityConn should not be penetrated } - t = reflect.TypeOf(commonConn).Elem() - p = uintptr(unsafe.Pointer(commonConn)) + target = commonConn } else if tlsConn, ok := iConn.(*tls.Conn); ok { - t = reflect.TypeOf(tlsConn.Conn).Elem() - p = uintptr(unsafe.Pointer(tlsConn.Conn)) + target = tlsConn.Conn } else if utlsConn, ok := iConn.(*tls.UConn); ok { - t = reflect.TypeOf(utlsConn.Conn).Elem() - p = uintptr(unsafe.Pointer(utlsConn.Conn)) + target = utlsConn.Conn } else if realityConn, ok := iConn.(*reality.UConn); ok { - t = reflect.TypeOf(realityConn.Conn).Elem() - p = uintptr(unsafe.Pointer(realityConn.Conn)) + target = realityConn.Conn } else { return errors.New("XTLS only supports TLS and REALITY directly for now.") } - i, _ := t.FieldByName("input") - r, _ := t.FieldByName("rawInput") - input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset)) - rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset)) + input, rawInput = vless.AccessTLSBuffers(target) default: panic("unknown VLESS request command") } diff --git a/proxy/vless/vless.go b/proxy/vless/vless.go index bdebdca64..5a055c47b 100644 --- a/proxy/vless/vless.go +++ b/proxy/vless/vless.go @@ -5,7 +5,28 @@ // clients with 'socks' for proxying. package vless +import ( + "bytes" + "net" + "reflect" + + "github.com/xtls/xray-core/common/utils" +) + const ( None = "none" XRV = "xtls-rprx-vision" ) + +// Old TLS lib holds bytes.Buffer, but new TLS lib holds *bytes.Buffer +// Since Xray has 3 TLS libraries (std, utls, REALITY server) +// we detect the type of rawInput and return the appropriate pointer +func AccessTLSBuffers(conn net.Conn) (*bytes.Reader, **bytes.Buffer) { + input := utils.AccessField[bytes.Reader](conn, "input") + field := reflect.ValueOf(conn).Elem().FieldByName("rawInput") + if field.Kind() == reflect.Pointer { + return input, utils.AccessField[*bytes.Buffer](conn, "rawInput") + } + raw := utils.AccessField[bytes.Buffer](conn, "rawInput") + return input, &raw +}