| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- package tuic
- import (
- "encoding/binary"
- "errors"
- "fmt"
- "io"
- "net"
- "strconv"
- )
- const (
- // ProtocolVersion is the TUIC protocol version (0x05).
- ProtocolVersion byte = 0x05
- // Command types
- CmdAuthenticate byte = 0x00
- CmdConnect byte = 0x01
- CmdPacket byte = 0x02
- CmdDissociate byte = 0x03
- CmdHeartbeat byte = 0x04
- // Address types
- AddrTypeDomain byte = 0x00
- AddrTypeIPv4 byte = 0x01
- AddrTypeIPv6 byte = 0x02
- AddrTypeNone byte = 0xff
- )
- var (
- ErrInvalidVersion = errors.New("tuic: invalid protocol version")
- ErrInvalidCmd = errors.New("tuic: invalid command type")
- ErrInvalidAddr = errors.New("tuic: invalid address format")
- )
- // Address represents a network endpoint (host + port) in TUIC v5.
- type Address struct {
- Type byte
- Host string
- IP net.IP
- Port uint16
- }
- // String returns "host:port" suitable for net.Dial.
- func (a *Address) String() string {
- if a == nil || a.Type == AddrTypeNone {
- return ""
- }
- if len(a.IP) > 0 {
- return net.JoinHostPort(a.IP.String(), strconv.Itoa(int(a.Port)))
- }
- return net.JoinHostPort(a.Host, strconv.Itoa(int(a.Port)))
- }
- // ReadAddress decodes a TUIC v5 address from the reader.
- func ReadAddress(r io.Reader) (*Address, error) {
- var typeBuf [1]byte
- if _, err := io.ReadFull(r, typeBuf[:]); err != nil {
- return nil, err
- }
- addrType := typeBuf[0]
- if addrType == AddrTypeNone {
- return &Address{Type: AddrTypeNone}, nil
- }
- addr := &Address{Type: addrType}
- switch addrType {
- case AddrTypeIPv4:
- var ip [4]byte
- if _, err := io.ReadFull(r, ip[:]); err != nil {
- return nil, err
- }
- addr.IP = net.IP(ip[:])
- addr.Host = addr.IP.String()
- case AddrTypeIPv6:
- var ip [16]byte
- if _, err := io.ReadFull(r, ip[:]); err != nil {
- return nil, err
- }
- addr.IP = net.IP(ip[:])
- addr.Host = addr.IP.String()
- case AddrTypeDomain:
- var lenBuf [1]byte
- if _, err := io.ReadFull(r, lenBuf[:]); err != nil {
- return nil, err
- }
- dLen := int(lenBuf[0])
- if dLen == 0 {
- return nil, ErrInvalidAddr
- }
- domainBuf := make([]byte, dLen)
- if _, err := io.ReadFull(r, domainBuf); err != nil {
- return nil, err
- }
- addr.Host = string(domainBuf)
- default:
- return nil, fmt.Errorf("%w: unknown type 0x%02x", ErrInvalidAddr, addrType)
- }
- var portBuf [2]byte
- if _, err := io.ReadFull(r, portBuf[:]); err != nil {
- return nil, err
- }
- addr.Port = binary.BigEndian.Uint16(portBuf[:])
- return addr, nil
- }
- // WriteAddress encodes a TUIC v5 address to the writer.
- func WriteAddress(w io.Writer, addr *Address) error {
- if addr == nil || addr.Type == AddrTypeNone {
- _, err := w.Write([]byte{AddrTypeNone})
- return err
- }
- var buf []byte
- switch addr.Type {
- case AddrTypeIPv4:
- ip4 := addr.IP.To4()
- if len(ip4) != 4 {
- return ErrInvalidAddr
- }
- buf = make([]byte, 1+4+2)
- buf[0] = AddrTypeIPv4
- copy(buf[1:5], ip4)
- binary.BigEndian.PutUint16(buf[5:7], addr.Port)
- case AddrTypeIPv6:
- ip16 := addr.IP.To16()
- if len(ip16) != 16 {
- return ErrInvalidAddr
- }
- buf = make([]byte, 1+16+2)
- buf[0] = AddrTypeIPv6
- copy(buf[1:17], ip16)
- binary.BigEndian.PutUint16(buf[17:19], addr.Port)
- case AddrTypeDomain:
- dLen := len(addr.Host)
- if dLen == 0 || dLen > 255 {
- return ErrInvalidAddr
- }
- buf = make([]byte, 1+1+dLen+2)
- buf[0] = AddrTypeDomain
- buf[1] = byte(dLen)
- copy(buf[2:2+dLen], []byte(addr.Host))
- binary.BigEndian.PutUint16(buf[2+dLen:4+dLen], addr.Port)
- default:
- return ErrInvalidAddr
- }
- _, err := w.Write(buf)
- return err
- }
- // ReadCommand reads the 2-byte TUIC command header: [VER (1)][TYPE (1)].
- func ReadCommand(r io.Reader) (byte, byte, error) {
- var hdr [2]byte
- if _, err := io.ReadFull(r, hdr[:]); err != nil {
- return 0, 0, err
- }
- if hdr[0] != ProtocolVersion {
- return hdr[0], hdr[1], fmt.Errorf("%w: got 0x%02x, want 0x%02x", ErrInvalidVersion, hdr[0], ProtocolVersion)
- }
- return hdr[0], hdr[1], nil
- }
- // PacketHeader represents the header of a UDP Packet command (0x02).
- type PacketHeader struct {
- AssocID uint16
- PktID uint16
- FragTotal uint8
- FragID uint8
- Size uint16
- Addr *Address
- }
- // ReadPacketHeader reads the packet command fields following [VER][0x02].
- func ReadPacketHeader(r io.Reader) (*PacketHeader, error) {
- var fixed [8]byte
- if _, err := io.ReadFull(r, fixed[:]); err != nil {
- return nil, err
- }
- ph := &PacketHeader{
- AssocID: binary.BigEndian.Uint16(fixed[0:2]),
- PktID: binary.BigEndian.Uint16(fixed[2:4]),
- FragTotal: fixed[4],
- FragID: fixed[5],
- Size: binary.BigEndian.Uint16(fixed[6:8]),
- }
- addr, err := ReadAddress(r)
- if err != nil {
- return nil, err
- }
- ph.Addr = addr
- return ph, nil
- }
- // WritePacket writes a complete Packet command frame to w.
- func WritePacket(w io.Writer, assocID, pktID uint16, fragTotal, fragID uint8, addr *Address, payload []byte) error {
- hdr := make([]byte, 10)
- hdr[0] = ProtocolVersion
- hdr[1] = CmdPacket
- binary.BigEndian.PutUint16(hdr[2:4], assocID)
- binary.BigEndian.PutUint16(hdr[4:6], pktID)
- hdr[6] = fragTotal
- hdr[7] = fragID
- binary.BigEndian.PutUint16(hdr[8:10], uint16(len(payload)))
- if _, err := w.Write(hdr); err != nil {
- return err
- }
- if err := WriteAddress(w, addr); err != nil {
- return err
- }
- _, err := w.Write(payload)
- return err
- }
|