Add vless outbound and xudp
This commit is contained in:
144
transport/vless/client.go
Normal file
144
transport/vless/client.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package vless
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
key []byte
|
||||
}
|
||||
|
||||
func NewClient(userId string) (*Client, error) {
|
||||
user := uuid.FromStringOrNil(userId)
|
||||
if user == uuid.Nil {
|
||||
user = uuid.NewV5(user, userId)
|
||||
}
|
||||
return &Client{key: user.Bytes()}, nil
|
||||
}
|
||||
|
||||
func (c *Client) DialEarlyConn(conn net.Conn, destination M.Socksaddr) *Conn {
|
||||
return &Conn{Conn: conn, key: c.key, destination: destination}
|
||||
}
|
||||
|
||||
func (c *Client) DialPacketConn(conn net.Conn, destination M.Socksaddr) *PacketConn {
|
||||
return &PacketConn{Conn: conn, key: c.key, destination: destination}
|
||||
}
|
||||
|
||||
func (c *Client) DialXUDPPacketConn(conn net.Conn, destination M.Socksaddr) *XUDPConn {
|
||||
return &XUDPConn{Conn: conn, key: c.key, destination: destination}
|
||||
}
|
||||
|
||||
type Conn struct {
|
||||
net.Conn
|
||||
key []byte
|
||||
destination M.Socksaddr
|
||||
requestWritten bool
|
||||
responseRead bool
|
||||
}
|
||||
|
||||
func (c *Conn) Read(b []byte) (n int, err error) {
|
||||
if !c.responseRead {
|
||||
err = ReadResponse(c.Conn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
return c.Conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *Conn) Write(b []byte) (n int, err error) {
|
||||
if !c.requestWritten {
|
||||
err = WriteRequest(c.Conn, Request{c.key, CommandTCP, c.destination}, b)
|
||||
if err == nil {
|
||||
n = len(b)
|
||||
}
|
||||
c.requestWritten = true
|
||||
return
|
||||
}
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
|
||||
func (c *Conn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
|
||||
type PacketConn struct {
|
||||
net.Conn
|
||||
key []byte
|
||||
destination M.Socksaddr
|
||||
requestWritten bool
|
||||
responseRead bool
|
||||
}
|
||||
|
||||
func (c *PacketConn) Read(b []byte) (n int, err error) {
|
||||
if !c.responseRead {
|
||||
err = ReadResponse(c.Conn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
}
|
||||
var length uint16
|
||||
err = binary.Read(c.Conn, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if cap(b) < int(length) {
|
||||
return 0, io.ErrShortBuffer
|
||||
}
|
||||
return io.ReadFull(c.Conn, b[:length])
|
||||
}
|
||||
|
||||
func (c *PacketConn) Write(b []byte) (n int, err error) {
|
||||
if !c.requestWritten {
|
||||
err = WritePacketRequest(c.Conn, Request{c.key, CommandUDP, c.destination}, b)
|
||||
if err == nil {
|
||||
n = len(b)
|
||||
}
|
||||
c.requestWritten = true
|
||||
return
|
||||
}
|
||||
err = binary.Write(c.Conn, binary.BigEndian, uint16(len(b)))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return c.Conn.Write(b)
|
||||
}
|
||||
|
||||
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
dataLen := buffer.Len()
|
||||
binary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(dataLen))
|
||||
if !c.requestWritten {
|
||||
err := WritePacketRequest(c.Conn, Request{c.key, CommandUDP, c.destination}, buffer.Bytes())
|
||||
c.requestWritten = true
|
||||
return err
|
||||
}
|
||||
return common.Error(c.Conn.Write(buffer.Bytes()))
|
||||
}
|
||||
|
||||
func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
n, err = c.Read(p)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
return c.Write(p)
|
||||
}
|
||||
|
||||
func (c *PacketConn) FrontHeadroom() int {
|
||||
return 2
|
||||
}
|
||||
|
||||
func (c *PacketConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
104
transport/vless/protocol.go
Normal file
104
transport/vless/protocol.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package vless
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
"github.com/sagernet/sing/common/rw"
|
||||
)
|
||||
|
||||
const (
|
||||
Version = 0
|
||||
CommandTCP = 1
|
||||
CommandUDP = 2
|
||||
CommandMux = 3
|
||||
NetworkUDP = 2
|
||||
)
|
||||
|
||||
var AddressSerializer = M.NewSerializer(
|
||||
M.AddressFamilyByte(0x01, M.AddressFamilyIPv4),
|
||||
M.AddressFamilyByte(0x02, M.AddressFamilyFqdn),
|
||||
M.AddressFamilyByte(0x03, M.AddressFamilyIPv6),
|
||||
M.PortThenAddress(),
|
||||
)
|
||||
|
||||
type Request struct {
|
||||
UUID []byte
|
||||
Command byte
|
||||
Destination M.Socksaddr
|
||||
}
|
||||
|
||||
func WriteRequest(writer io.Writer, request Request, payload []byte) error {
|
||||
var requestLen int
|
||||
requestLen += 1 // version
|
||||
requestLen += 16 // uuid
|
||||
requestLen += 1 // protobuf length
|
||||
requestLen += 1 // command
|
||||
requestLen += AddressSerializer.AddrPortLen(request.Destination)
|
||||
requestLen += len(payload)
|
||||
_buffer := buf.StackNewSize(requestLen)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
common.Must(
|
||||
buffer.WriteByte(Version),
|
||||
common.Error(buffer.Write(request.UUID)),
|
||||
buffer.WriteByte(0),
|
||||
buffer.WriteByte(CommandTCP),
|
||||
AddressSerializer.WriteAddrPort(buffer, request.Destination),
|
||||
common.Error(buffer.Write(payload)),
|
||||
)
|
||||
return common.Error(writer.Write(buffer.Bytes()))
|
||||
}
|
||||
|
||||
func WritePacketRequest(writer io.Writer, request Request, payload []byte) error {
|
||||
var requestLen int
|
||||
requestLen += 1 // version
|
||||
requestLen += 16 // uuid
|
||||
requestLen += 1 // protobuf length
|
||||
requestLen += 1 // command
|
||||
requestLen += AddressSerializer.AddrPortLen(request.Destination)
|
||||
if len(payload) > 0 {
|
||||
requestLen += 2
|
||||
requestLen += len(payload)
|
||||
}
|
||||
_buffer := buf.StackNewSize(requestLen)
|
||||
defer common.KeepAlive(_buffer)
|
||||
buffer := common.Dup(_buffer)
|
||||
defer buffer.Release()
|
||||
common.Must(
|
||||
buffer.WriteByte(Version),
|
||||
common.Error(buffer.Write(request.UUID)),
|
||||
buffer.WriteByte(0),
|
||||
buffer.WriteByte(CommandUDP),
|
||||
AddressSerializer.WriteAddrPort(buffer, request.Destination),
|
||||
binary.Write(buffer, binary.BigEndian, uint16(len(payload))),
|
||||
common.Error(buffer.Write(payload)),
|
||||
)
|
||||
return common.Error(writer.Write(buffer.Bytes()))
|
||||
}
|
||||
|
||||
func ReadResponse(reader io.Reader) error {
|
||||
version, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if version != Version {
|
||||
return E.New("unknown version: ", version)
|
||||
}
|
||||
protobufLength, err := rw.ReadByte(reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if protobufLength > 0 {
|
||||
err = rw.SkipN(reader, int(protobufLength))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
174
transport/vless/xudp.go
Normal file
174
transport/vless/xudp.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package vless
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/sagernet/sing/common"
|
||||
"github.com/sagernet/sing/common/buf"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
)
|
||||
|
||||
type XUDPConn struct {
|
||||
net.Conn
|
||||
key []byte
|
||||
destination M.Socksaddr
|
||||
requestWritten bool
|
||||
responseRead bool
|
||||
}
|
||||
|
||||
func (c *XUDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
return 0, nil, os.ErrInvalid
|
||||
}
|
||||
|
||||
func (c *XUDPConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||||
start := buffer.Start()
|
||||
if !c.responseRead {
|
||||
err = ReadResponse(c.Conn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
c.responseRead = true
|
||||
buffer.FullReset()
|
||||
}
|
||||
_, err = buffer.ReadFullFrom(c.Conn, 6)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var length uint16
|
||||
err = binary.Read(buffer, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
header, err := buffer.ReadBytes(4)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch header[2] {
|
||||
case 1:
|
||||
// frame new
|
||||
return M.Socksaddr{}, E.New("unexpected frame new")
|
||||
case 2:
|
||||
// frame keep
|
||||
if length != 4 {
|
||||
_, err = buffer.ReadFullFrom(c.Conn, int(length)-2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buffer.Advance(1)
|
||||
destination, err = AddressSerializer.ReadAddrPort(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
_, err = buffer.ReadFullFrom(c.Conn, 2)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
destination = c.destination
|
||||
}
|
||||
case 3:
|
||||
// frame end
|
||||
return M.Socksaddr{}, io.EOF
|
||||
case 4:
|
||||
// frame keep alive
|
||||
default:
|
||||
return M.Socksaddr{}, E.New("unexpected frame: ", buffer.Byte(2))
|
||||
}
|
||||
// option error
|
||||
if header[3]&2 == 2 {
|
||||
return M.Socksaddr{}, E.Cause(net.ErrClosed, "remote closed")
|
||||
}
|
||||
// option data
|
||||
if header[3]&1 != 1 {
|
||||
buffer.Resize(start, 0)
|
||||
return c.ReadPacket(buffer)
|
||||
} else {
|
||||
err = binary.Read(buffer, binary.BigEndian, &length)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buffer.Resize(start, 0)
|
||||
_, err = buffer.ReadFullFrom(c.Conn, int(length))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (c *XUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
destination := M.SocksaddrFromNet(addr)
|
||||
headerLen := c.frontHeadroom(AddressSerializer.AddrPortLen(destination))
|
||||
buffer := buf.NewSize(headerLen + len(p))
|
||||
buffer.Advance(headerLen)
|
||||
common.Must1(buffer.Write(p))
|
||||
err = c.WritePacket(buffer, destination)
|
||||
if err == nil {
|
||||
n = len(p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *XUDPConn) frontHeadroom(addrLen int) int {
|
||||
if !c.requestWritten {
|
||||
var headerLen int
|
||||
headerLen += 1 // version
|
||||
headerLen += 16 // uuid
|
||||
headerLen += 1 // protobuf length
|
||||
headerLen += 1 // command
|
||||
headerLen += 2 // frame len
|
||||
headerLen += 5 // frame header
|
||||
headerLen += addrLen
|
||||
headerLen += 2 // payload len
|
||||
return headerLen
|
||||
} else {
|
||||
return 7 + addrLen + 2
|
||||
}
|
||||
}
|
||||
|
||||
func (c *XUDPConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||||
defer buffer.Release()
|
||||
dataLen := buffer.Len()
|
||||
addrLen := M.SocksaddrSerializer.AddrPortLen(destination)
|
||||
if !c.requestWritten {
|
||||
header := buf.With(buffer.ExtendHeader(c.frontHeadroom(addrLen)))
|
||||
common.Must(
|
||||
header.WriteByte(Version),
|
||||
common.Error(header.Write(c.key)),
|
||||
header.WriteByte(0),
|
||||
header.WriteByte(CommandMux),
|
||||
binary.Write(header, binary.BigEndian, uint16(5+addrLen)),
|
||||
header.WriteByte(0),
|
||||
header.WriteByte(0),
|
||||
header.WriteByte(1), // frame type new
|
||||
header.WriteByte(1), // option data
|
||||
header.WriteByte(NetworkUDP),
|
||||
AddressSerializer.WriteAddrPort(header, destination),
|
||||
binary.Write(header, binary.BigEndian, uint16(dataLen)),
|
||||
)
|
||||
c.requestWritten = true
|
||||
} else {
|
||||
header := buffer.ExtendHeader(c.frontHeadroom(addrLen))
|
||||
binary.BigEndian.PutUint16(header, uint16(5+addrLen))
|
||||
header[2] = 0
|
||||
header[3] = 0
|
||||
header[4] = 2 // frame keep
|
||||
header[5] = 1 // option data
|
||||
header[6] = NetworkUDP
|
||||
err := AddressSerializer.WriteAddrPort(buf.With(header[7:]), destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binary.BigEndian.PutUint16(header[7+addrLen:], uint16(dataLen))
|
||||
}
|
||||
return common.Error(c.Conn.Write(buffer.Bytes()))
|
||||
}
|
||||
|
||||
func (c *XUDPConn) FrontHeadroom() int {
|
||||
return c.frontHeadroom(M.MaxSocksaddrLength)
|
||||
}
|
||||
|
||||
func (c *XUDPConn) Upstream() any {
|
||||
return c.Conn
|
||||
}
|
||||
Reference in New Issue
Block a user