mirror of
https://github.com/SagerNet/sing-quic.git
synced 2025-04-03 03:47:39 +03:00
254 lines
6.7 KiB
Go
254 lines
6.7 KiB
Go
package hysteria
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"io"
|
|
"time"
|
|
|
|
"github.com/sagernet/quic-go"
|
|
"github.com/sagernet/sing/common"
|
|
"github.com/sagernet/sing/common/buf"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
)
|
|
|
|
const (
|
|
MbpsToBps = 125000
|
|
MinSpeedBPS = 16384
|
|
DefaultALPN = "hysteria"
|
|
DefaultStreamReceiveWindow = 8388608 // 8MB
|
|
DefaultConnReceiveWindow = DefaultStreamReceiveWindow * 5 / 2 // 20MB
|
|
DefaultMaxIdleTimeout = 30 * time.Second
|
|
DefaultKeepAlivePeriod = 10 * time.Second
|
|
)
|
|
|
|
const (
|
|
ProtocolVersion = 3
|
|
ProtocolTimeout = 10 * time.Second
|
|
ErrorCodeGeneric = 0
|
|
ErrorCodeProtocolError = 1
|
|
ErrorCodeAuthError = 2
|
|
)
|
|
|
|
type ClientHello struct {
|
|
SendBPS uint64
|
|
RecvBPS uint64
|
|
Auth string
|
|
}
|
|
|
|
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.NewSize(requestLen)
|
|
defer request.Release()
|
|
common.Must(
|
|
request.WriteByte(ProtocolVersion),
|
|
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.WriteString(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 != ProtocolVersion {
|
|
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
|
|
}
|
|
if clientHello.SendBPS == 0 || clientHello.RecvBPS == 0 {
|
|
return nil, E.New("invalid rate from client")
|
|
}
|
|
var authLen uint16
|
|
err = binary.Read(reader, binary.BigEndian, &authLen)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authBytes := make([]byte, authLen)
|
|
_, err = io.ReadFull(reader, authBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clientHello.Auth = string(authBytes)
|
|
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.NewSize(responseLen)
|
|
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.NewSize(responseLen)
|
|
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(request ClientRequest, payload []byte) *buf.Buffer {
|
|
var requestLen int
|
|
requestLen += 1 // udp
|
|
requestLen += 2 // host len
|
|
requestLen += len(request.Host)
|
|
requestLen += 2 // port
|
|
buffer := buf.NewSize(requestLen + len(payload))
|
|
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),
|
|
common.Error(buffer.Write(payload)),
|
|
)
|
|
return buffer
|
|
}
|
|
|
|
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.NewSize(responseLen)
|
|
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 quic.Stream, response ServerResponse) error {
|
|
var responseLen int
|
|
responseLen += 1 // ok
|
|
responseLen += 4 // udp session id
|
|
responseLen += 2 // message len
|
|
responseLen += len(response.Message)
|
|
buffer := buf.NewSize(responseLen)
|
|
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()))
|
|
}
|