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
This commit is contained in:
yuhan6665 2022-10-29 00:51:59 -04:00 committed by GitHub
parent 341d317d0c
commit 5e695327b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 420 additions and 56 deletions

View File

@ -203,6 +203,19 @@ func SplitSize(mb MultiBuffer, size int32) (MultiBuffer, MultiBuffer) {
return mb, r 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. // 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) { func WriteMultiBuffer(writer io.Writer, mb MultiBuffer) (MultiBuffer, error) {
for { for {

View File

@ -53,8 +53,8 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
account.Id = u.String() account.Id = u.String()
switch account.Flow { switch account.Flow {
case "", "xtls-rprx-origin", "xtls-rprx-direct": case "", vless.XRO, vless.XRD, vless.XRV:
case "xtls-rprx-splice": case vless.XRS:
return nil, newError(`VLESS clients: inbound doesn't support "xtls-rprx-splice" in this version, please use "xtls-rprx-direct" instead`) return nil, newError(`VLESS clients: inbound doesn't support "xtls-rprx-splice" in this version, please use "xtls-rprx-direct" instead`)
default: default:
return nil, newError(`VLESS clients: "flow" doesn't support "` + account.Flow + `" in this version`) 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() account.Id = u.String()
switch account.Flow { switch account.Flow {
case "", "xtls-rprx-origin", "xtls-rprx-origin-udp443", "xtls-rprx-direct", "xtls-rprx-direct-udp443": case "", vless.XRO, vless.XRO + "-udp443", vless.XRD, vless.XRD + "-udp443", vless.XRV, vless.XRV + "-udp443":
case "xtls-rprx-splice", "xtls-rprx-splice-udp443": case vless.XRS, vless.XRS + "-udp443":
if runtime.GOOS != "linux" && runtime.GOOS != "android" { if runtime.GOOS != "linux" && runtime.GOOS != "android" {
return nil, newError(`VLESS users: "` + account.Flow + `" only support linux in this version`) return nil, newError(`VLESS users: "` + account.Flow + `" only support linux in this version`)
} }

View File

@ -11,7 +11,7 @@ import (
func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error { func EncodeHeaderAddons(buffer *buf.Buffer, addons *Addons) error {
switch addons.Flow { switch addons.Flow {
case vless.XRO, vless.XRD: case vless.XRO, vless.XRD, vless.XRV:
bytes, err := proto.Marshal(addons) bytes, err := proto.Marshal(addons)
if err != nil { if err != nil {
return newError("failed to marshal addons protobuf value").Base(err) return newError("failed to marshal addons protobuf value").Base(err)

View File

@ -3,11 +3,15 @@ package encoding
//go:generate go run github.com/xtls/xray-core/common/errors/errorgen //go:generate go run github.com/xtls/xray-core/common/errors/errorgen
import ( import (
"bytes"
"context" "context"
"crypto/rand"
"fmt" "fmt"
"io" "io"
"math/big"
"runtime" "runtime"
"syscall" "syscall"
"time"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
"github.com/xtls/xray-core/common/errors" "github.com/xtls/xray-core/common/errors"
@ -18,6 +22,7 @@ import (
"github.com/xtls/xray-core/features/stats" "github.com/xtls/xray-core/features/stats"
"github.com/xtls/xray-core/proxy/vless" "github.com/xtls/xray-core/proxy/vless"
"github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
"github.com/xtls/xray-core/transport/internet/xtls" "github.com/xtls/xray-core/transport/internet/xtls"
) )
@ -25,6 +30,11 @@ const (
Version = byte(0) 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( var addrParser = protocol.NewAddressParser(
protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4), protocol.AddressFamilyByte(byte(protocol.AddressTypeIPv4), net.AddressFamilyIPv4),
protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain), protocol.AddressFamilyByte(byte(protocol.AddressTypeDomain), net.AddressFamilyDomain),
@ -176,40 +186,36 @@ func DecodeResponseHeader(reader io.Reader, request *protocol.RequestHeader) (*A
return responseAddons, nil 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 { err := func() error {
var ct stats.Counter var ct stats.Counter
for { for {
if conn.DirectIn { if conn.DirectIn {
conn.DirectIn = false conn.DirectIn = false
if sctx != nil { if inbound := session.InboundFromContext(ctx); inbound != nil && inbound.Conn != nil {
if inbound := session.InboundFromContext(sctx); inbound != nil && inbound.Conn != nil { iConn := inbound.Conn
iConn := inbound.Conn statConn, ok := iConn.(*stat.CounterConnection)
statConn, ok := iConn.(*stat.CounterConnection) if ok {
if ok { iConn = statConn.Connection
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 { runtime.Gosched() // necessary
iConn = xc.NetConn() w, err := tc.ReadFrom(conn.NetConn())
if counter != nil {
counter.Add(w)
} }
if tc, ok := iConn.(*net.TCPConn); ok { if statConn != nil && statConn.WriteCounter != nil {
if conn.SHOW { statConn.WriteCounter.Add(w)
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")
} }
return err
} else { } else {
// panic("XTLS Splice: nil inbound or nil inbound.Conn") panic("XTLS Splice: not TCP inbound")
} }
} }
reader = buf.NewReadVReader(conn.NetConn(), rawConn, nil) 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 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, &currentCommand)
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
}

View File

@ -442,7 +442,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
var rawConn syscall.RawConn var rawConn syscall.RawConn
switch requestAddons.Flow { switch requestAddons.Flow {
case vless.XRO, vless.XRD: case vless.XRO, vless.XRD, vless.XRV:
if account.Flow == requestAddons.Flow { if account.Flow == requestAddons.Flow {
switch request.Command { switch request.Command {
case protocol.RequestCommandMux: case protocol.RequestCommandMux:
@ -450,7 +450,14 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
case protocol.RequestCommandUDP: case protocol.RequestCommandUDP:
return newError(requestAddons.Flow + " doesn't support UDP").AtWarning() return newError(requestAddons.Flow + " doesn't support UDP").AtWarning()
case protocol.RequestCommandTCP: 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.RPRX = true
xtlsConn.SHOW = xtls_show xtlsConn.SHOW = xtls_show
xtlsConn.MARK = "XTLS" xtlsConn.MARK = "XTLS"
@ -494,6 +501,10 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
serverReader := link.Reader // .(*pipe.Reader) serverReader := link.Reader // .(*pipe.Reader)
serverWriter := link.Writer // .(*pipe.Writer) serverWriter := link.Writer // .(*pipe.Writer)
isTLS13 := false
isTLS12 := false
isTLS := false
numberOfPacketToFilter := 8
postRequest := func() error { postRequest := func() error {
defer timer.SetTimeout(sessionPolicy.Timeouts.DownlinkOnly) 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 { if statConn != nil {
counter = statConn.ReadCounter 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 { } else {
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer // from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer
err = buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)) 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 // default: clientWriter := bufferWriter
clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons) clientWriter := encoding.EncodeBodyAddons(bufferWriter, request, responseAddons)
{ userUUID := account.ID.Bytes()
multiBuffer, err := serverReader.ReadMultiBuffer() multiBuffer, err1 := serverReader.ReadMultiBuffer()
if err != nil { if err1 != nil {
return err // ... return err1 // ...
} }
if err := clientWriter.WriteMultiBuffer(multiBuffer); err != nil { if requestAddons.Flow == vless.XRV {
return err // ... 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 // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
if err := bufferWriter.SetBuffered(false); err != nil { if err := bufferWriter.SetBuffered(false); err != nil {
return newError("failed to write A response payload").Base(err).AtWarning() return newError("failed to write A response payload").Base(err).AtWarning()
} }
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer var err error
if err := buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)); err != nil { 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() return newError("failed to transfer response payload").Base(err).AtInfo()
} }
// Indicates the end of response payload. // Indicates the end of response payload.
switch responseAddons.Flow { switch responseAddons.Flow {
default: default:

View File

@ -5,7 +5,6 @@ package outbound
import ( import (
"context" "context"
"syscall" "syscall"
"time"
"github.com/xtls/xray-core/common" "github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/buf"
@ -25,6 +24,7 @@ import (
"github.com/xtls/xray-core/transport" "github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/internet" "github.com/xtls/xray-core/transport/internet"
"github.com/xtls/xray-core/transport/internet/stat" "github.com/xtls/xray-core/transport/internet/stat"
"github.com/xtls/xray-core/transport/internet/tls"
"github.com/xtls/xray-core/transport/internet/xtls" "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 rawConn syscall.RawConn
var sctx context.Context
allowUDP443 := false allowUDP443 := false
switch requestAddons.Flow { 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 allowUDP443 = true
requestAddons.Flow = requestAddons.Flow[:16] requestAddons.Flow = requestAddons.Flow[:16]
fallthrough fallthrough
case vless.XRO, vless.XRD, vless.XRS: case vless.XRO, vless.XRD, vless.XRS, vless.XRV:
switch request.Command { switch request.Command {
case protocol.RequestCommandMux: case protocol.RequestCommandMux:
return newError(requestAddons.Flow + " doesn't support Mux").AtWarning() 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 = "" requestAddons.Flow = ""
case protocol.RequestCommandTCP: 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.RPRX = true
xtlsConn.SHOW = xtls_show xtlsConn.SHOW = xtls_show
xtlsConn.MARK = "XTLS" xtlsConn.MARK = "XTLS"
if requestAddons.Flow == vless.XRS { if requestAddons.Flow == vless.XRS {
sctx = ctx
requestAddons.Flow = vless.XRD requestAddons.Flow = vless.XRD
} }
if 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) clientReader := link.Reader // .(*pipe.Reader)
clientWriter := link.Writer // .(*pipe.Writer) 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 { if request.Command == protocol.RequestCommandUDP && h.cone && request.Port != 53 && request.Port != 443 {
request.Command = protocol.RequestCommandMux 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 { if request.Command == protocol.RequestCommandMux && request.Port == 666 {
serverWriter = xudp.NewPacketWriter(serverWriter, target) 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 // ... return err // ...
} }
// Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer // Flush; bufferWriter.WriteMultiBufer now is bufferWriter.writer.WriteMultiBuffer
if err := bufferWriter.SetBuffered(false); err != nil { if err := bufferWriter.SetBuffered(false); err != nil {
return newError("failed to write A request payload").Base(err).AtWarning() return newError("failed to write A request payload").Base(err).AtWarning()
} }
// from clientReader.ReadMultiBuffer to serverWriter.WriteMultiBufer var err error
if err := buf.Copy(clientReader, serverWriter, buf.UpdateActivity(timer)); err != nil { 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() 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 { if statConn != nil {
counter = statConn.ReadCounter 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 { } else {
// from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer // from serverReader.ReadMultiBuffer to clientWriter.WriteMultiBufer
err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer)) err = buf.Copy(serverReader, clientWriter, buf.UpdateActivity(timer))

View File

@ -11,4 +11,5 @@ const (
XRO = "xtls-rprx-origin" XRO = "xtls-rprx-origin"
XRD = "xtls-rprx-direct" XRD = "xtls-rprx-direct"
XRS = "xtls-rprx-splice" XRS = "xtls-rprx-splice"
XRV = "xtls-rprx-vision"
) )