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.NewSize(requestLen) 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.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(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.NewSize(requestLen) 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.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 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.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())) } 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 } if 6+int(hostLen) > len(packet) { err = E.New("invalid host length") 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.NewSize(messageLen) defer buffer.Release() err := writeUDPMessage(conn, message, buffer) if errSize, ok := err.(quic.ErrMessageTooLarge); 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 needReadResponse bool } func NewConn(stream quic.Stream, destination M.Socksaddr, isClient bool) *Conn { return &Conn{ Stream: stream, destination: destination, needReadResponse: isClient, } } func (c *Conn) Read(p []byte) (n int, err error) { if c.needReadResponse { var response *ServerResponse response, err = ReadServerResponse(c.Stream) if err != nil { c.Close() return } if !response.OK { c.Close() return 0, E.New("remote error: ", response.Message) } c.needReadResponse = false } return c.Stream.Read(p) } func (c *Conn) LocalAddr() net.Addr { return nil } func (c *Conn) RemoteAddr() net.Addr { return c.destination.TCPAddr() } func (c *Conn) ReaderReplaceable() bool { return !c.needReadResponse } 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).Unwrap() 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).Unwrap() return } func (c *PacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error { return WriteUDPMessage(c.session, UDPMessage{ SessionID: c.sessionId, Host: destination.AddrString(), Port: destination.Port, FragCount: 1, Data: buffer.Bytes(), }) } func (c *PacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { msg := <-c.msgCh if msg == nil { err = net.ErrClosed return } n = copy(p, msg.Data) destination := M.ParseSocksaddrHostPort(msg.Host, msg.Port) if destination.IsFqdn() { addr = destination } else { addr = destination.UDPAddr() } return } func (c *PacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { err = c.WritePacket(buf.As(p), M.SocksaddrFromNet(addr)) if err == nil { n = len(p) } return } 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) NeedAdditionalReadDeadline() bool { return true } func (c *PacketConn) Read(b []byte) (n int, err error) { n, _, err = c.ReadFrom(b) return } func (c *PacketConn) Write(b []byte) (n int, err error) { return c.WriteTo(b, c.destination) } func (c *PacketConn) Close() error { return common.Close(c.stream, c.closer) }