From 5e695327b173c6917ce8840c7e7d38f75a3b7099 Mon Sep 17 00:00:00 2001 From: yuhan6665 <1588741+yuhan6665@users.noreply.github.com> Date: Sat, 29 Oct 2022 00:51:59 -0400 Subject: [PATCH] Add XTLS RPRX's Vision (#1235) * Add XTLS RPRX's Vision * Add helpful warning when security is wrong * Add XTLS padding (draft) * Fix number of packet to filter * Xtls padding version 1.0 and unpadding logic --- common/buf/multi_buffer.go | 13 ++ infra/conf/vless.go | 8 +- proxy/vless/encoding/addons.go | 2 +- proxy/vless/encoding/encoding.go | 333 ++++++++++++++++++++++++++++--- proxy/vless/inbound/inbound.go | 61 ++++-- proxy/vless/outbound/outbound.go | 58 ++++-- proxy/vless/vless.go | 1 + 7 files changed, 420 insertions(+), 56 deletions(-) diff --git a/common/buf/multi_buffer.go b/common/buf/multi_buffer.go index 478d0c63..f3b17aef 100644 --- a/common/buf/multi_buffer.go +++ b/common/buf/multi_buffer.go @@ -203,6 +203,19 @@ func SplitSize(mb MultiBuffer, size int32) (MultiBuffer, MultiBuffer) { return mb, r } +// SplitMulti splits the beginning of the MultiBuffer into first one, the index i and after into second one +func SplitMulti(mb MultiBuffer, i int) (MultiBuffer, MultiBuffer) { + mb2 := make(MultiBuffer, 0, len(mb)) + if i < len(mb) && i >= 0 { + mb2 = append(mb2, mb[i:]...) + for j := i; j < len(mb); j++ { + mb[j] = nil + } + mb = mb[:i] + } + return mb, mb2 +} + // WriteMultiBuffer writes all buffers from the MultiBuffer to the Writer one by one, and return error if any, with leftover MultiBuffer. func WriteMultiBuffer(writer io.Writer, mb MultiBuffer) (MultiBuffer, error) { for { diff --git a/infra/conf/vless.go b/infra/conf/vless.go index a3598d8b..1f69c7e4 100644 --- a/infra/conf/vless.go +++ b/infra/conf/vless.go @@ -53,8 +53,8 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) { account.Id = u.String() switch account.Flow { - case "", "xtls-rprx-origin", "xtls-rprx-direct": - case "xtls-rprx-splice": + case "", vless.XRO, vless.XRD, vless.XRV: + case vless.XRS: return nil, newError(`VLESS clients: inbound doesn't support "xtls-rprx-splice" in this version, please use "xtls-rprx-direct" instead`) default: return nil, newError(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`) @@ -182,8 +182,8 @@ func (c *VLessOutboundConfig) Build() (proto.Message, error) { account.Id = u.String() switch account.Flow { - case "", "xtls-rprx-origin", "xtls-rprx-origin-udp443", "xtls-rprx-direct", "xtls-rprx-direct-udp443": - case "xtls-rprx-splice", "xtls-rprx-splice-udp443": + case "", vless.XRO, vless.XRO + "-udp443", vless.XRD, vless.XRD + "-udp443", vless.XRV, vless.XRV + "-udp443": + case vless.XRS, vless.XRS + "-udp443": if runtime.GOOS != "linux" && runtime.GOOS != "android" { return nil, newError(`VLESS users: "` + account.Flow + `" only support linux in this version`) } diff --git a/proxy/vless/encoding/addons.go b/proxy/vless/encoding/addons.go index 2249e916..fb457412 100644 --- a/proxy/vless/encoding/addons.go +++ b/proxy/vless/encoding/addons.go @@ -11,7 +11,7 @@ import ( func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error { switch addons.Flow { - case vless.XRO, vless.XRD: + case vless.XRO, vless.XRD, vless.XRV: bytes, err := proto.Marshal(addons) if err != nil { return newError("failed to marshal addons protobuf value").Base(err) diff --git a/proxy/vless/encoding/encoding.go b/proxy/vless/encoding/encoding.go index 403e09cf..79d9c51e 100644 --- a/proxy/vless/encoding/encoding.go +++ b/proxy/vless/encoding/encoding.go @@ -3,11 +3,15 @@ package encoding //go:generate go run github.com/xtls/xray-core/common/errors/errorgen import ( + "bytes" "context" + "crypto/rand" "fmt" "io" + "math/big" "runtime" "syscall" + "time" "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/errors" @@ -18,6 +22,7 @@ import ( "github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/xtls" ) @@ -25,6 +30,11 @@ const ( Version = byte(0) ) +var tls13SupportedVersions = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04} +var tlsClientHandShakeStart = []byte{0x16, 0x03} +var tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03} +var tlsApplicationDataStart = []byte{0x17, 0x03, 0x03} + var addrParser = protocol.NewAddressParser( protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), @@ -176,40 +186,36 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A return responseAddons, nil } -func ReadV(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn *xtls.Conn, rawConn syscall.RawConn, counter stats.Counter, sctx context.Context) error { +func ReadV(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn *xtls.Conn, rawConn syscall.RawConn, counter stats.Counter, ctx context.Context) error { err := func() error { var ct stats.Counter for { if conn.DirectIn { conn.DirectIn = false - if sctx != nil { - if inbound := session.InboundFromContext(sctx); inbound != nil && inbound.Conn != nil { - iConn := inbound.Conn - statConn, ok := iConn.(*stat.CounterConnection) - if ok { - iConn = statConn.Connection + if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil { + iConn := inbound.Conn + statConn, ok := iConn.(*stat.CounterConnection) + if ok { + iConn = statConn.Connection + } + if xc, ok := iConn.(*xtls.Conn); ok { + iConn = xc.NetConn() + } + if tc, ok := iConn.(*net.TCPConn); ok { + if conn.SHOW { + fmt.Println(conn.MARK, "Splice") } - if xc, ok := iConn.(*xtls.Conn); ok { - iConn = xc.NetConn() + runtime.Gosched() // necessary + w, err := tc.ReadFrom(conn.NetConn()) + if counter != nil { + counter.Add(w) } - if tc, ok := iConn.(*net.TCPConn); ok { - if conn.SHOW { - fmt.Println(conn.MARK, "Splice") - } - runtime.Gosched() // necessary - w, err := tc.ReadFrom(conn.NetConn()) - if counter != nil { - counter.Add(w) - } - if statConn != nil && statConn.WriteCounter != nil { - statConn.WriteCounter.Add(w) - } - return err - } else { - panic("XTLS Splice: not TCP inbound") + if statConn != nil && statConn.WriteCounter != nil { + statConn.WriteCounter.Add(w) } + return err } else { - // panic("XTLS Splice: nil inbound or nil inbound.Conn") + panic("XTLS Splice: not TCP inbound") } } reader = buf.NewReadVReader(conn.NetConn(), rawConn, nil) @@ -238,3 +244,280 @@ func ReadV(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, c } return nil } + +// XtlsRead filter and read xtls protocol +func XtlsRead(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn *tls.Conn, rawConn syscall.RawConn, counter stats.Counter, ctx context.Context, userUUID []byte, numberOfPacketToFilter *int, isTLS13 *bool, isTLS12 *bool, isTLS *bool) error { + err := func() error { + var ct stats.Counter + filterUUID := true + shouldSwitchToDirectCopy := false + var remainingContent int32 = -1 + var remainingPadding int32 = -1 + currentCommand := 0 + for { + if shouldSwitchToDirectCopy { + shouldSwitchToDirectCopy = false + if runtime.GOOS == "linux" || runtime.GOOS == "android" { + if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil { + iConn := inbound.Conn + statConn, ok := iConn.(*stat.CounterConnection) + if ok { + iConn = statConn.Connection + } + if xc, ok := iConn.(*tls.Conn); ok { + iConn = xc.NetConn() + } + if tc, ok := iConn.(*net.TCPConn); ok { + newError("XtlsRead splice").WriteToLog(session.ExportIDToError(ctx)) + runtime.Gosched() // necessary + w, err := tc.ReadFrom(conn.NetConn()) + if counter != nil { + counter.Add(w) + } + if statConn != nil && statConn.WriteCounter != nil { + statConn.WriteCounter.Add(w) + } + return err + } else { + panic("XTLS Splice: not TCP inbound") + } + } else { + // panic("XTLS Splice: nil inbound or nil inbound.Conn") + } + } + reader = buf.NewReadVReader(conn.NetConn(), rawConn, nil) + ct = counter + newError("XtlsRead readV").WriteToLog(session.ExportIDToError(ctx)) + } + buffer, err := reader.ReadMultiBuffer() + if !buffer.IsEmpty() { + if filterUUID && (*isTLS || *numberOfPacketToFilter > 0) { + buffer = XtlsUnpadding(ctx, buffer, userUUID, &remainingContent, &remainingPadding, ¤tCommand) + if remainingContent == 0 && remainingPadding == 0 { + if currentCommand == 1 { + filterUUID = false + } else if currentCommand == 2 { + filterUUID = false + shouldSwitchToDirectCopy = true + } else if currentCommand != 0 { + newError("XtlsRead unknown command ", currentCommand, buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + } + } + } + if *numberOfPacketToFilter > 0 { + XtlsFilterTls13(buffer, numberOfPacketToFilter, isTLS13, isTLS12, isTLS, ctx) + } + if ct != nil { + ct.Add(int64(buffer.Len())) + } + timer.Update() + if werr := writer.WriteMultiBuffer(buffer); werr != nil { + return werr + } + } + if err != nil { + return err + } + } + }() + if err != nil && errors.Cause(err) != io.EOF { + return err + } + return nil +} + +// XtlsWrite filter and write xtls protocol +func XtlsWrite(reader buf.Reader, writer buf.Writer, timer signal.ActivityUpdater, conn *tls.Conn, counter stats.Counter, ctx context.Context, userUUID *[]byte, numberOfPacketToFilter *int, isTLS13 *bool, isTLS12 *bool, isTLS *bool) error { + err := func() error { + var ct stats.Counter + filterTlsApplicationData := true + shouldSwitchToDirectCopy := false + for { + buffer, err := reader.ReadMultiBuffer() + if !buffer.IsEmpty() { + if *numberOfPacketToFilter > 0 { + XtlsFilterTls13(buffer, numberOfPacketToFilter, isTLS13, isTLS12, isTLS, ctx) + } + if filterTlsApplicationData && *isTLS { + var xtlsSpecIndex int + for i, b := range buffer { + if b.Len() >= 6 && bytes.Equal(tlsApplicationDataStart, b.BytesTo(3)) { + var command byte = 0x01 + if *isTLS13 { + shouldSwitchToDirectCopy = true + xtlsSpecIndex = i + command = 0x02 + } + filterTlsApplicationData = false + buffer[i] = XtlsPadding(b, command, userUUID, ctx) + break + } else if !*isTLS12 && !*isTLS13 && *numberOfPacketToFilter <= 0 { + //maybe tls 1.1 or 1.0 + filterTlsApplicationData = false + buffer[i] = XtlsPadding(b, 0x01, userUUID, ctx) + break + } + buffer[i] = XtlsPadding(b, 0x00, userUUID, ctx) + } + if shouldSwitchToDirectCopy { + encryptBuffer, directBuffer := buf.SplitMulti(buffer, xtlsSpecIndex+1) + length := encryptBuffer.Len() + if !encryptBuffer.IsEmpty() { + timer.Update() + if werr := writer.WriteMultiBuffer(encryptBuffer); werr != nil { + return werr + } + } + buffer = directBuffer + writer = buf.NewWriter(conn.NetConn()) + ct = counter + newError("XtlsWrite writeV ", xtlsSpecIndex, " ", length, " ", buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + time.Sleep(5 * time.Millisecond) // for some device, the first xtls direct packet fails without this delay + } + } + if !buffer.IsEmpty() { + if ct != nil { + ct.Add(int64(buffer.Len())) + } + timer.Update() + if werr := writer.WriteMultiBuffer(buffer); werr != nil { + return werr + } + } + } + if err != nil { + return err + } + } + }() + if err != nil && errors.Cause(err) != io.EOF { + return err + } + return nil +} + +// XtlsFilterTls13 filter and recognize tls 1.3 +func XtlsFilterTls13(buffer buf.MultiBuffer, numberOfPacketToFilter *int, isTLS13 *bool, isTLS12 *bool, isTLS *bool, ctx context.Context) { + for _, b := range buffer { + *numberOfPacketToFilter-- + if b.Len() >= 6 { + startsBytes := b.BytesTo(6) + if bytes.Equal(tlsServerHandShakeStart, startsBytes[:3]) && startsBytes[5] == 0x02 { + total := (int(startsBytes[3])<<8 | int(startsBytes[4])) + 5 + if b.Len() >= int32(total) { + if bytes.Contains(b.BytesTo(int32(total)), tls13SupportedVersions) { + *isTLS13 = true + *isTLS = true + newError("XtlsFilterTls13 found tls 1.3! ", buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + } else { + *isTLS12 = true + *isTLS = true + newError("XtlsFilterTls13 found tls 1.2! ", buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + } + *numberOfPacketToFilter = 0 + return + } + } else if bytes.Equal(tlsClientHandShakeStart, startsBytes[:2]) && startsBytes[5] == 0x01 { + *isTLS = true + newError("XtlsFilterTls13 found tls client hello! ", buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + } + } + if *numberOfPacketToFilter <= 0 { + newError("XtlsFilterTls13 stop filtering", buffer.Len()).WriteToLog(session.ExportIDToError(ctx)) + } + } +} + +// XtlsPadding add padding to eliminate length siganature during tls handshake +func XtlsPadding(b *buf.Buffer, command byte, userUUID *[]byte, ctx context.Context) *buf.Buffer { + var length int32 = 0 + if b.Len() < 900 { + l, err := rand.Int(rand.Reader, big.NewInt(500)) + if err != nil { + newError("failed to generate padding").Base(err).WriteToLog(session.ExportIDToError(ctx)) + } + length = int32(l.Int64()) + 900 - b.Len() + } + newbuffer := buf.New() + if userUUID != nil { + newbuffer.Write(*userUUID) + *userUUID = nil + } + newbuffer.Write([]byte{command, byte(b.Len() >> 8), byte(b.Len()), byte(length >> 8), byte(length)}) + newbuffer.Write(b.Bytes()) + newbuffer.Extend(length) + newError("XtlsPadding ", b.Len(), " ", length, " ", command).WriteToLog(session.ExportIDToError(ctx)) + b.Release() + b = nil + return newbuffer +} + +// XtlsUnpadding remove padding and parse command +func XtlsUnpadding(ctx context.Context, buffer buf.MultiBuffer, userUUID []byte, remainingContent *int32, remainingPadding *int32, currentCommand *int) buf.MultiBuffer { + posindex := 0 + var posByte int32 = 0 + if *remainingContent == -1 && *remainingPadding == -1 { + for i, b := range buffer { + if b.Len() >= 21 && bytes.Equal(userUUID, b.BytesTo(16)) { + posindex = i + posByte = 16 + *remainingContent = 0 + *remainingPadding = 0 + break + } + } + } + if *remainingContent == -1 && *remainingPadding == -1 { + return buffer + } + mb2 := make(buf.MultiBuffer, 0, len(buffer)) + for i := 0; i < posindex; i++ { + newbuffer := buf.New() + newbuffer.Write(buffer[i].Bytes()) + mb2 = append(mb2, newbuffer) + } + for i := posindex; i < len(buffer); i++ { + b := buffer[i] + for posByte < b.Len() { + if *remainingContent <= 0 && *remainingPadding <= 0 { + if *currentCommand == 1 { + len := b.Len() - posByte + newbuffer := buf.New() + newbuffer.Write(b.BytesRange(posByte, posByte+len)) + mb2 = append(mb2, newbuffer) + posByte += len + } else { + paddingInfo := b.BytesRange(posByte, posByte+5) + *currentCommand = int(paddingInfo[0]) + *remainingContent = int32(paddingInfo[1])<<8 | int32(paddingInfo[2]) + *remainingPadding = int32(paddingInfo[3])<<8 | int32(paddingInfo[4]) + newError("Xtls Unpadding new block", i, " ", posByte, " content ", *remainingContent, " padding ", *remainingPadding, " ", paddingInfo[0]).WriteToLog(session.ExportIDToError(ctx)) + posByte += 5 + } + } else if *remainingContent > 0 { + len := *remainingContent + if b.Len() < posByte+*remainingContent { + len = b.Len() - posByte + } + newbuffer := buf.New() + newbuffer.Write(b.BytesRange(posByte, posByte+len)) + mb2 = append(mb2, newbuffer) + *remainingContent -= len + posByte += len + } else { // remainingPadding > 0 + len := *remainingPadding + if b.Len() < posByte+*remainingPadding { + len = b.Len() - posByte + } + *remainingPadding -= len + posByte += len + } + if posByte == b.Len() { + posByte = 0 + break + } + } + } + buf.ReleaseMulti(buffer) + return mb2 +} diff --git a/proxy/vless/inbound/inbound.go b/proxy/vless/inbound/inbound.go index 1a436f08..5ee491a7 100644 --- a/proxy/vless/inbound/inbound.go +++ b/proxy/vless/inbound/inbound.go @@ -442,7 +442,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s var rawConn syscall.RawConn switch requestAddons.Flow { - case vless.XRO, vless.XRD: + case vless.XRO, vless.XRD, vless.XRV: if account.Flow == requestAddons.Flow { switch request.Command { case protocol.RequestCommandMux: @@ -450,7 +450,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s case protocol.RequestCommandUDP: return newError(requestAddons.Flow + " doesn't support UDP").AtWarning() case protocol.RequestCommandTCP: - if xtlsConn, ok := iConn.(*xtls.Conn); ok { + if requestAddons.Flow == vless.XRV { + if _, ok := iConn.(*xtls.Conn); ok { + return newError(`failed to use ` + requestAddons.Flow + `, vision "security" must be "tls"`).AtWarning() + } + if sc, ok := iConn.(*tls.Conn).NetConn().(syscall.Conn); ok { + rawConn, _ = sc.SyscallConn() + } + } else if xtlsConn, ok := iConn.(*xtls.Conn); ok { xtlsConn.RPRX = true xtlsConn.SHOW = xtls_show xtlsConn.MARK = "XTLS" @@ -494,6 +501,10 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s serverReader := link.Reader // .(*pipe.Reader) serverWriter := link.Writer // .(*pipe.Writer) + isTLS13 := false + isTLS12 := false + isTLS := false + numberOfPacketToFilter := 8 postRequest := func() error { defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) @@ -508,7 +519,13 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s if statConn != nil { counter = statConn.ReadCounter } - err = encoding.ReadV(clientReader, serverWriter, timer, iConn.(*xtls.Conn), rawConn, counter, nil) + if requestAddons.Flow == vless.XRV { + //TODO enable splice + ctx = session.ContextWithInbound(ctx, nil) + err = encoding.XtlsRead(clientReader, serverWriter, timer, iConn.(*tls.Conn), rawConn, counter, ctx, account.ID.Bytes(), &numberOfPacketToFilter, &isTLS13, &isTLS12, &isTLS) + } else { + err = encoding.ReadV(clientReader, serverWriter, timer, iConn.(*xtls.Conn), rawConn, counter, ctx) + } } else { // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)) @@ -531,26 +548,42 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s // default: clientWriter := bufferWriter clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons) - { - multiBuffer, err := serverReader.ReadMultiBuffer() - if err != nil { - return err // ... - } - if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil { - return err // ... + userUUID := account.ID.Bytes() + multiBuffer, err1 := serverReader.ReadMultiBuffer() + if err1 != nil { + return err1 // ... + } + if requestAddons.Flow == vless.XRV { + encoding.XtlsFilterTls13(multiBuffer, &numberOfPacketToFilter, &isTLS13, &isTLS12, &isTLS, ctx) + if isTLS { + for i, b := range multiBuffer { + + multiBuffer[i] = encoding.XtlsPadding(b, 0x00, &userUUID, ctx) + } } } - + if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil { + return err // ... + } // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer if err := bufferWriter.SetBuffered(false); err != nil { return newError("failed to write A response payload").Base(err).AtWarning() } - // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer - if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil { + var err error + if rawConn != nil && requestAddons.Flow == vless.XRV { + var counter stats.Counter + if statConn != nil { + counter = statConn.WriteCounter + } + err = encoding.XtlsWrite(serverReader, clientWriter, timer, iConn.(*tls.Conn), counter, ctx, &userUUID, &numberOfPacketToFilter, &isTLS13, &isTLS12, &isTLS) + } else { + // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer + err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)) + } + if err != nil { return newError("failed to transfer response payload").Base(err).AtInfo() } - // Indicates the end of response payload. switch responseAddons.Flow { default: diff --git a/proxy/vless/outbound/outbound.go b/proxy/vless/outbound/outbound.go index de40aaeb..1e290caf 100644 --- a/proxy/vless/outbound/outbound.go +++ b/proxy/vless/outbound/outbound.go @@ -5,7 +5,6 @@ package outbound import ( "context" "syscall" - "time" "github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common/buf" @@ -25,6 +24,7 @@ import ( "github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet/stat" + "github.com/xtls/xray-core/transport/internet/tls" "github.com/xtls/xray-core/transport/internet/xtls" ) @@ -128,15 +128,13 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte } var rawConn syscall.RawConn - var sctx context.Context - allowUDP443 := false switch requestAddons.Flow { - case vless.XRO + "-udp443", vless.XRD + "-udp443", vless.XRS + "-udp443": + case vless.XRO + "-udp443", vless.XRD + "-udp443", vless.XRS + "-udp443", vless.XRV + "-udp443": allowUDP443 = true requestAddons.Flow = requestAddons.Flow[:16] fallthrough - case vless.XRO, vless.XRD, vless.XRS: + case vless.XRO, vless.XRD, vless.XRS, vless.XRV: switch request.Command { case protocol.RequestCommandMux: return newError(requestAddons.Flow + " doesn't support Mux").AtWarning() @@ -146,12 +144,18 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte } requestAddons.Flow = "" case protocol.RequestCommandTCP: - if xtlsConn, ok := iConn.(*xtls.Conn); ok { + if requestAddons.Flow == vless.XRV { + if _, ok := iConn.(*xtls.Conn); ok { + return newError(`failed to use ` + requestAddons.Flow + `, vision "security" must be "tls"`).AtWarning() + } + if sc, ok := iConn.(*tls.Conn).NetConn().(syscall.Conn); ok { + rawConn, _ = sc.SyscallConn() + } + } else if xtlsConn, ok := iConn.(*xtls.Conn); ok { xtlsConn.RPRX = true xtlsConn.SHOW = xtls_show xtlsConn.MARK = "XTLS" if requestAddons.Flow == vless.XRS { - sctx = ctx requestAddons.Flow = vless.XRD } if requestAddons.Flow == vless.XRD { @@ -176,6 +180,10 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte clientReader := link.Reader // .(*pipe.Reader) clientWriter := link.Writer // .(*pipe.Writer) + isTLS13 := false + isTLS12 := false + isTLS := false + numberOfPacketToFilter := 8 if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 { request.Command = protocol.RequestCommandMux @@ -196,17 +204,39 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte if request.Command == protocol.RequestCommandMux && request.Port == 666 { serverWriter = xudp.NewPacketWriter(serverWriter, target) } - if err := buf.CopyOnceTimeout(clientReader, serverWriter, time.Millisecond*100); err != nil && err != buf.ErrNotTimeoutReader && err != buf.ErrReadTimeout { + userUUID := account.ID.Bytes() + multiBuffer, err1 := clientReader.ReadMultiBuffer() + if err1 != nil { + return err1 // ... + } + if requestAddons.Flow == vless.XRV { + encoding.XtlsFilterTls13(multiBuffer, &numberOfPacketToFilter, &isTLS13, &isTLS12, &isTLS, ctx) + if isTLS { + for i, b := range multiBuffer { + multiBuffer[i] = encoding.XtlsPadding(b, 0x00, &userUUID, ctx) + } + } + } + if err := serverWriter.WriteMultiBuffer(multiBuffer); err != nil { return err // ... } - // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer if err := bufferWriter.SetBuffered(false); err != nil { return newError("failed to write A request payload").Base(err).AtWarning() } - // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer - if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil { + var err error + if rawConn != nil && requestAddons.Flow == vless.XRV { + var counter stats.Counter + if statConn != nil { + counter = statConn.WriteCounter + } + err = encoding.XtlsWrite(clientReader, serverWriter, timer, iConn.(*tls.Conn), counter, ctx, &userUUID, &numberOfPacketToFilter, &isTLS13, &isTLS12, &isTLS) + } else { + // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer + err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)) + } + if err != nil { return newError("failed to transfer request payload").Base(err).AtInfo() } @@ -236,7 +266,11 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte if statConn != nil { counter = statConn.ReadCounter } - err = encoding.ReadV(serverReader, clientWriter, timer, iConn.(*xtls.Conn), rawConn, counter, sctx) + if requestAddons.Flow == vless.XRV { + err = encoding.XtlsRead(serverReader, clientWriter, timer, iConn.(*tls.Conn), rawConn, counter, ctx, account.ID.Bytes(), &numberOfPacketToFilter, &isTLS13, &isTLS12, &isTLS) + } else { + err = encoding.ReadV(serverReader, clientWriter, timer, iConn.(*xtls.Conn), rawConn, counter, ctx) + } } else { // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)) diff --git a/proxy/vless/vless.go b/proxy/vless/vless.go index a9e847ec..1e78b8af 100644 --- a/proxy/vless/vless.go +++ b/proxy/vless/vless.go @@ -11,4 +11,5 @@ const ( XRO = "xtls-rprx-origin" XRD = "xtls-rprx-direct" XRS = "xtls-rprx-splice" + XRV = "xtls-rprx-vision" )