mirror of
https://github.com/SagerNet/sing-box.git
synced 2024-12-01 19:06:43 +00:00
511 lines
13 KiB
Go
511 lines
13 KiB
Go
|
package hysteria
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/binary"
|
||
|
"io"
|
||
|
"math/rand"
|
||
|
"net"
|
||
|
"os"
|
||
|
"time"
|
||
|
|
||
|
"github.com/sagernet/quic-go"
|
||
|
"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"
|
||
|
)
|
||
|
|
||
|
const (
|
||
|
MbpsToBps = 125000
|
||
|
MinSpeedBPS = 16384
|
||
|
DefaultStreamReceiveWindow = 15728640 // 15 MB/s
|
||
|
DefaultConnectionReceiveWindow = 67108864 // 64 MB/s
|
||
|
DefaultMaxIncomingStreams = 1024
|
||
|
DefaultALPN = "hysteria"
|
||
|
KeepAlivePeriod = 10 * time.Second
|
||
|
)
|
||
|
|
||
|
const Version = 3
|
||
|
|
||
|
type ClientHello struct {
|
||
|
SendBPS uint64
|
||
|
RecvBPS uint64
|
||
|
Auth []byte
|
||
|
}
|
||
|
|
||
|
func WriteClientHello(stream io.Writer, hello ClientHello) error {
|
||
|
var requestLen int
|
||
|
requestLen += 1 // version
|
||
|
requestLen += 8 // sendBPS
|
||
|
requestLen += 8 // recvBPS
|
||
|
requestLen += 2 // auth len
|
||
|
requestLen += len(hello.Auth)
|
||
|
_request := buf.StackNewSize(requestLen)
|
||
|
defer common.KeepAlive(_request)
|
||
|
request := common.Dup(_request)
|
||
|
defer request.Release()
|
||
|
common.Must(
|
||
|
request.WriteByte(Version),
|
||
|
binary.Write(request, binary.BigEndian, hello.SendBPS),
|
||
|
binary.Write(request, binary.BigEndian, hello.RecvBPS),
|
||
|
binary.Write(request, binary.BigEndian, uint16(len(hello.Auth))),
|
||
|
common.Error(request.Write(hello.Auth)),
|
||
|
)
|
||
|
return common.Error(stream.Write(request.Bytes()))
|
||
|
}
|
||
|
|
||
|
func ReadClientHello(reader io.Reader) (*ClientHello, error) {
|
||
|
var version uint8
|
||
|
err := binary.Read(reader, binary.BigEndian, &version)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
if version != Version {
|
||
|
return nil, E.New("unsupported client version: ", version)
|
||
|
}
|
||
|
var clientHello ClientHello
|
||
|
err = binary.Read(reader, binary.BigEndian, &clientHello.SendBPS)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
err = binary.Read(reader, binary.BigEndian, &clientHello.RecvBPS)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
var authLen uint16
|
||
|
err = binary.Read(reader, binary.BigEndian, &authLen)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
clientHello.Auth = make([]byte, authLen)
|
||
|
_, err = io.ReadFull(reader, clientHello.Auth)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &clientHello, nil
|
||
|
}
|
||
|
|
||
|
type ServerHello struct {
|
||
|
OK bool
|
||
|
SendBPS uint64
|
||
|
RecvBPS uint64
|
||
|
Message string
|
||
|
}
|
||
|
|
||
|
func ReadServerHello(stream io.Reader) (*ServerHello, error) {
|
||
|
var responseLen int
|
||
|
responseLen += 1 // ok
|
||
|
responseLen += 8 // sendBPS
|
||
|
responseLen += 8 // recvBPS
|
||
|
responseLen += 2 // message len
|
||
|
_response := buf.StackNewSize(responseLen)
|
||
|
defer common.KeepAlive(_response)
|
||
|
response := common.Dup(_response)
|
||
|
defer response.Release()
|
||
|
_, err := response.ReadFullFrom(stream, responseLen)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
var serverHello ServerHello
|
||
|
serverHello.OK = response.Byte(0) == 1
|
||
|
serverHello.SendBPS = binary.BigEndian.Uint64(response.Range(1, 9))
|
||
|
serverHello.RecvBPS = binary.BigEndian.Uint64(response.Range(9, 17))
|
||
|
messageLen := binary.BigEndian.Uint16(response.Range(17, 19))
|
||
|
if messageLen == 0 {
|
||
|
return &serverHello, nil
|
||
|
}
|
||
|
message := make([]byte, messageLen)
|
||
|
_, err = io.ReadFull(stream, message)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
serverHello.Message = string(message)
|
||
|
return &serverHello, nil
|
||
|
}
|
||
|
|
||
|
func WriteServerHello(stream io.Writer, hello ServerHello) error {
|
||
|
var responseLen int
|
||
|
responseLen += 1 // ok
|
||
|
responseLen += 8 // sendBPS
|
||
|
responseLen += 8 // recvBPS
|
||
|
responseLen += 2 // message len
|
||
|
responseLen += len(hello.Message)
|
||
|
_response := buf.StackNewSize(responseLen)
|
||
|
defer common.KeepAlive(_response)
|
||
|
response := common.Dup(_response)
|
||
|
defer response.Release()
|
||
|
if hello.OK {
|
||
|
common.Must(response.WriteByte(1))
|
||
|
} else {
|
||
|
common.Must(response.WriteByte(0))
|
||
|
}
|
||
|
common.Must(
|
||
|
binary.Write(response, binary.BigEndian, hello.SendBPS),
|
||
|
binary.Write(response, binary.BigEndian, hello.RecvBPS),
|
||
|
binary.Write(response, binary.BigEndian, uint16(len(hello.Message))),
|
||
|
common.Error(response.WriteString(hello.Message)),
|
||
|
)
|
||
|
return common.Error(stream.Write(response.Bytes()))
|
||
|
}
|
||
|
|
||
|
type ClientRequest struct {
|
||
|
UDP bool
|
||
|
Host string
|
||
|
Port uint16
|
||
|
}
|
||
|
|
||
|
func ReadClientRequest(stream io.Reader) (*ClientRequest, error) {
|
||
|
var clientRequest ClientRequest
|
||
|
err := binary.Read(stream, binary.BigEndian, &clientRequest.UDP)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
var hostLen uint16
|
||
|
err = binary.Read(stream, binary.BigEndian, &hostLen)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
host := make([]byte, hostLen)
|
||
|
_, err = io.ReadFull(stream, host)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
clientRequest.Host = string(host)
|
||
|
err = binary.Read(stream, binary.BigEndian, &clientRequest.Port)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return &clientRequest, nil
|
||
|
}
|
||
|
|
||
|
func WriteClientRequest(stream io.Writer, request ClientRequest) error {
|
||
|
var requestLen int
|
||
|
requestLen += 1 // udp
|
||
|
requestLen += 2 // host len
|
||
|
requestLen += len(request.Host)
|
||
|
requestLen += 2 // port
|
||
|
_buffer := buf.StackNewSize(requestLen)
|
||
|
defer common.KeepAlive(_buffer)
|
||
|
buffer := common.Dup(_buffer)
|
||
|
defer buffer.Release()
|
||
|
if request.UDP {
|
||
|
common.Must(buffer.WriteByte(1))
|
||
|
} else {
|
||
|
common.Must(buffer.WriteByte(0))
|
||
|
}
|
||
|
common.Must(
|
||
|
binary.Write(buffer, binary.BigEndian, uint16(len(request.Host))),
|
||
|
common.Error(buffer.WriteString(request.Host)),
|
||
|
binary.Write(buffer, binary.BigEndian, request.Port),
|
||
|
)
|
||
|
return common.Error(stream.Write(buffer.Bytes()))
|
||
|
}
|
||
|
|
||
|
type ServerResponse struct {
|
||
|
OK bool
|
||
|
UDPSessionID uint32
|
||
|
Message string
|
||
|
}
|
||
|
|
||
|
func ReadServerResponse(stream io.Reader) (*ServerResponse, error) {
|
||
|
var responseLen int
|
||
|
responseLen += 1 // ok
|
||
|
responseLen += 4 // udp session id
|
||
|
responseLen += 2 // message len
|
||
|
_response := buf.StackNewSize(responseLen)
|
||
|
defer common.KeepAlive(_response)
|
||
|
response := common.Dup(_response)
|
||
|
defer response.Release()
|
||
|
_, err := response.ReadFullFrom(stream, responseLen)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
var serverResponse ServerResponse
|
||
|
serverResponse.OK = response.Byte(0) == 1
|
||
|
serverResponse.UDPSessionID = binary.BigEndian.Uint32(response.Range(1, 5))
|
||
|
messageLen := binary.BigEndian.Uint16(response.Range(5, 7))
|
||
|
if messageLen == 0 {
|
||
|
return &serverResponse, nil
|
||
|
}
|
||
|
message := make([]byte, messageLen)
|
||
|
_, err = io.ReadFull(stream, message)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
serverResponse.Message = string(message)
|
||
|
return &serverResponse, nil
|
||
|
}
|
||
|
|
||
|
func WriteServerResponse(stream io.Writer, response ServerResponse) error {
|
||
|
var responseLen int
|
||
|
responseLen += 1 // ok
|
||
|
responseLen += 4 // udp session id
|
||
|
responseLen += 2 // message len
|
||
|
responseLen += len(response.Message)
|
||
|
_buffer := buf.StackNewSize(responseLen)
|
||
|
defer common.KeepAlive(_buffer)
|
||
|
buffer := common.Dup(_buffer)
|
||
|
defer buffer.Release()
|
||
|
if response.OK {
|
||
|
common.Must(buffer.WriteByte(1))
|
||
|
} else {
|
||
|
common.Must(buffer.WriteByte(0))
|
||
|
}
|
||
|
common.Must(
|
||
|
binary.Write(buffer, binary.BigEndian, response.UDPSessionID),
|
||
|
binary.Write(buffer, binary.BigEndian, uint16(len(response.Message))),
|
||
|
common.Error(buffer.WriteString(response.Message)),
|
||
|
)
|
||
|
return common.Error(stream.Write(buffer.Bytes()))
|
||
|
}
|
||
|
|
||
|
type UDPMessage struct {
|
||
|
SessionID uint32
|
||
|
Host string
|
||
|
Port uint16
|
||
|
MsgID uint16 // doesn't matter when not fragmented, but must not be 0 when fragmented
|
||
|
FragID uint8 // doesn't matter when not fragmented, starts at 0 when fragmented
|
||
|
FragCount uint8 // must be 1 when not fragmented
|
||
|
Data []byte
|
||
|
}
|
||
|
|
||
|
func (m UDPMessage) HeaderSize() int {
|
||
|
return 4 + 2 + len(m.Host) + 2 + 2 + 1 + 1 + 2
|
||
|
}
|
||
|
|
||
|
func (m UDPMessage) Size() int {
|
||
|
return m.HeaderSize() + len(m.Data)
|
||
|
}
|
||
|
|
||
|
func ParseUDPMessage(packet []byte) (message UDPMessage, err error) {
|
||
|
reader := bytes.NewReader(packet)
|
||
|
err = binary.Read(reader, binary.BigEndian, &message.SessionID)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
var hostLen uint16
|
||
|
err = binary.Read(reader, binary.BigEndian, &hostLen)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
_, err = reader.Seek(int64(hostLen), io.SeekCurrent)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
message.Host = string(packet[6 : 6+hostLen])
|
||
|
err = binary.Read(reader, binary.BigEndian, &message.Port)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
err = binary.Read(reader, binary.BigEndian, &message.MsgID)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
err = binary.Read(reader, binary.BigEndian, &message.FragID)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
err = binary.Read(reader, binary.BigEndian, &message.FragCount)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
var dataLen uint16
|
||
|
err = binary.Read(reader, binary.BigEndian, &dataLen)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
if reader.Len() != int(dataLen) {
|
||
|
err = E.New("invalid data length")
|
||
|
}
|
||
|
dataOffset := int(reader.Size()) - reader.Len()
|
||
|
message.Data = packet[dataOffset:]
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func WriteUDPMessage(conn quic.Connection, message UDPMessage) error {
|
||
|
var messageLen int
|
||
|
messageLen += 4 // session id
|
||
|
messageLen += 2 // host len
|
||
|
messageLen += len(message.Host)
|
||
|
messageLen += 2 // port
|
||
|
messageLen += 2 // msg id
|
||
|
messageLen += 1 // frag id
|
||
|
messageLen += 1 // frag count
|
||
|
messageLen += 2 // data len
|
||
|
messageLen += len(message.Data)
|
||
|
_buffer := buf.StackNewSize(messageLen)
|
||
|
defer common.KeepAlive(_buffer)
|
||
|
buffer := common.Dup(_buffer)
|
||
|
defer buffer.Release()
|
||
|
err := writeUDPMessage(conn, message, buffer)
|
||
|
if errSize, ok := err.(quic.ErrMessageToLarge); ok {
|
||
|
// need to frag
|
||
|
message.MsgID = uint16(rand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1
|
||
|
fragMsgs := FragUDPMessage(message, int(errSize))
|
||
|
for _, fragMsg := range fragMsgs {
|
||
|
buffer.FullReset()
|
||
|
err = writeUDPMessage(conn, fragMsg, buffer)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
func writeUDPMessage(conn quic.Connection, message UDPMessage, buffer *buf.Buffer) error {
|
||
|
common.Must(
|
||
|
binary.Write(buffer, binary.BigEndian, message.SessionID),
|
||
|
binary.Write(buffer, binary.BigEndian, uint16(len(message.Host))),
|
||
|
common.Error(buffer.WriteString(message.Host)),
|
||
|
binary.Write(buffer, binary.BigEndian, message.Port),
|
||
|
binary.Write(buffer, binary.BigEndian, message.MsgID),
|
||
|
binary.Write(buffer, binary.BigEndian, message.FragID),
|
||
|
binary.Write(buffer, binary.BigEndian, message.FragCount),
|
||
|
binary.Write(buffer, binary.BigEndian, uint16(len(message.Data))),
|
||
|
common.Error(buffer.Write(message.Data)),
|
||
|
)
|
||
|
return conn.SendMessage(buffer.Bytes())
|
||
|
}
|
||
|
|
||
|
var _ net.Conn = (*Conn)(nil)
|
||
|
|
||
|
type Conn struct {
|
||
|
quic.Stream
|
||
|
destination M.Socksaddr
|
||
|
responseWritten bool
|
||
|
}
|
||
|
|
||
|
func NewConn(stream quic.Stream, destination M.Socksaddr) *Conn {
|
||
|
return &Conn{
|
||
|
Stream: stream,
|
||
|
destination: destination,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *Conn) LocalAddr() net.Addr {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *Conn) RemoteAddr() net.Addr {
|
||
|
return c.destination.TCPAddr()
|
||
|
}
|
||
|
|
||
|
func (c *Conn) ReaderReplaceable() bool {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (c *Conn) WriterReplaceable() bool {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func (c *Conn) Upstream() any {
|
||
|
return c.Stream
|
||
|
}
|
||
|
|
||
|
type PacketConn struct {
|
||
|
session quic.Connection
|
||
|
stream quic.Stream
|
||
|
sessionId uint32
|
||
|
destination M.Socksaddr
|
||
|
msgCh <-chan *UDPMessage
|
||
|
closer io.Closer
|
||
|
}
|
||
|
|
||
|
func NewPacketConn(session quic.Connection, stream quic.Stream, sessionId uint32, destination M.Socksaddr, msgCh <-chan *UDPMessage, closer io.Closer) *PacketConn {
|
||
|
return &PacketConn{
|
||
|
session: session,
|
||
|
stream: stream,
|
||
|
sessionId: sessionId,
|
||
|
destination: destination,
|
||
|
msgCh: msgCh,
|
||
|
closer: closer,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) Hold() {
|
||
|
// Hold the stream until it's closed
|
||
|
buf := make([]byte, 1024)
|
||
|
for {
|
||
|
_, err := c.stream.Read(buf)
|
||
|
if err != nil {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
_ = c.Close()
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {
|
||
|
msg := <-c.msgCh
|
||
|
if msg == nil {
|
||
|
err = net.ErrClosed
|
||
|
return
|
||
|
}
|
||
|
err = common.Error(buffer.Write(msg.Data))
|
||
|
destination = M.ParseSocksaddrHostPort(msg.Host, msg.Port)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) ReadPacketThreadSafe() (buffer *buf.Buffer, destination M.Socksaddr, err error) {
|
||
|
msg := <-c.msgCh
|
||
|
if msg == nil {
|
||
|
err = net.ErrClosed
|
||
|
return
|
||
|
}
|
||
|
buffer = buf.As(msg.Data)
|
||
|
destination = M.ParseSocksaddrHostPort(msg.Host, msg.Port)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {
|
||
|
return WriteUDPMessage(c.session, UDPMessage{
|
||
|
SessionID: c.sessionId,
|
||
|
Host: destination.Unwrap().AddrString(),
|
||
|
Port: destination.Port,
|
||
|
FragCount: 1,
|
||
|
Data: buffer.Bytes(),
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) LocalAddr() net.Addr {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) RemoteAddr() net.Addr {
|
||
|
return c.destination.UDPAddr()
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) SetDeadline(t time.Time) error {
|
||
|
return os.ErrInvalid
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) SetReadDeadline(t time.Time) error {
|
||
|
return os.ErrInvalid
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) SetWriteDeadline(t time.Time) error {
|
||
|
return os.ErrInvalid
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||
|
return 0, nil, os.ErrInvalid
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||
|
return 0, os.ErrInvalid
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) Read(b []byte) (n int, err error) {
|
||
|
return 0, os.ErrInvalid
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) Write(b []byte) (n int, err error) {
|
||
|
return 0, os.ErrInvalid
|
||
|
}
|
||
|
|
||
|
func (c *PacketConn) Close() error {
|
||
|
return common.Close(c.stream, c.closer)
|
||
|
}
|