From 108bf645fa73f79077460195fe85f0ed4553e1e6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 8 Sep 2025 16:00:07 +0800 Subject: [PATCH] chore: merge the server-side and client-side vision implementations --- listener/sing_vless/server.go | 35 +--- listener/sing_vless/service.go | 304 +++++++++++++++++++++++++++++++++ 2 files changed, 306 insertions(+), 33 deletions(-) create mode 100644 listener/sing_vless/service.go diff --git a/listener/sing_vless/server.go b/listener/sing_vless/server.go index 3a5943b5..d4fc3973 100644 --- a/listener/sing_vless/server.go +++ b/listener/sing_vless/server.go @@ -5,9 +5,7 @@ import ( "errors" "net" "net/http" - "reflect" "strings" - "unsafe" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/ca" @@ -17,48 +15,19 @@ import ( LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/reality" "github.com/metacubex/mihomo/listener/sing" - "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/gun" "github.com/metacubex/mihomo/transport/vless/encryption" mihomoVMess "github.com/metacubex/mihomo/transport/vmess" - "github.com/metacubex/sing-vmess/vless" "github.com/metacubex/sing/common" "github.com/metacubex/sing/common/metadata" - "github.com/metacubex/sing/common/network" ) -func init() { - vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) { - tlsConn, loaded := network.CastReader[*reality.Conn](conn) // *utls.Conn - if !loaded { - return - } - return true, tlsConn.NetConn(), reflect.TypeOf(tlsConn).Elem(), unsafe.Pointer(tlsConn) - }) - - vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) { - tlsConn, loaded := network.CastReader[*tlsC.UConn](conn) // *utls.UConn - if !loaded { - return - } - return true, tlsConn.NetConn(), reflect.TypeOf(tlsConn.Conn).Elem(), unsafe.Pointer(tlsConn.Conn) - }) - - vless.RegisterTLS(func(conn net.Conn) (loaded bool, netConn net.Conn, reflectType reflect.Type, reflectPointer unsafe.Pointer) { - tlsConn, loaded := network.CastReader[*encryption.CommonConn](conn) - if !loaded { - return - } - return true, tlsConn.Conn, reflect.TypeOf(tlsConn).Elem(), unsafe.Pointer(tlsConn) - }) -} - type Listener struct { closed bool config LC.VlessServer listeners []net.Listener - service *vless.Service[string] + service *Service[string] decryption *encryption.ServerInstance } @@ -79,7 +48,7 @@ func New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) return nil, err } - service := vless.NewService[string](log.SingLogger, h) + service := NewService[string](h) service.UpdateUsers( common.Map(config.Users, func(it LC.VlessUser) string { return it.Username diff --git a/listener/sing_vless/service.go b/listener/sing_vless/service.go new file mode 100644 index 00000000..d30b3c01 --- /dev/null +++ b/listener/sing_vless/service.go @@ -0,0 +1,304 @@ +package sing_vless + +// copy and modify from https://github.com/SagerNet/sing-vmess/tree/3c1cf255413250b09a57e4ecdf1def1fa505e3cc/vless + +import ( + "context" + "encoding/binary" + "io" + "net" + + "github.com/metacubex/mihomo/transport/vless" + "github.com/metacubex/mihomo/transport/vless/vision" + + "github.com/gofrs/uuid/v5" + "github.com/metacubex/sing-vmess" + "github.com/metacubex/sing/common/auth" + "github.com/metacubex/sing/common/buf" + "github.com/metacubex/sing/common/bufio" + E "github.com/metacubex/sing/common/exceptions" + M "github.com/metacubex/sing/common/metadata" + N "github.com/metacubex/sing/common/network" + "google.golang.org/protobuf/proto" +) + +type Service[T comparable] struct { + userMap map[[16]byte]T + userFlow map[T]string + handler Handler +} + +type Handler interface { + N.TCPConnectionHandler + N.UDPConnectionHandler + E.Handler +} + +func NewService[T comparable](handler Handler) *Service[T] { + return &Service[T]{ + handler: handler, + } +} + +func (s *Service[T]) UpdateUsers(userList []T, userUUIDList []string, userFlowList []string) { + userMap := make(map[[16]byte]T) + userFlowMap := make(map[T]string) + for i, userName := range userList { + userID, err := uuid.FromString(userUUIDList[i]) + if err != nil { + userID = uuid.NewV5(uuid.Nil, userUUIDList[i]) + } + userMap[userID] = userName + userFlowMap[userName] = userFlowList[i] + } + s.userMap = userMap + s.userFlow = userFlowMap +} + +var _ N.TCPConnectionHandler = (*Service[int])(nil) + +func (s *Service[T]) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + var version uint8 + err := binary.Read(conn, binary.BigEndian, &version) + if err != nil { + return err + } + if version != vless.Version { + return E.New("unknown version: ", version) + } + + var requestUUID [16]byte + _, err = io.ReadFull(conn, requestUUID[:]) + if err != nil { + return err + } + + var addonsLen uint8 + err = binary.Read(conn, binary.BigEndian, &addonsLen) + if err != nil { + return err + } + + var addons vless.Addons + if addonsLen > 0 { + addonsBytes := make([]byte, addonsLen) + _, err = io.ReadFull(conn, addonsBytes) + if err != nil { + return err + } + + err = proto.Unmarshal(addonsBytes, &addons) + if err != nil { + return err + } + } + + var command byte + err = binary.Read(conn, binary.BigEndian, &command) + if err != nil { + return err + } + + var destination M.Socksaddr + if command != vless.CommandMux { + destination, err = vmess.AddressSerializer.ReadAddrPort(conn) + if err != nil { + return err + } + } + + user, loaded := s.userMap[requestUUID] + if !loaded { + return E.New("unknown UUID: ", uuid.FromBytesOrNil(requestUUID[:])) + } + ctx = auth.ContextWithUser(ctx, user) + metadata.Destination = destination + + userFlow := s.userFlow[user] + requestFlow := addons.Flow + if requestFlow != userFlow && requestFlow != "" { + return E.New("flow mismatch: expected ", flowName(userFlow), ", but got ", flowName(requestFlow)) + } + + responseConn := &serverConn{ExtendedConn: bufio.NewExtendedConn(conn)} + switch requestFlow { + case vless.XRV: + conn, err = vision.NewConn(responseConn, conn, requestUUID) + if err != nil { + return E.Cause(err, "initialize vision") + } + case "": + conn = responseConn + default: + return E.New("unknown flow: ", requestFlow) + } + switch command { + case vless.CommandTCP: + return s.handler.NewConnection(ctx, conn, metadata) + case vless.CommandUDP: + if requestFlow == vless.XRV { + return E.New(vless.XRV, " flow does not support UDP") + } + return s.handler.NewPacketConnection(ctx, &serverPacketConn{ExtendedConn: bufio.NewExtendedConn(conn), destination: destination}, metadata) + case vless.CommandMux: + return vmess.HandleMuxConnection(ctx, conn, metadata, s.handler) + default: + return E.New("unknown command: ", command) + } +} + +func flowName(value string) string { + if value == "" { + return "none" + } + return value +} + +type serverConn struct { + N.ExtendedConn + responseWritten bool +} + +func (c *serverConn) Write(b []byte) (n int, err error) { + if !c.responseWritten { + buffer := buf.NewSize(2 + len(b)) + buffer.WriteByte(vless.Version) + buffer.WriteByte(0) + buffer.Write(b) + _, err = c.ExtendedConn.Write(buffer.Bytes()) + buffer.Release() + if err == nil { + n = len(b) + } + c.responseWritten = true + return + } + return c.ExtendedConn.Write(b) +} + +func (c *serverConn) WriteBuffer(buffer *buf.Buffer) error { + if !c.responseWritten { + header := buffer.ExtendHeader(2) + header[0] = vless.Version + header[1] = 0 + c.responseWritten = true + } + return c.ExtendedConn.WriteBuffer(buffer) +} + +func (c *serverConn) FrontHeadroom() int { + if c.responseWritten { + return 0 + } + return 2 +} + +func (c *serverConn) ReaderReplaceable() bool { + return true +} + +func (c *serverConn) WriterReplaceable() bool { + return c.responseWritten +} + +func (c *serverConn) NeedAdditionalReadDeadline() bool { + return true +} + +func (c *serverConn) Upstream() any { + return c.ExtendedConn +} + +type serverPacketConn struct { + N.ExtendedConn + destination M.Socksaddr + readWaitOptions N.ReadWaitOptions +} + +func (c *serverPacketConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) { + c.readWaitOptions = options + return false +} + +func (c *serverPacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) { + var packetLen uint16 + err = binary.Read(c.ExtendedConn, binary.BigEndian, &packetLen) + if err != nil { + return + } + + buffer = c.readWaitOptions.NewPacketBuffer() + _, err = buffer.ReadFullFrom(c.ExtendedConn, int(packetLen)) + if err != nil { + buffer.Release() + return + } + c.readWaitOptions.PostReturn(buffer) + + destination = c.destination + return +} + +func (c *serverPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + var packetLen uint16 + err = binary.Read(c.ExtendedConn, binary.BigEndian, &packetLen) + if err != nil { + return + } + if len(p) < int(packetLen) { + err = io.ErrShortBuffer + return + } + n, err = io.ReadFull(c.ExtendedConn, p[:packetLen]) + if err != nil { + return + } + if c.destination.IsFqdn() { + addr = c.destination + } else { + addr = c.destination.UDPAddr() + } + return +} + +func (c *serverPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + err = binary.Write(c.ExtendedConn, binary.BigEndian, uint16(len(p))) + if err != nil { + return + } + return c.ExtendedConn.Write(p) +} + +func (c *serverPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) { + var packetLen uint16 + err = binary.Read(c.ExtendedConn, binary.BigEndian, &packetLen) + if err != nil { + return + } + + _, err = buffer.ReadFullFrom(c.ExtendedConn, int(packetLen)) + if err != nil { + return + } + + destination = c.destination + return +} + +func (c *serverPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { + packetLen := buffer.Len() + binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(packetLen)) + return c.ExtendedConn.WriteBuffer(buffer) +} + +func (c *serverPacketConn) FrontHeadroom() int { + return 2 +} + +func (c *serverPacketConn) NeedAdditionalReadDeadline() bool { + return true +} + +func (c *serverPacketConn) Upstream() any { + return c.ExtendedConn +}