sing-mux/protocol.go
2024-11-09 12:30:16 +08:00

185 lines
3.9 KiB
Go

package mux
import (
"encoding/binary"
"io"
"math/rand"
"time"
"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"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/rw"
"github.com/sagernet/sing/common/varbin"
)
const (
ProtocolSmux = iota
ProtocolYAMux
ProtocolH2Mux
)
const (
Version0 = iota
Version1
)
const (
TCPTimeout = 5 * time.Second
)
var Destination = M.Socksaddr{
Fqdn: "sp.mux.sing-box.arpa",
Port: 444,
}
type Request struct {
Version byte
Protocol byte
Padding bool
}
func ReadRequest(reader io.Reader) (*Request, error) {
var (
version byte
protocol byte
)
err := binary.Read(reader, binary.BigEndian, &version)
if err != nil {
return nil, err
}
if version < Version0 || version > Version1 {
return nil, E.New("unsupported version: ", version)
}
err = binary.Read(reader, binary.BigEndian, &protocol)
if err != nil {
return nil, err
}
var paddingEnabled bool
if version == Version1 {
err = binary.Read(reader, binary.BigEndian, &paddingEnabled)
if err != nil {
return nil, err
}
if paddingEnabled {
var paddingLen uint16
err = binary.Read(reader, binary.BigEndian, &paddingLen)
if err != nil {
return nil, err
}
err = rw.SkipN(reader, int(paddingLen))
if err != nil {
return nil, err
}
}
}
return &Request{Version: version, Protocol: protocol, Padding: paddingEnabled}, nil
}
func EncodeRequest(request Request, payload []byte) *buf.Buffer {
var requestLen int
requestLen += 2
var paddingLen uint16
if request.Version == Version1 {
requestLen += 1
if request.Padding {
requestLen += 2
paddingLen = uint16(256 + rand.Intn(512))
requestLen += int(paddingLen)
}
}
buffer := buf.NewSize(requestLen + len(payload))
common.Must(
buffer.WriteByte(request.Version),
buffer.WriteByte(request.Protocol),
)
if request.Version == Version1 {
common.Must(binary.Write(buffer, binary.BigEndian, request.Padding))
if request.Padding {
common.Must(binary.Write(buffer, binary.BigEndian, paddingLen))
buffer.Extend(int(paddingLen))
}
}
common.Must1(buffer.Write(payload))
return buffer
}
const (
flagUDP = 1
flagAddr = 2
statusSuccess = 0
statusError = 1
)
type StreamRequest struct {
Network string
Destination M.Socksaddr
PacketAddr bool
}
func ReadStreamRequest(reader io.Reader) (*StreamRequest, error) {
var flags uint16
err := binary.Read(reader, binary.BigEndian, &flags)
if err != nil {
return nil, err
}
destination, err := M.SocksaddrSerializer.ReadAddrPort(reader)
if err != nil {
return nil, err
}
var network string
var udpAddr bool
if flags&flagUDP == 0 {
network = N.NetworkTCP
} else {
network = N.NetworkUDP
udpAddr = flags&flagAddr != 0
}
return &StreamRequest{network, destination, udpAddr}, nil
}
func streamRequestLen(request StreamRequest) int {
var rLen int
rLen += 1 // version
rLen += 2 // flags
rLen += M.SocksaddrSerializer.AddrPortLen(request.Destination)
return rLen
}
func EncodeStreamRequest(request StreamRequest, buffer *buf.Buffer) error {
destination := request.Destination
var flags uint16
if request.Network == N.NetworkUDP {
flags |= flagUDP
}
if request.PacketAddr {
flags |= flagAddr
if !destination.IsValid() {
destination = Destination
}
}
common.Must(binary.Write(buffer, binary.BigEndian, flags))
return M.SocksaddrSerializer.WriteAddrPort(buffer, destination)
}
type StreamResponse struct {
Status uint8
Message string
}
func ReadStreamResponse(reader io.Reader) (*StreamResponse, error) {
var response StreamResponse
err := binary.Read(reader, binary.BigEndian, &response.Status)
if err != nil {
return nil, err
}
if response.Status == statusError {
response.Message, err = varbin.ReadValue[string](reader, binary.BigEndian)
if err != nil {
return nil, err
}
}
return &response, nil
}