mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-11-22 08:31:30 +00:00
Improve VLESS request
This commit is contained in:
parent
a62ad44883
commit
8eb7dd0059
|
@ -135,7 +135,7 @@ func (e *RealityClientConfig) ClientHandshake(ctx context.Context, conn net.Conn
|
||||||
|
|
||||||
hello.SessionId[0] = 1
|
hello.SessionId[0] = 1
|
||||||
hello.SessionId[1] = 8
|
hello.SessionId[1] = 8
|
||||||
hello.SessionId[2] = 0
|
hello.SessionId[2] = 1
|
||||||
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
|
binary.BigEndian.PutUint32(hello.SessionId[4:], uint32(time.Now().Unix()))
|
||||||
copy(hello.SessionId[8:], e.shortID[:])
|
copy(hello.SessionId[8:], e.shortID[:])
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,95 @@ func TestVLESSVisionReality(t *testing.T) {
|
||||||
testSuit(t, clientPort, testPort)
|
testSuit(t, clientPort, testPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVLESSVisionRealityPlain(t *testing.T) {
|
||||||
|
userUUID := newUUID()
|
||||||
|
startInstance(t, option.Options{
|
||||||
|
Inbounds: []option.Inbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeMixed,
|
||||||
|
Tag: "mixed-in",
|
||||||
|
MixedOptions: option.HTTPMixedInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: clientPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeVLESS,
|
||||||
|
VLESSOptions: option.VLESSInboundOptions{
|
||||||
|
ListenOptions: option.ListenOptions{
|
||||||
|
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
||||||
|
ListenPort: serverPort,
|
||||||
|
},
|
||||||
|
Users: []option.VLESSUser{
|
||||||
|
{
|
||||||
|
Name: "sekai",
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
Flow: vless.FlowVision,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TLS: &option.InboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.InboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
Handshake: option.InboundRealityHandshakeOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "google.com",
|
||||||
|
ServerPort: 443,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ShortID: []string{"0123456789abcdef"},
|
||||||
|
PrivateKey: "UuMBgl7MXTPx9inmQp2UC7Jcnwc6XYbwDNebonM-FCc",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Outbounds: []option.Outbound{
|
||||||
|
{
|
||||||
|
Type: C.TypeDirect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: C.TypeVLESS,
|
||||||
|
Tag: "vless-out",
|
||||||
|
VLESSOptions: option.VLESSOutboundOptions{
|
||||||
|
ServerOptions: option.ServerOptions{
|
||||||
|
Server: "127.0.0.1",
|
||||||
|
ServerPort: serverPort,
|
||||||
|
},
|
||||||
|
UUID: userUUID.String(),
|
||||||
|
Flow: vless.FlowVision,
|
||||||
|
TLS: &option.OutboundTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ServerName: "google.com",
|
||||||
|
Reality: &option.OutboundRealityOptions{
|
||||||
|
Enabled: true,
|
||||||
|
ShortID: "0123456789abcdef",
|
||||||
|
PublicKey: "jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0",
|
||||||
|
},
|
||||||
|
UTLS: &option.OutboundUTLSOptions{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Route: &option.RouteOptions{
|
||||||
|
Rules: []option.Rule{
|
||||||
|
{
|
||||||
|
DefaultOptions: option.DefaultRule{
|
||||||
|
Inbound: []string{"mixed-in"},
|
||||||
|
Outbound: "vless-out",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
testSuit(t, clientPort, testPort)
|
||||||
|
}
|
||||||
|
|
||||||
func TestVLESSRealityTransport(t *testing.T) {
|
func TestVLESSRealityTransport(t *testing.T) {
|
||||||
t.Run("grpc", func(t *testing.T) {
|
t.Run("grpc", func(t *testing.T) {
|
||||||
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
testVLESSRealityTransport(t, &option.V2RayTransportOptions{
|
||||||
|
@ -160,8 +249,6 @@ func TestVLESSRealityTransport(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) {
|
func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOptions) {
|
||||||
_, certPem, keyPem := createSelfSignedCertificate(t, "example.org")
|
|
||||||
|
|
||||||
userUUID := newUUID()
|
userUUID := newUUID()
|
||||||
startInstance(t, option.Options{
|
startInstance(t, option.Options{
|
||||||
Inbounds: []option.Inbound{
|
Inbounds: []option.Inbound{
|
||||||
|
@ -206,52 +293,11 @@ func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOpt
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Type: C.TypeTrojan,
|
|
||||||
Tag: "trojan",
|
|
||||||
TrojanOptions: option.TrojanInboundOptions{
|
|
||||||
ListenOptions: option.ListenOptions{
|
|
||||||
Listen: option.NewListenAddress(netip.IPv4Unspecified()),
|
|
||||||
ListenPort: otherPort,
|
|
||||||
},
|
|
||||||
Users: []option.TrojanUser{
|
|
||||||
{
|
|
||||||
Name: "sekai",
|
|
||||||
Password: userUUID.String(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TLS: &option.InboundTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ServerName: "example.org",
|
|
||||||
CertificatePath: certPem,
|
|
||||||
KeyPath: keyPem,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Outbounds: []option.Outbound{
|
Outbounds: []option.Outbound{
|
||||||
{
|
{
|
||||||
Type: C.TypeDirect,
|
Type: C.TypeDirect,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Type: C.TypeTrojan,
|
|
||||||
Tag: "trojan-out",
|
|
||||||
TrojanOptions: option.TrojanOutboundOptions{
|
|
||||||
ServerOptions: option.ServerOptions{
|
|
||||||
Server: "127.0.0.1",
|
|
||||||
ServerPort: otherPort,
|
|
||||||
},
|
|
||||||
Password: userUUID.String(),
|
|
||||||
TLS: &option.OutboundTLSOptions{
|
|
||||||
Enabled: true,
|
|
||||||
ServerName: "example.org",
|
|
||||||
CertificatePath: certPem,
|
|
||||||
},
|
|
||||||
DialerOptions: option.DialerOptions{
|
|
||||||
Detour: "vless-out",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Type: C.TypeVLESS,
|
Type: C.TypeVLESS,
|
||||||
Tag: "vless-out",
|
Tag: "vless-out",
|
||||||
|
@ -282,7 +328,7 @@ func testVLESSRealityTransport(t *testing.T, transport *option.V2RayTransportOpt
|
||||||
{
|
{
|
||||||
DefaultOptions: option.DefaultRule{
|
DefaultOptions: option.DefaultRule{
|
||||||
Inbound: []string{"mixed-in"},
|
Inbound: []string{"mixed-in"},
|
||||||
Outbound: "trojan-out",
|
Outbound: "vless-out",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/sagernet/sing-vmess"
|
"github.com/sagernet/sing-vmess"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
"github.com/sagernet/sing/common/buf"
|
"github.com/sagernet/sing/common/buf"
|
||||||
|
"github.com/sagernet/sing/common/bufio"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
|
@ -35,32 +36,28 @@ func NewClient(userId string, flow string, logger logger.Logger) (*Client, error
|
||||||
return &Client{user, flow, logger}, nil
|
return &Client{user, flow, logger}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) prepareConn(conn net.Conn) (net.Conn, error) {
|
func (c *Client) prepareConn(conn net.Conn, tlsConn net.Conn) (net.Conn, error) {
|
||||||
if c.flow == FlowVision {
|
if c.flow == FlowVision {
|
||||||
vConn, err := NewVisionConn(conn, c.key, c.logger)
|
protocolConn, err := NewVisionConn(conn, tlsConn, c.key, c.logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, E.Cause(err, "initialize vision")
|
return nil, E.Cause(err, "initialize vision")
|
||||||
}
|
}
|
||||||
conn = vConn
|
conn = protocolConn
|
||||||
}
|
}
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DialConn(conn net.Conn, destination M.Socksaddr) (*Conn, error) {
|
func (c *Client) DialConn(conn net.Conn, destination M.Socksaddr) (net.Conn, error) {
|
||||||
vConn, err := c.prepareConn(conn)
|
remoteConn := NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow)
|
||||||
|
protocolConn, err := c.prepareConn(remoteConn, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
serverConn := &Conn{Conn: conn, protocolConn: vConn, key: c.key, command: vmess.CommandTCP, destination: destination, flow: c.flow}
|
return protocolConn, common.Error(remoteConn.Write(nil))
|
||||||
return serverConn, common.Error(serverConn.Write(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DialEarlyConn(conn net.Conn, destination M.Socksaddr) (*Conn, error) {
|
func (c *Client) DialEarlyConn(conn net.Conn, destination M.Socksaddr) (net.Conn, error) {
|
||||||
vConn, err := c.prepareConn(conn)
|
return c.prepareConn(NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow), conn)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &Conn{Conn: conn, protocolConn: vConn, key: c.key, command: vmess.CommandTCP, destination: destination, flow: c.flow}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) (*PacketConn, error) {
|
func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) (*PacketConn, error) {
|
||||||
|
@ -73,71 +70,122 @@ func (c *Client) DialEarlyPacketConn(conn net.Conn, destination M.Socksaddr) (*P
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
|
func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
|
||||||
serverConn := &Conn{Conn: conn, protocolConn: conn, key: c.key, command: vmess.CommandMux, destination: destination, flow: c.flow}
|
remoteConn := NewConn(conn, c.key, vmess.CommandTCP, destination, c.flow)
|
||||||
err := common.Error(serverConn.Write(nil))
|
protocolConn, err := c.prepareConn(remoteConn, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return vmess.NewXUDPConn(serverConn, destination), nil
|
return vmess.NewXUDPConn(protocolConn, destination), common.Error(remoteConn.Write(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DialEarlyXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
|
func (c *Client) DialEarlyXUDPPacketConn(conn net.Conn, destination M.Socksaddr) (vmess.PacketConn, error) {
|
||||||
return vmess.NewXUDPConn(&Conn{Conn: conn, protocolConn: conn, key: c.key, command: vmess.CommandMux, destination: destination, flow: c.flow}, destination), nil
|
remoteConn := NewConn(conn, c.key, vmess.CommandMux, destination, c.flow)
|
||||||
|
protocolConn, err := c.prepareConn(remoteConn, conn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return vmess.NewXUDPConn(protocolConn, destination), common.Error(remoteConn.Write(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ N.EarlyConn = (*Conn)(nil)
|
var (
|
||||||
|
_ N.EarlyConn = (*Conn)(nil)
|
||||||
|
_ N.VectorisedWriter = (*Conn)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
net.Conn
|
N.ExtendedConn
|
||||||
protocolConn net.Conn
|
writer N.VectorisedWriter
|
||||||
key [16]byte
|
request Request
|
||||||
command byte
|
|
||||||
destination M.Socksaddr
|
|
||||||
flow string
|
|
||||||
requestWritten bool
|
requestWritten bool
|
||||||
responseRead bool
|
responseRead bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewConn(conn net.Conn, uuid [16]byte, command byte, destination M.Socksaddr, flow string) *Conn {
|
||||||
|
return &Conn{
|
||||||
|
ExtendedConn: bufio.NewExtendedConn(conn),
|
||||||
|
writer: bufio.NewVectorisedWriter(conn),
|
||||||
|
request: Request{
|
||||||
|
UUID: uuid,
|
||||||
|
Command: command,
|
||||||
|
Destination: destination,
|
||||||
|
Flow: flow,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Read(b []byte) (n int, err error) {
|
||||||
|
if !c.responseRead {
|
||||||
|
err = ReadResponse(c.ExtendedConn)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.responseRead = true
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) ReadBuffer(buffer *buf.Buffer) error {
|
||||||
|
if !c.responseRead {
|
||||||
|
err := ReadResponse(c.ExtendedConn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.responseRead = true
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.ReadBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Write(b []byte) (n int, err error) {
|
||||||
|
if !c.requestWritten {
|
||||||
|
err = WriteRequest(c.ExtendedConn, c.request, b)
|
||||||
|
if err == nil {
|
||||||
|
n = len(b)
|
||||||
|
}
|
||||||
|
c.requestWritten = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
|
if !c.requestWritten {
|
||||||
|
EncodeRequest(c.request, buf.With(buffer.ExtendHeader(RequestLen(c.request))))
|
||||||
|
c.requestWritten = true
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) WriteVectorised(buffers []*buf.Buffer) error {
|
||||||
|
if !c.requestWritten {
|
||||||
|
buffer := buf.NewSize(RequestLen(c.request))
|
||||||
|
EncodeRequest(c.request, buffer)
|
||||||
|
c.requestWritten = true
|
||||||
|
return c.writer.WriteVectorised(append([]*buf.Buffer{buffer}, buffers...))
|
||||||
|
}
|
||||||
|
return c.writer.WriteVectorised(buffers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) ReaderReplaceable() bool {
|
||||||
|
return c.responseRead
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) WriterReplaceable() bool {
|
||||||
|
return c.requestWritten
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Conn) NeedHandshake() bool {
|
func (c *Conn) NeedHandshake() bool {
|
||||||
return !c.requestWritten
|
return !c.requestWritten
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Read(b []byte) (n int, err error) {
|
func (c *Conn) FrontHeadroom() int {
|
||||||
if !c.responseRead {
|
if c.requestWritten {
|
||||||
err = ReadResponse(c.Conn)
|
return 0
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.responseRead = true
|
|
||||||
}
|
}
|
||||||
return c.protocolConn.Read(b)
|
return RequestLen(c.request)
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) Write(b []byte) (n int, err error) {
|
|
||||||
if !c.requestWritten {
|
|
||||||
request := Request{c.key, c.command, c.destination, c.flow}
|
|
||||||
if c.protocolConn != nil {
|
|
||||||
err = WriteRequest(c.Conn, request, nil)
|
|
||||||
} else {
|
|
||||||
err = WriteRequest(c.Conn, request, b)
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
n = len(b)
|
|
||||||
}
|
|
||||||
c.requestWritten = true
|
|
||||||
if c.protocolConn == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.protocolConn.Write(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Conn) NeedAdditionalReadDeadline() bool {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Upstream() any {
|
func (c *Conn) Upstream() any {
|
||||||
return c.Conn
|
return c.ExtendedConn
|
||||||
}
|
}
|
||||||
|
|
||||||
type PacketConn struct {
|
type PacketConn struct {
|
||||||
|
|
|
@ -11,10 +11,12 @@ var (
|
||||||
tlsClientHandShakeStart = []byte{0x16, 0x03}
|
tlsClientHandShakeStart = []byte{0x16, 0x03}
|
||||||
tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03}
|
tlsServerHandShakeStart = []byte{0x16, 0x03, 0x03}
|
||||||
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
|
tlsApplicationDataStart = []byte{0x17, 0x03, 0x03}
|
||||||
|
)
|
||||||
|
|
||||||
commandPaddingContinue byte = 0
|
const (
|
||||||
commandPaddingEnd byte = 1
|
commandPaddingContinue byte = iota
|
||||||
commandPaddingDirect byte = 2
|
commandPaddingEnd
|
||||||
|
commandPaddingDirect
|
||||||
)
|
)
|
||||||
|
|
||||||
var tls13CipherSuiteDic = map[uint16]string{
|
var tls13CipherSuiteDic = map[uint16]string{
|
||||||
|
|
|
@ -128,7 +128,7 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
|
||||||
requestLen += 1 // protobuf length
|
requestLen += 1 // protobuf length
|
||||||
|
|
||||||
var addonsLen int
|
var addonsLen int
|
||||||
if request.Command == vmess.CommandTCP && request.Flow != "" {
|
if request.Flow != "" {
|
||||||
addonsLen += 1 // protobuf header
|
addonsLen += 1 // protobuf header
|
||||||
addonsLen += UvarintLen(uint64(len(request.Flow)))
|
addonsLen += UvarintLen(uint64(len(request.Flow)))
|
||||||
addonsLen += len(request.Flow)
|
addonsLen += len(request.Flow)
|
||||||
|
@ -165,6 +165,62 @@ func WriteRequest(writer io.Writer, request Request, payload []byte) error {
|
||||||
return common.Error(writer.Write(buffer.Bytes()))
|
return common.Error(writer.Write(buffer.Bytes()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func EncodeRequest(request Request, buffer *buf.Buffer) {
|
||||||
|
var requestLen int
|
||||||
|
requestLen += 1 // version
|
||||||
|
requestLen += 16 // uuid
|
||||||
|
requestLen += 1 // protobuf length
|
||||||
|
|
||||||
|
var addonsLen int
|
||||||
|
if request.Flow != "" {
|
||||||
|
addonsLen += 1 // protobuf header
|
||||||
|
addonsLen += UvarintLen(uint64(len(request.Flow)))
|
||||||
|
addonsLen += len(request.Flow)
|
||||||
|
requestLen += addonsLen
|
||||||
|
}
|
||||||
|
requestLen += 1 // command
|
||||||
|
if request.Command != vmess.CommandMux {
|
||||||
|
requestLen += vmess.AddressSerializer.AddrPortLen(request.Destination)
|
||||||
|
}
|
||||||
|
common.Must(
|
||||||
|
buffer.WriteByte(Version),
|
||||||
|
common.Error(buffer.Write(request.UUID[:])),
|
||||||
|
buffer.WriteByte(byte(addonsLen)),
|
||||||
|
)
|
||||||
|
if addonsLen > 0 {
|
||||||
|
common.Must(buffer.WriteByte(10))
|
||||||
|
binary.PutUvarint(buffer.Extend(UvarintLen(uint64(len(request.Flow)))), uint64(len(request.Flow)))
|
||||||
|
common.Must(common.Error(buffer.Write([]byte(request.Flow))))
|
||||||
|
}
|
||||||
|
common.Must(
|
||||||
|
buffer.WriteByte(request.Command),
|
||||||
|
)
|
||||||
|
|
||||||
|
if request.Command != vmess.CommandMux {
|
||||||
|
common.Must(vmess.AddressSerializer.WriteAddrPort(buffer, request.Destination))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func RequestLen(request Request) int {
|
||||||
|
var requestLen int
|
||||||
|
requestLen += 1 // version
|
||||||
|
requestLen += 16 // uuid
|
||||||
|
requestLen += 1 // protobuf length
|
||||||
|
|
||||||
|
var addonsLen int
|
||||||
|
if request.Flow != "" {
|
||||||
|
addonsLen += 1 // protobuf header
|
||||||
|
addonsLen += UvarintLen(uint64(len(request.Flow)))
|
||||||
|
addonsLen += len(request.Flow)
|
||||||
|
requestLen += addonsLen
|
||||||
|
}
|
||||||
|
requestLen += 1 // command
|
||||||
|
if request.Command != vmess.CommandMux {
|
||||||
|
requestLen += vmess.AddressSerializer.AddrPortLen(request.Destination)
|
||||||
|
}
|
||||||
|
return requestLen
|
||||||
|
}
|
||||||
|
|
||||||
func WritePacketRequest(writer io.Writer, request Request, payload []byte) error {
|
func WritePacketRequest(writer io.Writer, request Request, payload []byte) error {
|
||||||
var requestLen int
|
var requestLen int
|
||||||
requestLen += 1 // version
|
requestLen += 1 // version
|
||||||
|
|
|
@ -68,30 +68,32 @@ func (s *Service[T]) NewConnection(ctx context.Context, conn net.Conn, metadata
|
||||||
metadata.Destination = request.Destination
|
metadata.Destination = request.Destination
|
||||||
|
|
||||||
userFlow := s.userFlow[user]
|
userFlow := s.userFlow[user]
|
||||||
|
if request.Flow == FlowVision && request.Command == vmess.NetworkUDP {
|
||||||
var responseWriter io.Writer
|
return E.New(FlowVision, " flow does not support UDP")
|
||||||
if request.Command == vmess.CommandTCP {
|
} else if request.Flow != userFlow {
|
||||||
if request.Flow != userFlow {
|
return E.New("flow mismatch: expected ", flowName(userFlow), ", but got ", flowName(request.Flow))
|
||||||
return E.New("flow mismatch: expected ", flowName(userFlow), ", but got ", flowName(request.Flow))
|
|
||||||
}
|
|
||||||
switch userFlow {
|
|
||||||
case "":
|
|
||||||
case FlowVision:
|
|
||||||
responseWriter = conn
|
|
||||||
conn, err = NewVisionConn(conn, request.UUID, s.logger)
|
|
||||||
if err != nil {
|
|
||||||
return E.Cause(err, "initialize vision")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if request.Command == vmess.CommandUDP {
|
||||||
|
return s.handler.NewPacketConnection(ctx, &serverPacketConn{ExtendedConn: bufio.NewExtendedConn(conn), destination: request.Destination}, metadata)
|
||||||
|
}
|
||||||
|
responseConn := &serverConn{ExtendedConn: bufio.NewExtendedConn(conn), writer: bufio.NewVectorisedWriter(conn)}
|
||||||
|
switch userFlow {
|
||||||
|
case FlowVision:
|
||||||
|
conn, err = NewVisionConn(responseConn, conn, request.UUID, s.logger)
|
||||||
|
if err != nil {
|
||||||
|
return E.Cause(err, "initialize vision")
|
||||||
|
}
|
||||||
|
case "":
|
||||||
|
conn = responseConn
|
||||||
|
default:
|
||||||
|
return E.New("unknown flow: ", userFlow)
|
||||||
|
}
|
||||||
switch request.Command {
|
switch request.Command {
|
||||||
case vmess.CommandTCP:
|
case vmess.CommandTCP:
|
||||||
return s.handler.NewConnection(ctx, &serverConn{Conn: conn, responseWriter: responseWriter}, metadata)
|
return s.handler.NewConnection(ctx, conn, metadata)
|
||||||
case vmess.CommandUDP:
|
|
||||||
return s.handler.NewPacketConnection(ctx, &serverPacketConn{ExtendedConn: bufio.NewExtendedConn(conn), destination: request.Destination}, metadata)
|
|
||||||
case vmess.CommandMux:
|
case vmess.CommandMux:
|
||||||
return vmess.HandleMuxConnection(ctx, &serverConn{Conn: conn, responseWriter: responseWriter}, s.handler)
|
return vmess.HandleMuxConnection(ctx, conn, s.handler)
|
||||||
default:
|
default:
|
||||||
return E.New("unknown command: ", request.Command)
|
return E.New("unknown command: ", request.Command)
|
||||||
}
|
}
|
||||||
|
@ -104,42 +106,70 @@ func flowName(value string) string {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ N.VectorisedWriter = (*serverConn)(nil)
|
||||||
|
|
||||||
type serverConn struct {
|
type serverConn struct {
|
||||||
net.Conn
|
N.ExtendedConn
|
||||||
responseWriter io.Writer
|
writer N.VectorisedWriter
|
||||||
responseWritten bool
|
responseWritten bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *serverConn) Read(b []byte) (n int, err error) {
|
func (c *serverConn) Read(b []byte) (n int, err error) {
|
||||||
return c.Conn.Read(b)
|
return c.ExtendedConn.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *serverConn) Write(b []byte) (n int, err error) {
|
func (c *serverConn) Write(b []byte) (n int, err error) {
|
||||||
if !c.responseWritten {
|
if !c.responseWritten {
|
||||||
if c.responseWriter == nil {
|
_, err = bufio.WriteVectorised(c.writer, [][]byte{{Version, 0}, b})
|
||||||
_, err = bufio.WriteVectorised(bufio.NewVectorisedWriter(c.Conn), [][]byte{{Version, 0}, b})
|
if err == nil {
|
||||||
if err == nil {
|
n = len(b)
|
||||||
n = len(b)
|
|
||||||
}
|
|
||||||
c.responseWritten = true
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
_, err = c.responseWriter.Write([]byte{Version, 0})
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.responseWritten = true
|
|
||||||
}
|
}
|
||||||
|
c.responseWritten = true
|
||||||
|
return
|
||||||
}
|
}
|
||||||
return c.Conn.Write(b)
|
return c.ExtendedConn.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *serverConn) WriteBuffer(buffer *buf.Buffer) error {
|
||||||
|
if !c.responseWritten {
|
||||||
|
header := buffer.ExtendHeader(2)
|
||||||
|
header[0] = Version
|
||||||
|
header[1] = 0
|
||||||
|
c.responseWritten = true
|
||||||
|
}
|
||||||
|
return c.ExtendedConn.WriteBuffer(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *serverConn) WriteVectorised(buffers []*buf.Buffer) error {
|
||||||
|
if !c.responseWritten {
|
||||||
|
err := c.writer.WriteVectorised(append([]*buf.Buffer{buf.As([]byte{Version, 0})}, buffers...))
|
||||||
|
c.responseWritten = true
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.writer.WriteVectorised(buffers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *serverConn) NeedAdditionalReadDeadline() bool {
|
func (c *serverConn) NeedAdditionalReadDeadline() bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) Upstream() any {
|
func (c *serverConn) Upstream() any {
|
||||||
return c.Conn
|
return c.ExtendedConn
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverPacketConn struct {
|
type serverPacketConn struct {
|
||||||
|
|
|
@ -56,12 +56,12 @@ type VisionConn struct {
|
||||||
withinPaddingBuffers bool
|
withinPaddingBuffers bool
|
||||||
remainingContent int
|
remainingContent int
|
||||||
remainingPadding int
|
remainingPadding int
|
||||||
currentCommand int
|
currentCommand byte
|
||||||
directRead bool
|
directRead bool
|
||||||
remainingReader io.Reader
|
remainingReader io.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVisionConn(conn net.Conn, userUUID [16]byte, logger logger.Logger) (*VisionConn, error) {
|
func NewVisionConn(conn net.Conn, tlsConn net.Conn, userUUID [16]byte, logger logger.Logger) (*VisionConn, error) {
|
||||||
var (
|
var (
|
||||||
loaded bool
|
loaded bool
|
||||||
reflectType reflect.Type
|
reflectType reflect.Type
|
||||||
|
@ -69,7 +69,7 @@ func NewVisionConn(conn net.Conn, userUUID [16]byte, logger logger.Logger) (*Vis
|
||||||
netConn net.Conn
|
netConn net.Conn
|
||||||
)
|
)
|
||||||
for _, tlsCreator := range tlsRegistry {
|
for _, tlsCreator := range tlsRegistry {
|
||||||
loaded, netConn, reflectType, reflectPointer = tlsCreator(conn)
|
loaded, netConn, reflectType, reflectPointer = tlsCreator(tlsConn)
|
||||||
if loaded {
|
if loaded {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,7 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||||
if c.remainingReader != nil {
|
if c.remainingReader != nil {
|
||||||
n, err = c.remainingReader.Read(p)
|
n, err = c.remainingReader.Read(p)
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
|
err = nil
|
||||||
c.remainingReader = nil
|
c.remainingReader = nil
|
||||||
}
|
}
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
|
@ -113,6 +114,7 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||||
return c.netConn.Read(p)
|
return c.netConn.Read(p)
|
||||||
}
|
}
|
||||||
var bufferBytes []byte
|
var bufferBytes []byte
|
||||||
|
var chunkBuffer *buf.Buffer
|
||||||
if len(p) > xrayChunkSize {
|
if len(p) > xrayChunkSize {
|
||||||
n, err = c.Conn.Read(p)
|
n, err = c.Conn.Read(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -120,21 +122,26 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||||
}
|
}
|
||||||
bufferBytes = p[:n]
|
bufferBytes = p[:n]
|
||||||
} else {
|
} else {
|
||||||
buffer, err := c.reader.ReadChunk()
|
chunkBuffer, err = c.reader.ReadChunk()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer buffer.FullReset()
|
bufferBytes = chunkBuffer.Bytes()
|
||||||
bufferBytes = buffer.Bytes()
|
|
||||||
}
|
}
|
||||||
if c.withinPaddingBuffers || c.numberOfPacketToFilter > 0 {
|
if c.withinPaddingBuffers || c.numberOfPacketToFilter > 0 {
|
||||||
buffers := c.unPadding(bufferBytes)
|
buffers := c.unPadding(bufferBytes)
|
||||||
|
if chunkBuffer != nil {
|
||||||
|
buffers = common.Map(buffers, func(it *buf.Buffer) *buf.Buffer {
|
||||||
|
return it.ToOwned()
|
||||||
|
})
|
||||||
|
chunkBuffer.FullReset()
|
||||||
|
}
|
||||||
if c.remainingContent == 0 && c.remainingPadding == 0 {
|
if c.remainingContent == 0 && c.remainingPadding == 0 {
|
||||||
if c.currentCommand == 1 {
|
if c.currentCommand == commandPaddingEnd {
|
||||||
c.withinPaddingBuffers = false
|
c.withinPaddingBuffers = false
|
||||||
c.remainingContent = -1
|
c.remainingContent = -1
|
||||||
c.remainingPadding = -1
|
c.remainingPadding = -1
|
||||||
} else if c.currentCommand == 2 {
|
} else if c.currentCommand == commandPaddingDirect {
|
||||||
c.withinPaddingBuffers = false
|
c.withinPaddingBuffers = false
|
||||||
c.directRead = true
|
c.directRead = true
|
||||||
|
|
||||||
|
@ -142,17 +149,17 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
buffers = append(buffers, inputBuffer)
|
buffers = append(buffers, buf.As(inputBuffer))
|
||||||
|
|
||||||
rawInputBuffer, err := io.ReadAll(c.rawInput)
|
rawInputBuffer, err := io.ReadAll(c.rawInput)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
buffers = append(buffers, rawInputBuffer)
|
buffers = append(buffers, buf.As(rawInputBuffer))
|
||||||
|
|
||||||
c.logger.Trace("XtlsRead readV")
|
c.logger.Trace("XtlsRead readV")
|
||||||
} else if c.currentCommand == 0 {
|
} else if c.currentCommand == commandPaddingContinue {
|
||||||
c.withinPaddingBuffers = true
|
c.withinPaddingBuffers = true
|
||||||
} else {
|
} else {
|
||||||
return 0, E.New("unknown command ", c.currentCommand)
|
return 0, E.New("unknown command ", c.currentCommand)
|
||||||
|
@ -163,14 +170,18 @@ func (c *VisionConn) Read(p []byte) (n int, err error) {
|
||||||
c.withinPaddingBuffers = false
|
c.withinPaddingBuffers = false
|
||||||
}
|
}
|
||||||
if c.numberOfPacketToFilter > 0 {
|
if c.numberOfPacketToFilter > 0 {
|
||||||
c.filterTLS(buffers)
|
c.filterTLS(buf.ToSliceMulti(buffers))
|
||||||
}
|
}
|
||||||
c.remainingReader = io.MultiReader(common.Map(buffers, func(it []byte) io.Reader { return bytes.NewReader(it) })...)
|
c.remainingReader = io.MultiReader(common.Map(buffers, func(it *buf.Buffer) io.Reader { return it })...)
|
||||||
return c.Read(p)
|
return c.Read(p)
|
||||||
} else {
|
} else {
|
||||||
if c.numberOfPacketToFilter > 0 {
|
if c.numberOfPacketToFilter > 0 {
|
||||||
c.filterTLS([][]byte{bufferBytes})
|
c.filterTLS([][]byte{bufferBytes})
|
||||||
}
|
}
|
||||||
|
if chunkBuffer != nil {
|
||||||
|
n = copy(p, bufferBytes)
|
||||||
|
chunkBuffer.Advance(n)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -310,7 +321,7 @@ func (c *VisionConn) padding(buffer *buf.Buffer, command byte) *buf.Buffer {
|
||||||
return newBuffer
|
return newBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
func (c *VisionConn) unPadding(buffer []byte) []*buf.Buffer {
|
||||||
var bufferIndex int
|
var bufferIndex int
|
||||||
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
||||||
if len(buffer) >= 21 && bytes.Equal(c.userUUID[:], buffer[:16]) {
|
if len(buffer) >= 21 && bytes.Equal(c.userUUID[:], buffer[:16]) {
|
||||||
|
@ -321,17 +332,17 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
if c.remainingContent == -1 && c.remainingPadding == -1 {
|
||||||
return [][]byte{buffer}
|
return []*buf.Buffer{buf.As(buffer)}
|
||||||
}
|
}
|
||||||
var buffers [][]byte
|
var buffers []*buf.Buffer
|
||||||
for bufferIndex < len(buffer) {
|
for bufferIndex < len(buffer) {
|
||||||
if c.remainingContent <= 0 && c.remainingPadding <= 0 {
|
if c.remainingContent <= 0 && c.remainingPadding <= 0 {
|
||||||
if c.currentCommand == 1 {
|
if c.currentCommand == 1 {
|
||||||
buffers = append(buffers, buffer[bufferIndex:])
|
buffers = append(buffers, buf.As(buffer[bufferIndex:]))
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
paddingInfo := buffer[bufferIndex : bufferIndex+5]
|
paddingInfo := buffer[bufferIndex : bufferIndex+5]
|
||||||
c.currentCommand = int(paddingInfo[0])
|
c.currentCommand = paddingInfo[0]
|
||||||
c.remainingContent = int(paddingInfo[1])<<8 | int(paddingInfo[2])
|
c.remainingContent = int(paddingInfo[1])<<8 | int(paddingInfo[2])
|
||||||
c.remainingPadding = int(paddingInfo[3])<<8 | int(paddingInfo[4])
|
c.remainingPadding = int(paddingInfo[3])<<8 | int(paddingInfo[4])
|
||||||
bufferIndex += 5
|
bufferIndex += 5
|
||||||
|
@ -342,7 +353,7 @@ func (c *VisionConn) unPadding(buffer []byte) [][]byte {
|
||||||
if end > len(buffer)-bufferIndex {
|
if end > len(buffer)-bufferIndex {
|
||||||
end = len(buffer) - bufferIndex
|
end = len(buffer) - bufferIndex
|
||||||
}
|
}
|
||||||
buffers = append(buffers, buffer[bufferIndex:bufferIndex+end])
|
buffers = append(buffers, buf.As(buffer[bufferIndex:bufferIndex+end]))
|
||||||
c.remainingContent -= end
|
c.remainingContent -= end
|
||||||
bufferIndex += end
|
bufferIndex += end
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue