diff --git a/transport/vless/addons.go b/transport/vless/addons.go new file mode 100644 index 00000000..2257adeb --- /dev/null +++ b/transport/vless/addons.go @@ -0,0 +1,61 @@ +package vless + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" +) + +func ReadAddons(data []byte) (*Addons, error) { + reader := bytes.NewReader(data) + var addons Addons + for reader.Len() > 0 { + protoHeader, err := reader.ReadByte() + if err != nil { + return nil, err + } + switch protoHeader { + case (1 << 3) | 2: + flowLen, err := binary.ReadUvarint(reader) + if err != nil { + return nil, err + } + flowBytes := make([]byte, flowLen) + _, err = io.ReadFull(reader, flowBytes) + if err != nil { + return nil, err + } + addons.Flow = string(flowBytes) + case (2 << 3) | 2: + seedLen, err := binary.ReadUvarint(reader) + if err != nil { + return nil, err + } + seedBytes := make([]byte, seedLen) + _, err = io.ReadFull(reader, seedBytes) + if err != nil { + return nil, err + } + addons.Seed = seedBytes + default: + return nil, fmt.Errorf("unknown protobuf message header: %v", protoHeader) + } + } + return &addons, nil +} + +func WriteAddons(addons *Addons) []byte { + var writer bytes.Buffer + if len(addons.Flow) > 0 { + writer.WriteByte((1 << 3) | 2) + writer.Write(binary.AppendUvarint(nil, uint64(len(addons.Flow)))) + writer.WriteString(addons.Flow) + } + if len(addons.Seed) > 0 { + writer.WriteByte((2 << 3) | 2) + writer.Write(binary.AppendUvarint(nil, uint64(len(addons.Seed)))) + writer.Write(addons.Seed) + } + return writer.Bytes() +} diff --git a/transport/vless/addons_test.go b/transport/vless/addons_test.go new file mode 100644 index 00000000..60a27a06 --- /dev/null +++ b/transport/vless/addons_test.go @@ -0,0 +1,95 @@ +package vless + +import ( + "bytes" + "strconv" + "testing" + + "google.golang.org/protobuf/proto" +) + +func TestAddons(t *testing.T) { + var tests = []struct { + flow string + seed []byte + }{ + {XRV, nil}, + {XRS, []byte{1, 2, 3}}, + {"", []byte{1, 2, 3}}, + {"", nil}, + } + + for i, test := range tests { + t.Run(strconv.Itoa(i), func(t *testing.T) { + t.Run("proto->handwritten", func(t *testing.T) { + addons := new(Addons) + addons.Flow = test.flow + addons.Seed = test.seed + + addonsBytes, err := proto.Marshal(addons) + if err != nil { + t.Errorf("error marshalling addons: %v", err) + return + } + addons, err = ReadAddons(addonsBytes) + if err != nil { + t.Errorf("error reading addons: %v", err) + return + } + + if addons.Flow != test.flow { + t.Errorf("got %v; want %v", addons.Flow, test.flow) + return + } + if !bytes.Equal(addons.Seed, test.seed) { + t.Errorf("got %v; want %v", addons.Seed, test.seed) + return + } + }) + + t.Run("handwritten->proto", func(t *testing.T) { + addons := new(Addons) + addons.Flow = test.flow + addons.Seed = test.seed + + addonsBytes := WriteAddons(addons) + err := proto.Unmarshal(addonsBytes, addons) + if err != nil { + t.Errorf("error reading addons: %v", err) + return + } + + if addons.Flow != test.flow { + t.Errorf("got %v; want %v", addons.Flow, test.flow) + return + } + if !bytes.Equal(addons.Seed, test.seed) { + t.Errorf("got %v; want %v", addons.Seed, test.seed) + return + } + }) + + t.Run("handwritten->handwritten", func(t *testing.T) { + addons := new(Addons) + addons.Flow = test.flow + addons.Seed = test.seed + + addonsBytes := WriteAddons(addons) + addons, err := ReadAddons(addonsBytes) + if err != nil { + t.Errorf("error reading addons: %v", err) + return + } + + if addons.Flow != test.flow { + t.Errorf("got %v; want %v", addons.Flow, test.flow) + return + } + if !bytes.Equal(addons.Seed, test.seed) { + t.Errorf("got %v; want %v", addons.Seed, test.seed) + return + } + }) + }) + } +} diff --git a/transport/vless/vision/conn.go b/transport/vless/vision/conn.go index 2801091c..fd9f787b 100644 --- a/transport/vless/vision/conn.go +++ b/transport/vless/vision/conn.go @@ -2,7 +2,6 @@ package vision import ( "bytes" - "crypto/subtle" "encoding/binary" "errors" "fmt" @@ -57,8 +56,8 @@ func (vc *Conn) Read(b []byte) (int, error) { } func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { - toRead := buffer.FreeBytes() if vc.readRemainingContent > 0 { + toRead := buffer.FreeBytes() if vc.readRemainingContent < buffer.FreeLen() { toRead = toRead[:vc.readRemainingContent] } @@ -79,12 +78,12 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { switch vc.readLastCommand { case commandPaddingContinue: //if vc.isTLS || vc.packetsToFilter > 0 { - headerUUIDLen := 0 - if vc.readFilterUUID { - headerUUIDLen = uuid.Size + need := PaddingHeaderLen + if !vc.readFilterUUID { + need = PaddingHeaderLen - uuid.Size } var header []byte - if need := headerUUIDLen + PaddingHeaderLen - uuid.Size; buffer.FreeLen() < need { + if buffer.FreeLen() < need { header = make([]byte, need) } else { header = buffer.FreeBytes()[:need] @@ -95,9 +94,8 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { } if vc.readFilterUUID { vc.readFilterUUID = false - if subtle.ConstantTimeCompare(vc.userUUID.Bytes(), header[:uuid.Size]) != 1 { - err = fmt.Errorf("XTLS Vision server responded unknown UUID: %s", - uuid.FromBytesOrNil(header[:uuid.Size]).String()) + if !bytes.Equal(vc.userUUID.Bytes(), header[:uuid.Size]) { + err = fmt.Errorf("XTLS Vision server responded unknown UUID: %s", uuid.FromBytesOrNil(header[:uuid.Size])) log.Errorln(err.Error()) return err } @@ -180,7 +178,7 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) { for i, buffer := range buffers { command := commandPaddingContinue if applyPadding { - if vc.isTLS && buffer.Len() > 6 && bytes.Equal(buffer.To(3), tlsApplicationDataStart) { + if vc.isTLS && buffer.Len() > 6 && bytes.Equal(tlsApplicationDataStart, buffer.To(3)) { command = commandPaddingEnd if vc.enableXTLS { command = commandPaddingDirect diff --git a/transport/vless/vision/padding.go b/transport/vless/vision/padding.go index 3152139a..38609552 100644 --- a/transport/vless/vision/padding.go +++ b/transport/vless/vision/padding.go @@ -5,8 +5,8 @@ import ( "encoding/binary" "github.com/metacubex/mihomo/common/buf" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/log" - N "github.com/metacubex/sing/common/network" "github.com/gofrs/uuid/v5" "github.com/metacubex/randv2"