mihomo/transport/tuic/protocol.go

468 lines
8.5 KiB
Go

package tuic
import (
"encoding/binary"
"fmt"
"io"
"net"
"net/netip"
"strconv"
"github.com/metacubex/quic-go"
"lukechampine.com/blake3"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/transport/socks5"
)
type BufferedReader interface {
io.Reader
io.ByteReader
}
type BufferedWriter interface {
io.Writer
io.ByteWriter
}
type CommandType byte
const (
AuthenticateType = CommandType(0x00)
ConnectType = CommandType(0x01)
PacketType = CommandType(0x02)
DissociateType = CommandType(0x03)
HeartbeatType = CommandType(0x04)
ResponseType = CommandType(0x05)
)
func (c CommandType) String() string {
switch c {
case AuthenticateType:
return "Authenticate"
case ConnectType:
return "Connect"
case PacketType:
return "Packet"
case DissociateType:
return "Dissociate"
case HeartbeatType:
return "Heartbeat"
case ResponseType:
return "Response"
default:
return fmt.Sprintf("UnknowCommand: %#x", byte(c))
}
}
func (c CommandType) BytesLen() int {
return 1
}
type CommandHead struct {
VER byte
TYPE CommandType
}
func NewCommandHead(TYPE CommandType) CommandHead {
return CommandHead{
VER: 0x04,
TYPE: TYPE,
}
}
func ReadCommandHead(reader BufferedReader) (c CommandHead, err error) {
c.VER, err = reader.ReadByte()
if err != nil {
return
}
TYPE, err := reader.ReadByte()
if err != nil {
return
}
c.TYPE = CommandType(TYPE)
return
}
func (c CommandHead) WriteTo(writer BufferedWriter) (err error) {
err = writer.WriteByte(c.VER)
if err != nil {
return
}
err = writer.WriteByte(byte(c.TYPE))
if err != nil {
return
}
return
}
func (c CommandHead) BytesLen() int {
return 1 + c.TYPE.BytesLen()
}
type Authenticate struct {
CommandHead
TKN [32]byte
}
func NewAuthenticate(TKN [32]byte) Authenticate {
return Authenticate{
CommandHead: NewCommandHead(AuthenticateType),
TKN: TKN,
}
}
func GenTKN(token string) [32]byte {
return blake3.Sum256([]byte(token))
}
func (c Authenticate) WriteTo(writer BufferedWriter) (err error) {
err = c.CommandHead.WriteTo(writer)
if err != nil {
return
}
_, err = writer.Write(c.TKN[:])
if err != nil {
return
}
return
}
func (c Authenticate) BytesLen() int {
return c.CommandHead.BytesLen() + 32
}
type Connect struct {
CommandHead
ADDR Address
}
func NewConnect(ADDR Address) Connect {
return Connect{
CommandHead: NewCommandHead(ConnectType),
ADDR: ADDR,
}
}
func (c Connect) WriteTo(writer BufferedWriter) (err error) {
err = c.CommandHead.WriteTo(writer)
if err != nil {
return
}
err = c.ADDR.WriteTo(writer)
if err != nil {
return
}
return
}
func (c Connect) BytesLen() int {
return c.CommandHead.BytesLen() + c.ADDR.BytesLen()
}
type Packet struct {
CommandHead
ASSOC_ID uint32
LEN uint16
ADDR Address
DATA []byte
}
func NewPacket(ASSOC_ID uint32, LEN uint16, ADDR Address, DATA []byte) Packet {
return Packet{
CommandHead: NewCommandHead(PacketType),
ASSOC_ID: ASSOC_ID,
LEN: LEN,
ADDR: ADDR,
DATA: DATA,
}
}
func ReadPacket(reader BufferedReader) (c Packet, err error) {
c.CommandHead, err = ReadCommandHead(reader)
if err != nil {
return
}
if c.CommandHead.TYPE != PacketType {
err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE)
}
err = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID)
if err != nil {
return
}
err = binary.Read(reader, binary.BigEndian, &c.LEN)
if err != nil {
return
}
c.ADDR, err = ReadAddress(reader)
if err != nil {
return
}
c.DATA = make([]byte, c.LEN)
_, err = io.ReadFull(reader, c.DATA)
if err != nil {
return
}
return
}
func (c Packet) WriteTo(writer BufferedWriter) (err error) {
err = c.CommandHead.WriteTo(writer)
if err != nil {
return
}
err = binary.Write(writer, binary.BigEndian, c.ASSOC_ID)
if err != nil {
return
}
err = binary.Write(writer, binary.BigEndian, c.LEN)
if err != nil {
return
}
err = c.ADDR.WriteTo(writer)
if err != nil {
return
}
_, err = writer.Write(c.DATA)
if err != nil {
return
}
return
}
func (c Packet) BytesLen() int {
return c.CommandHead.BytesLen() + 4 + 2 + c.ADDR.BytesLen() + len(c.DATA)
}
type Dissociate struct {
CommandHead
ASSOC_ID uint32
}
func NewDissociate(ASSOC_ID uint32) Dissociate {
return Dissociate{
CommandHead: NewCommandHead(DissociateType),
ASSOC_ID: ASSOC_ID,
}
}
func (c Dissociate) WriteTo(writer BufferedWriter) (err error) {
err = c.CommandHead.WriteTo(writer)
if err != nil {
return
}
err = binary.Write(writer, binary.BigEndian, c.ASSOC_ID)
if err != nil {
return
}
return
}
func (c Dissociate) BytesLen() int {
return c.CommandHead.BytesLen() + 4
}
type Heartbeat struct {
CommandHead
}
func NewHeartbeat() Heartbeat {
return Heartbeat{
CommandHead: NewCommandHead(HeartbeatType),
}
}
func ReadHeartbeat(reader BufferedReader) (c Response, err error) {
c.CommandHead, err = ReadCommandHead(reader)
if err != nil {
return
}
if c.CommandHead.TYPE != HeartbeatType {
err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE)
}
return
}
type Response struct {
CommandHead
REP byte
}
func NewResponse(REP byte) Response {
return Response{
CommandHead: NewCommandHead(ResponseType),
REP: REP,
}
}
func ReadResponse(reader BufferedReader) (c Response, err error) {
c.CommandHead, err = ReadCommandHead(reader)
if err != nil {
return
}
if c.CommandHead.TYPE != ResponseType {
err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE)
}
c.REP, err = reader.ReadByte()
if err != nil {
return
}
return
}
func (c Response) WriteTo(writer BufferedWriter) (err error) {
err = c.CommandHead.WriteTo(writer)
if err != nil {
return
}
err = writer.WriteByte(c.REP)
if err != nil {
return
}
return
}
func (c Response) IsSucceed() bool {
return c.REP == 0x00
}
func (c Response) IsFailed() bool {
return c.REP == 0xff
}
func (c Response) BytesLen() int {
return c.CommandHead.BytesLen() + 1
}
// Addr types
const (
AtypDomainName byte = 0
AtypIPv4 byte = 1
AtypIPv6 byte = 2
)
type Address struct {
TYPE byte
ADDR []byte
PORT uint16
}
func NewAddress(metadata *C.Metadata) Address {
var addrType byte
var addr []byte
switch metadata.AddrType() {
case socks5.AtypIPv4:
addrType = AtypIPv4
addr = make([]byte, net.IPv4len)
copy(addr[:], metadata.DstIP.AsSlice())
case socks5.AtypIPv6:
addrType = AtypIPv6
addr = make([]byte, net.IPv6len)
copy(addr[:], metadata.DstIP.AsSlice())
case socks5.AtypDomainName:
addrType = AtypDomainName
addr = make([]byte, len(metadata.Host)+1)
addr[0] = byte(len(metadata.Host))
copy(addr[1:], metadata.Host)
}
port, _ := strconv.ParseUint(metadata.DstPort, 10, 16)
return Address{
TYPE: addrType,
ADDR: addr,
PORT: uint16(port),
}
}
func NewAddressAddrPort(addrPort netip.AddrPort) Address {
var addrType byte
var addr []byte
if addrPort.Addr().Is4() {
addrType = AtypIPv4
addr = make([]byte, net.IPv4len)
} else {
addrType = AtypIPv6
addr = make([]byte, net.IPv6len)
}
copy(addr[:], addrPort.Addr().AsSlice())
return Address{
TYPE: addrType,
ADDR: addr,
PORT: addrPort.Port(),
}
}
func ReadAddress(reader BufferedReader) (c Address, err error) {
c.TYPE, err = reader.ReadByte()
if err != nil {
return
}
switch c.TYPE {
case AtypIPv4:
c.ADDR = make([]byte, net.IPv4len)
_, err = io.ReadFull(reader, c.ADDR)
if err != nil {
return
}
case AtypIPv6:
c.ADDR = make([]byte, net.IPv6len)
_, err = io.ReadFull(reader, c.ADDR)
if err != nil {
return
}
case AtypDomainName:
var addrLen byte
addrLen, err = reader.ReadByte()
if err != nil {
return
}
c.ADDR = make([]byte, addrLen+1)
c.ADDR[0] = addrLen
_, err = io.ReadFull(reader, c.ADDR[1:])
if err != nil {
return
}
}
err = binary.Read(reader, binary.BigEndian, &c.PORT)
if err != nil {
return
}
return
}
func (c Address) WriteTo(writer BufferedWriter) (err error) {
err = writer.WriteByte(c.TYPE)
if err != nil {
return
}
_, err = writer.Write(c.ADDR[:])
if err != nil {
return
}
err = binary.Write(writer, binary.BigEndian, c.PORT)
if err != nil {
return
}
return
}
func (c Address) UDPAddr() *net.UDPAddr {
return &net.UDPAddr{
IP: c.ADDR,
Port: int(c.PORT),
Zone: "",
}
}
func (c Address) BytesLen() int {
return 1 + len(c.ADDR) + 2
}
const (
ProtocolError = quic.ApplicationErrorCode(0xfffffff0)
AuthenticationFailed = quic.ApplicationErrorCode(0xfffffff1)
AuthenticationTimeout = quic.ApplicationErrorCode(0xfffffff2)
BadCommand = quic.ApplicationErrorCode(0xfffffff3)
)