chore: ready for handwritten addons parsing

This commit is contained in:
wwqgtxx 2025-09-08 21:51:30 +08:00
parent 108bf645fa
commit 0d3d31dc5f
4 changed files with 165 additions and 11 deletions

61
transport/vless/addons.go Normal file
View File

@ -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()
}

View File

@ -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
}
})
})
}
}

View File

@ -2,7 +2,6 @@ package vision
import ( import (
"bytes" "bytes"
"crypto/subtle"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt" "fmt"
@ -57,8 +56,8 @@ func (vc *Conn) Read(b []byte) (int, error) {
} }
func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error { func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
toRead := buffer.FreeBytes()
if vc.readRemainingContent > 0 { if vc.readRemainingContent > 0 {
toRead := buffer.FreeBytes()
if vc.readRemainingContent < buffer.FreeLen() { if vc.readRemainingContent < buffer.FreeLen() {
toRead = toRead[:vc.readRemainingContent] toRead = toRead[:vc.readRemainingContent]
} }
@ -79,12 +78,12 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
switch vc.readLastCommand { switch vc.readLastCommand {
case commandPaddingContinue: case commandPaddingContinue:
//if vc.isTLS || vc.packetsToFilter > 0 { //if vc.isTLS || vc.packetsToFilter > 0 {
headerUUIDLen := 0 need := PaddingHeaderLen
if vc.readFilterUUID { if !vc.readFilterUUID {
headerUUIDLen = uuid.Size need = PaddingHeaderLen - uuid.Size
} }
var header []byte var header []byte
if need := headerUUIDLen + PaddingHeaderLen - uuid.Size; buffer.FreeLen() < need { if buffer.FreeLen() < need {
header = make([]byte, need) header = make([]byte, need)
} else { } else {
header = buffer.FreeBytes()[:need] header = buffer.FreeBytes()[:need]
@ -95,9 +94,8 @@ func (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {
} }
if vc.readFilterUUID { if vc.readFilterUUID {
vc.readFilterUUID = false vc.readFilterUUID = false
if subtle.ConstantTimeCompare(vc.userUUID.Bytes(), header[:uuid.Size]) != 1 { if !bytes.Equal(vc.userUUID.Bytes(), header[:uuid.Size]) {
err = fmt.Errorf("XTLS Vision server responded unknown UUID: %s", err = fmt.Errorf("XTLS Vision server responded unknown UUID: %s", uuid.FromBytesOrNil(header[:uuid.Size]))
uuid.FromBytesOrNil(header[:uuid.Size]).String())
log.Errorln(err.Error()) log.Errorln(err.Error())
return err return err
} }
@ -180,7 +178,7 @@ func (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) {
for i, buffer := range buffers { for i, buffer := range buffers {
command := commandPaddingContinue command := commandPaddingContinue
if applyPadding { 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 command = commandPaddingEnd
if vc.enableXTLS { if vc.enableXTLS {
command = commandPaddingDirect command = commandPaddingDirect

View File

@ -5,8 +5,8 @@ import (
"encoding/binary" "encoding/binary"
"github.com/metacubex/mihomo/common/buf" "github.com/metacubex/mihomo/common/buf"
N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/log"
N "github.com/metacubex/sing/common/network"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
"github.com/metacubex/randv2" "github.com/metacubex/randv2"