mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-12-22 23:57:26 +08:00
DRAFT 1
This commit is contained in:
parent
5bfad04b41
commit
4f27911659
@ -171,7 +171,7 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
|
|||||||
func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) {
|
func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) {
|
||||||
host, _, _ := net.SplitHostPort(v.addr)
|
host, _, _ := net.SplitHostPort(v.addr)
|
||||||
|
|
||||||
if v.isXTLSEnabled() && !isH2 {
|
if v.isLegacyXTLSEnabled() && !isH2 {
|
||||||
xtlsOpts := vless.XTLSConfig{
|
xtlsOpts := vless.XTLSConfig{
|
||||||
Host: host,
|
Host: host,
|
||||||
SkipCertVerify: v.option.SkipCertVerify,
|
SkipCertVerify: v.option.SkipCertVerify,
|
||||||
@ -206,8 +206,8 @@ func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error)
|
|||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Vless) isXTLSEnabled() bool {
|
func (v *Vless) isLegacyXTLSEnabled() bool {
|
||||||
return v.client.Addons != nil
|
return v.client.Addons != nil && v.client.Addons.Flow != vless.XRV
|
||||||
}
|
}
|
||||||
|
|
||||||
// DialContext implements C.ProxyAdapter
|
// DialContext implements C.ProxyAdapter
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
type Buffer = buf.Buffer
|
type Buffer = buf.Buffer
|
||||||
|
|
||||||
|
var StackNew = buf.StackNew
|
||||||
var StackNewSize = buf.StackNewSize
|
var StackNewSize = buf.StackNewSize
|
||||||
var KeepAlive = common.KeepAlive
|
var KeepAlive = common.KeepAlive
|
||||||
|
|
||||||
|
@ -1,12 +1,21 @@
|
|||||||
package vless
|
package vless
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
gotls "crypto/tls"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
|
"github.com/Dreamacro/clash/log"
|
||||||
|
utls "github.com/refraction-networking/utls"
|
||||||
|
"github.com/sagernet/sing/common/network"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/common/buf"
|
"github.com/Dreamacro/clash/common/buf"
|
||||||
N "github.com/Dreamacro/clash/common/net"
|
N "github.com/Dreamacro/clash/common/net"
|
||||||
@ -17,7 +26,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
N.ExtendedConn
|
network.ExtendedWriter
|
||||||
|
network.ExtendedReader
|
||||||
|
net.Conn
|
||||||
dst *DstAddr
|
dst *DstAddr
|
||||||
id *uuid.UUID
|
id *uuid.UUID
|
||||||
addons *Addons
|
addons *Addons
|
||||||
@ -26,30 +37,49 @@ type Conn struct {
|
|||||||
handshake chan struct{}
|
handshake chan struct{}
|
||||||
handshakeMutex sync.Mutex
|
handshakeMutex sync.Mutex
|
||||||
err error
|
err error
|
||||||
|
|
||||||
|
tlsConn net.Conn
|
||||||
|
input *bytes.Reader
|
||||||
|
rawInput *bytes.Buffer
|
||||||
|
|
||||||
|
packetsToFilter int
|
||||||
|
isTLS bool
|
||||||
|
isTLS12orAbove bool
|
||||||
|
enableXTLS bool
|
||||||
|
cipher uint16
|
||||||
|
remainingServerHello uint16
|
||||||
|
readRemainingContent uint16
|
||||||
|
readRemainingPadding uint16
|
||||||
|
readFilterUUID bool
|
||||||
|
readDirect bool
|
||||||
|
writeFilterApplicationData bool
|
||||||
|
writeDirect bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) Read(b []byte) (int, error) {
|
func (vc *Conn) Read(b []byte) (int, error) {
|
||||||
if vc.received {
|
if vc.received {
|
||||||
return vc.ExtendedConn.Read(b)
|
|
||||||
|
return vc.ExtendedReader.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vc.recvResponse(); err != nil {
|
if err := vc.recvResponse(); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
vc.received = true
|
vc.received = true
|
||||||
return vc.ExtendedConn.Read(b)
|
return vc.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
|
func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
|
||||||
if vc.received {
|
if vc.received {
|
||||||
return vc.ExtendedConn.ReadBuffer(buffer)
|
|
||||||
|
return vc.ExtendedReader.ReadBuffer(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vc.recvResponse(); err != nil {
|
if err := vc.recvResponse(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
vc.received = true
|
vc.received = true
|
||||||
return vc.ExtendedConn.ReadBuffer(buffer)
|
return vc.ReadBuffer(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) Write(p []byte) (int, error) {
|
func (vc *Conn) Write(p []byte) (int, error) {
|
||||||
@ -66,7 +96,19 @@ func (vc *Conn) Write(p []byte) (int, error) {
|
|||||||
return 0, vc.err
|
return 0, vc.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vc.ExtendedConn.Write(p)
|
if vc.writeFilterApplicationData {
|
||||||
|
_buffer := buf.StackNew()
|
||||||
|
defer buf.KeepAlive(_buffer)
|
||||||
|
buffer := buf.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
buffer.Write(p)
|
||||||
|
err := vc.WriteBuffer(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
return vc.ExtendedWriter.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
@ -80,7 +122,48 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
|||||||
return vc.err
|
return vc.err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vc.ExtendedConn.WriteBuffer(buffer)
|
if vc.writeFilterApplicationData && vc.isTLS {
|
||||||
|
buffer2 := ReshapeBuffer(buffer)
|
||||||
|
defer buffer2.Release()
|
||||||
|
if buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) {
|
||||||
|
command := commandPaddingEnd
|
||||||
|
if vc.enableXTLS {
|
||||||
|
command = commandPaddingDirect
|
||||||
|
vc.writeDirect = true
|
||||||
|
}
|
||||||
|
vc.writeFilterApplicationData = false
|
||||||
|
ApplyPadding(buffer, command, vc.id)
|
||||||
|
}
|
||||||
|
err := vc.ExtendedWriter.WriteBuffer(buffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if vc.writeDirect {
|
||||||
|
vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn)
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
}
|
||||||
|
if buffer2 != nil {
|
||||||
|
if vc.writeDirect {
|
||||||
|
return vc.ExtendedWriter.WriteBuffer(buffer2)
|
||||||
|
}
|
||||||
|
if buffer2.Len() > 6 && bytes.Equal(buffer2.To(3), tlsApplicationDataStart) {
|
||||||
|
command := commandPaddingEnd
|
||||||
|
if vc.enableXTLS {
|
||||||
|
command = commandPaddingDirect
|
||||||
|
vc.writeDirect = true
|
||||||
|
}
|
||||||
|
vc.writeFilterApplicationData = false
|
||||||
|
ApplyPadding(buffer2, command, vc.id)
|
||||||
|
}
|
||||||
|
err = vc.ExtendedWriter.WriteBuffer(buffer2)
|
||||||
|
}
|
||||||
|
if vc.writeDirect {
|
||||||
|
vc.ExtendedWriter = N.NewExtendedWriter(vc.Conn)
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return vc.ExtendedWriter.WriteBuffer(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) sendRequest(p []byte) bool {
|
func (vc *Conn) sendRequest(p []byte) bool {
|
||||||
@ -96,9 +179,6 @@ func (vc *Conn) sendRequest(p []byte) bool {
|
|||||||
}
|
}
|
||||||
defer close(vc.handshake)
|
defer close(vc.handshake)
|
||||||
|
|
||||||
requestLen := 1 // protocol version
|
|
||||||
requestLen += 16 // UUID
|
|
||||||
requestLen += 1 // addons length
|
|
||||||
var addonsBytes []byte
|
var addonsBytes []byte
|
||||||
if vc.addons != nil {
|
if vc.addons != nil {
|
||||||
addonsBytes, vc.err = proto.Marshal(vc.addons)
|
addonsBytes, vc.err = proto.Marshal(vc.addons)
|
||||||
@ -106,19 +186,32 @@ func (vc *Conn) sendRequest(p []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requestLen += len(addonsBytes)
|
isVision := vc.IsXTLSVisionEnabled()
|
||||||
requestLen += 1 // command
|
|
||||||
if !vc.dst.Mux {
|
|
||||||
requestLen += 2 // port
|
|
||||||
requestLen += 1 // addr type
|
|
||||||
requestLen += len(vc.dst.Addr)
|
|
||||||
}
|
|
||||||
requestLen += len(p)
|
|
||||||
|
|
||||||
_buffer := buf.StackNewSize(requestLen)
|
var buffer *buf.Buffer
|
||||||
defer buf.KeepAlive(_buffer)
|
if isVision {
|
||||||
buffer := buf.Dup(_buffer)
|
_buffer := buf.StackNew()
|
||||||
defer buffer.Release()
|
defer buf.KeepAlive(_buffer)
|
||||||
|
buffer = buf.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
} else {
|
||||||
|
requestLen := 1 // protocol version
|
||||||
|
requestLen += 16 // UUID
|
||||||
|
requestLen += 1 // addons length
|
||||||
|
requestLen += len(addonsBytes)
|
||||||
|
requestLen += 1 // command
|
||||||
|
if !vc.dst.Mux {
|
||||||
|
requestLen += 2 // port
|
||||||
|
requestLen += 1 // addr type
|
||||||
|
requestLen += len(vc.dst.Addr)
|
||||||
|
}
|
||||||
|
requestLen += len(p)
|
||||||
|
|
||||||
|
_buffer := buf.StackNewSize(requestLen)
|
||||||
|
defer buf.KeepAlive(_buffer)
|
||||||
|
buffer = buf.Dup(_buffer)
|
||||||
|
defer buffer.Release()
|
||||||
|
}
|
||||||
|
|
||||||
buf.Must(
|
buf.Must(
|
||||||
buffer.WriteByte(Version), // protocol version
|
buffer.WriteByte(Version), // protocol version
|
||||||
@ -143,15 +236,52 @@ func (vc *Conn) sendRequest(p []byte) bool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf.Must(buf.Error(buffer.Write(p)))
|
if isVision && !vc.dst.UDP && !vc.dst.Mux {
|
||||||
|
if len(p) == 0 {
|
||||||
|
vc.packetsToFilter = 0
|
||||||
|
vc.writeFilterApplicationData = false
|
||||||
|
WriteWithPadding(buffer, nil, commandPaddingEnd, vc.id)
|
||||||
|
} else {
|
||||||
|
vc.FilterTLS(p)
|
||||||
|
if vc.isTLS {
|
||||||
|
WriteWithPadding(buffer, p, commandPaddingContinue, vc.id)
|
||||||
|
} else {
|
||||||
|
buf.Must(buf.Error(buffer.Write(p)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
buf.Must(buf.Error(buffer.Write(p)))
|
||||||
|
}
|
||||||
|
|
||||||
_, vc.err = vc.ExtendedConn.Write(buffer.Bytes())
|
_, vc.err = vc.ExtendedWriter.Write(buffer.Bytes())
|
||||||
|
if vc.err != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if isVision {
|
||||||
|
switch underlying := vc.tlsConn.(type) {
|
||||||
|
case *gotls.Conn:
|
||||||
|
if underlying.ConnectionState().Version != gotls.VersionTLS13 {
|
||||||
|
vc.err = ErrNotTLS13
|
||||||
|
}
|
||||||
|
case *utls.UConn:
|
||||||
|
if underlying.ConnectionState().Version != utls.VersionTLS13 {
|
||||||
|
vc.err = ErrNotTLS13
|
||||||
|
}
|
||||||
|
case *tlsC.UConn:
|
||||||
|
if underlying.ConnectionState().Version != utls.VersionTLS13 {
|
||||||
|
vc.err = ErrNotTLS13
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
vc.err = fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, vc.addons.Flow)
|
||||||
|
}
|
||||||
|
vc.tlsConn = nil
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vc *Conn) recvResponse() error {
|
func (vc *Conn) recvResponse() error {
|
||||||
var buf [1]byte
|
var buf [1]byte
|
||||||
_, vc.err = io.ReadFull(vc.ExtendedConn, buf[:])
|
_, vc.err = io.ReadFull(vc.ExtendedReader, buf[:])
|
||||||
if vc.err != nil {
|
if vc.err != nil {
|
||||||
return vc.err
|
return vc.err
|
||||||
}
|
}
|
||||||
@ -160,30 +290,43 @@ func (vc *Conn) recvResponse() error {
|
|||||||
return errors.New("unexpected response version")
|
return errors.New("unexpected response version")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, vc.err = io.ReadFull(vc.ExtendedConn, buf[:])
|
_, vc.err = io.ReadFull(vc.ExtendedReader, buf[:])
|
||||||
if vc.err != nil {
|
if vc.err != nil {
|
||||||
return vc.err
|
return vc.err
|
||||||
}
|
}
|
||||||
|
|
||||||
length := int64(buf[0])
|
length := int64(buf[0])
|
||||||
if length != 0 { // addon data length > 0
|
if length != 0 { // addon data length > 0
|
||||||
io.CopyN(io.Discard, vc.ExtendedConn, length) // just discard
|
io.CopyN(io.Discard, vc.ExtendedReader, length) // just discard
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (vc *Conn) FrontHeadroom() int {
|
||||||
|
if vc.IsXTLSVisionEnabled() {
|
||||||
|
return paddingHeaderLen
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
func (vc *Conn) Upstream() any {
|
func (vc *Conn) Upstream() any {
|
||||||
return vc.ExtendedConn
|
return vc.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vc *Conn) IsXTLSVisionEnabled() bool {
|
||||||
|
return vc.addons != nil && vc.addons.Flow == XRV
|
||||||
}
|
}
|
||||||
|
|
||||||
// newConn return a Conn instance
|
// newConn return a Conn instance
|
||||||
func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
|
func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
|
||||||
c := &Conn{
|
c := &Conn{
|
||||||
ExtendedConn: N.NewExtendedConn(conn),
|
ExtendedReader: N.NewExtendedReader(conn),
|
||||||
id: client.uuid,
|
ExtendedWriter: N.NewExtendedWriter(conn),
|
||||||
dst: dst,
|
Conn: conn,
|
||||||
handshake: make(chan struct{}),
|
id: client.uuid,
|
||||||
|
dst: dst,
|
||||||
|
handshake: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
if !dst.UDP && client.Addons != nil {
|
if !dst.UDP && client.Addons != nil {
|
||||||
@ -204,15 +347,46 @@ func newConn(conn net.Conn, client *Client, dst *DstAddr) (*Conn, error) {
|
|||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", client.Addons.Flow)
|
return nil, fmt.Errorf("failed to use %s, maybe \"security\" is not \"xtls\"", client.Addons.Flow)
|
||||||
}
|
}
|
||||||
|
case XRV:
|
||||||
|
c.packetsToFilter = 2048
|
||||||
|
c.writeFilterApplicationData = true
|
||||||
|
var t reflect.Type
|
||||||
|
var p uintptr
|
||||||
|
switch underlying := conn.(type) {
|
||||||
|
case *gotls.Conn:
|
||||||
|
c.Conn = underlying.NetConn()
|
||||||
|
c.tlsConn = conn
|
||||||
|
t = reflect.TypeOf(underlying).Elem()
|
||||||
|
p = uintptr(unsafe.Pointer(underlying))
|
||||||
|
case *utls.UConn:
|
||||||
|
c.Conn = underlying.NetConn()
|
||||||
|
c.tlsConn = conn
|
||||||
|
t = reflect.TypeOf(underlying.Conn).Elem()
|
||||||
|
p = uintptr(unsafe.Pointer(underlying.Conn))
|
||||||
|
case *tlsC.UConn:
|
||||||
|
c.Conn = underlying.NetConn()
|
||||||
|
c.tlsConn = underlying.UConn
|
||||||
|
t = reflect.TypeOf(underlying.Conn).Elem()
|
||||||
|
p = uintptr(unsafe.Pointer(underlying.Conn))
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf(`failed to use %s, maybe "security" is not "tls" or "utls"`, client.Addons.Flow)
|
||||||
|
}
|
||||||
|
i, _ := t.FieldByName("input")
|
||||||
|
r, _ := t.FieldByName("rawInput")
|
||||||
|
c.input = (*bytes.Reader)(unsafe.Pointer(p + i.Offset))
|
||||||
|
c.rawInput = (*bytes.Buffer)(unsafe.Pointer(p + r.Offset))
|
||||||
|
if _, ok := c.Conn.(*net.TCPConn); !ok {
|
||||||
|
log.Debugln("XTLS underlying conn is not *net.TCPConn, got", reflect.TypeOf(conn).Name())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//go func() {
|
go func() {
|
||||||
// select {
|
select {
|
||||||
// case <-c.handshake:
|
case <-c.handshake:
|
||||||
// case <-time.After(200 * time.Millisecond):
|
case <-time.After(200 * time.Millisecond):
|
||||||
// c.sendRequest(nil)
|
c.sendRequest(nil)
|
||||||
// }
|
}
|
||||||
//}()
|
}()
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
76
transport/vless/filter.go
Normal file
76
transport/vless/filter.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
package vless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
tls13SupportedVersions = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04}
|
||||||
|
tlsClientHandshakeStart = []byte{0x16, 0x03}
|
||||||
|
tlsServerHandshakeStart = []byte{0x16, 0x03, 0x03}
|
||||||
|
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
|
||||||
|
|
||||||
|
tls13CipherSuiteMap = map[uint16]string{
|
||||||
|
0x1301: "TLS_AES_128_GCM_SHA256",
|
||||||
|
0x1302: "TLS_AES_256_GCM_SHA384",
|
||||||
|
0x1303: "TLS_CHACHA20_POLY1305_SHA256",
|
||||||
|
0x1304: "TLS_AES_128_CCM_SHA256",
|
||||||
|
0x1305: "TLS_AES_128_CCM_8_SHA256",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tlsHandshakeTypeClientHello byte = 0x01
|
||||||
|
tlsHandshakeTypeServerHello byte = 0x02
|
||||||
|
)
|
||||||
|
|
||||||
|
func (vc *Conn) FilterTLS(p []byte) (index int) {
|
||||||
|
lenP := len(p)
|
||||||
|
vc.packetsToFilter -= 1
|
||||||
|
if index = bytes.Index(p, tlsServerHandshakeStart); index != -1 {
|
||||||
|
if lenP >= index+5 && p[index+5] == tlsHandshakeTypeServerHello {
|
||||||
|
vc.remainingServerHello = binary.BigEndian.Uint16(p[index+3:]) + 5
|
||||||
|
vc.isTLS = true
|
||||||
|
vc.isTLS12orAbove = true
|
||||||
|
if lenP-index >= 79 && vc.remainingServerHello >= 79 {
|
||||||
|
sessionIDLen := int(p[index+43])
|
||||||
|
vc.cipher = binary.BigEndian.Uint16(p[index+43+sessionIDLen+1:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if index = bytes.Index(p, tlsClientHandshakeStart); index != -1 {
|
||||||
|
if lenP >= index+5 && p[index+5] == tlsHandshakeTypeClientHello {
|
||||||
|
vc.isTLS = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if vc.remainingServerHello > 0 {
|
||||||
|
end := vc.remainingServerHello
|
||||||
|
vc.remainingServerHello -= end
|
||||||
|
if end > uint16(lenP) {
|
||||||
|
end = uint16(lenP)
|
||||||
|
}
|
||||||
|
if bytes.Contains(p[index:end], tls13SupportedVersions) {
|
||||||
|
// TLS 1.3 Client Hello
|
||||||
|
cs, ok := tls13CipherSuiteMap[vc.cipher]
|
||||||
|
if ok && cs != "TLS_AES_128_CCM_8_SHA256" {
|
||||||
|
vc.enableXTLS = true
|
||||||
|
}
|
||||||
|
log.Debugln("XTLS Vision found TLS 1.3, packetLength=", lenP, ", CipherSuite=", cs)
|
||||||
|
vc.packetsToFilter = 0
|
||||||
|
return
|
||||||
|
} else if vc.remainingServerHello < 0 {
|
||||||
|
log.Debugln("XTLS Vision found TLS 1.2, packetLength=", lenP)
|
||||||
|
vc.packetsToFilter = 0
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugln("XTLS Vision found inconclusive server hello, packetLength=", lenP,
|
||||||
|
", remainingServerHelloBytes=", vc.remainingServerHello)
|
||||||
|
}
|
||||||
|
if vc.packetsToFilter <= 0 {
|
||||||
|
log.Debugln("XTLS Vision stop filtering")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
67
transport/vless/vision.go
Normal file
67
transport/vless/vision.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package vless
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/Dreamacro/clash/common/buf"
|
||||||
|
|
||||||
|
"github.com/gofrs/uuid"
|
||||||
|
buf2 "github.com/sagernet/sing/common/buf"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
paddingHeaderLen = 16 + 1 + 2 + 2 // =21
|
||||||
|
|
||||||
|
commandPaddingContinue byte = 0x00
|
||||||
|
commandPaddingEnd byte = 0x01
|
||||||
|
commandPaddingDirect byte = 0x02
|
||||||
|
)
|
||||||
|
|
||||||
|
func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid.UUID) {
|
||||||
|
contentLen := int32(len(p))
|
||||||
|
var paddingLen int32
|
||||||
|
if contentLen < 900 {
|
||||||
|
paddingLen = rand.Int31n(500) + 900 - contentLen
|
||||||
|
}
|
||||||
|
|
||||||
|
if userUUID != nil { // unnecessary, but keep the same with Xray
|
||||||
|
buffer.Write(userUUID.Bytes())
|
||||||
|
}
|
||||||
|
buffer.WriteByte(command)
|
||||||
|
binary.BigEndian.PutUint16(buffer.Extend(2), uint16(contentLen))
|
||||||
|
binary.BigEndian.PutUint16(buffer.Extend(2), uint16(paddingLen))
|
||||||
|
buffer.Write(p)
|
||||||
|
buffer.Extend(int(paddingLen))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID) {
|
||||||
|
contentLen := int32(buffer.Len())
|
||||||
|
var paddingLen int32
|
||||||
|
if contentLen < 900 {
|
||||||
|
paddingLen = rand.Int31n(500) + 900 - contentLen
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(paddingLen))
|
||||||
|
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(contentLen))
|
||||||
|
buffer.ExtendHeader(1)[0] = command
|
||||||
|
if userUUID != nil { // unnecessary, but keep the same with Xray
|
||||||
|
copy(buffer.ExtendHeader(uuid.Size), userUUID.Bytes())
|
||||||
|
}
|
||||||
|
buffer.Extend(int(paddingLen))
|
||||||
|
}
|
||||||
|
|
||||||
|
func ReshapeBuffer(buffer *buf.Buffer) *buf.Buffer {
|
||||||
|
if buffer.Len() <= buf2.BufferSize-paddingHeaderLen {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cutAt := bytes.LastIndex(buffer.Bytes(), tlsApplicationDataStart)
|
||||||
|
if cutAt == -1 {
|
||||||
|
cutAt = buf2.BufferSize / 2
|
||||||
|
}
|
||||||
|
buffer2 := buf2.New()
|
||||||
|
buffer2.Write(buffer.From(cutAt))
|
||||||
|
buffer.Truncate(cutAt)
|
||||||
|
return buffer2
|
||||||
|
}
|
@ -12,6 +12,7 @@ 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"
|
||||||
|
|
||||||
Version byte = 0 // protocol version. preview version is 0
|
Version byte = 0 // protocol version. preview version is 0
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ package vless
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
tlsC "github.com/Dreamacro/clash/component/tls"
|
tlsC "github.com/Dreamacro/clash/component/tls"
|
||||||
@ -9,6 +10,10 @@ import (
|
|||||||
xtls "github.com/xtls/go"
|
xtls "github.com/xtls/go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNotTLS13 = errors.New("XTLS Vision based on TLS 1.3 outer connection")
|
||||||
|
)
|
||||||
|
|
||||||
type XTLSConfig struct {
|
type XTLSConfig struct {
|
||||||
Host string
|
Host string
|
||||||
SkipCertVerify bool
|
SkipCertVerify bool
|
||||||
|
@ -28,7 +28,7 @@ type trackerInfo struct {
|
|||||||
RulePayload string `json:"rulePayload"`
|
RulePayload string `json:"rulePayload"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type tcpTracker struct {
|
type TCPTracker struct {
|
||||||
C.Conn `json:"-"`
|
C.Conn `json:"-"`
|
||||||
*trackerInfo
|
*trackerInfo
|
||||||
manager *Manager
|
manager *Manager
|
||||||
@ -36,11 +36,16 @@ type tcpTracker struct {
|
|||||||
extendedWriter N.ExtendedWriter
|
extendedWriter N.ExtendedWriter
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) ID() string {
|
func (tt *TCPTracker) ID() string {
|
||||||
return tt.UUID.String()
|
return tt.UUID.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Read(b []byte) (int, error) {
|
func (tt *TCPTracker) AddDownload(n int64) {
|
||||||
|
tt.manager.PushDownloaded(n)
|
||||||
|
tt.DownloadTotal.Add(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *TCPTracker) Read(b []byte) (int, error) {
|
||||||
n, err := tt.Conn.Read(b)
|
n, err := tt.Conn.Read(b)
|
||||||
download := int64(n)
|
download := int64(n)
|
||||||
tt.manager.PushDownloaded(download)
|
tt.manager.PushDownloaded(download)
|
||||||
@ -48,7 +53,7 @@ func (tt *tcpTracker) Read(b []byte) (int, error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) {
|
func (tt *TCPTracker) ReadBuffer(buffer *buf.Buffer) (err error) {
|
||||||
err = tt.extendedReader.ReadBuffer(buffer)
|
err = tt.extendedReader.ReadBuffer(buffer)
|
||||||
download := int64(buffer.Len())
|
download := int64(buffer.Len())
|
||||||
tt.manager.PushDownloaded(download)
|
tt.manager.PushDownloaded(download)
|
||||||
@ -56,7 +61,12 @@ func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Write(b []byte) (int, error) {
|
func (tt *TCPTracker) AddUpload(n int64) {
|
||||||
|
tt.manager.PushUploaded(n)
|
||||||
|
tt.UploadTotal.Add(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tt *TCPTracker) Write(b []byte) (int, error) {
|
||||||
n, err := tt.Conn.Write(b)
|
n, err := tt.Conn.Write(b)
|
||||||
upload := int64(n)
|
upload := int64(n)
|
||||||
tt.manager.PushUploaded(upload)
|
tt.manager.PushUploaded(upload)
|
||||||
@ -64,7 +74,7 @@ func (tt *tcpTracker) Write(b []byte) (int, error) {
|
|||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) {
|
func (tt *TCPTracker) WriteBuffer(buffer *buf.Buffer) (err error) {
|
||||||
upload := int64(buffer.Len())
|
upload := int64(buffer.Len())
|
||||||
err = tt.extendedWriter.WriteBuffer(buffer)
|
err = tt.extendedWriter.WriteBuffer(buffer)
|
||||||
tt.manager.PushUploaded(upload)
|
tt.manager.PushUploaded(upload)
|
||||||
@ -72,16 +82,16 @@ func (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Close() error {
|
func (tt *TCPTracker) Close() error {
|
||||||
tt.manager.Leave(tt)
|
tt.manager.Leave(tt)
|
||||||
return tt.Conn.Close()
|
return tt.Conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tt *tcpTracker) Upstream() any {
|
func (tt *TCPTracker) Upstream() any {
|
||||||
return tt.Conn
|
return tt.Conn
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule) *tcpTracker {
|
func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule) *TCPTracker {
|
||||||
uuid, _ := uuid.NewV4()
|
uuid, _ := uuid.NewV4()
|
||||||
if conn != nil {
|
if conn != nil {
|
||||||
if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
|
if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok {
|
||||||
@ -91,7 +101,7 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t := &tcpTracker{
|
t := &TCPTracker{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
manager: manager,
|
manager: manager,
|
||||||
trackerInfo: &trackerInfo{
|
trackerInfo: &trackerInfo{
|
||||||
|
Loading…
Reference in New Issue
Block a user