mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2025-12-22 10:40:04 +08:00
Some checks are pending
Test / test (1.20, macos-13) (push) Waiting to run
Test / test (1.20, macos-latest) (push) Waiting to run
Test / test (1.20, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.20, ubuntu-latest) (push) Waiting to run
Test / test (1.20, windows-latest) (push) Waiting to run
Test / test (1.21, macos-13) (push) Waiting to run
Test / test (1.21, macos-latest) (push) Waiting to run
Test / test (1.21, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.21, ubuntu-latest) (push) Waiting to run
Test / test (1.21, windows-latest) (push) Waiting to run
Test / test (1.22, macos-13) (push) Waiting to run
Test / test (1.22, macos-latest) (push) Waiting to run
Test / test (1.22, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.22, ubuntu-latest) (push) Waiting to run
Test / test (1.22, windows-latest) (push) Waiting to run
Test / test (1.23, macos-13) (push) Waiting to run
Test / test (1.23, macos-latest) (push) Waiting to run
Test / test (1.23, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.23, ubuntu-latest) (push) Waiting to run
Test / test (1.23, windows-latest) (push) Waiting to run
Test / test (1.24, macos-13) (push) Waiting to run
Test / test (1.24, macos-latest) (push) Waiting to run
Test / test (1.24, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.24, ubuntu-latest) (push) Waiting to run
Test / test (1.24, windows-latest) (push) Waiting to run
Test / test (1.25, macos-13) (push) Waiting to run
Test / test (1.25, macos-latest) (push) Waiting to run
Test / test (1.25, ubuntu-24.04-arm) (push) Waiting to run
Test / test (1.25, ubuntu-latest) (push) Waiting to run
Test / test (1.25, windows-latest) (push) Waiting to run
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run
309 lines
7.7 KiB
Go
309 lines
7.7 KiB
Go
package core
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"errors"
|
|
"io"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
protocolVersion = uint8(3)
|
|
protocolTimeout = 10 * time.Second
|
|
|
|
closeErrorCodeGeneric = 0
|
|
closeErrorCodeProtocol = 1
|
|
closeErrorCodeAuth = 2
|
|
)
|
|
|
|
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 := make([]byte, requestLen)
|
|
request[0] = protocolVersion
|
|
binary.BigEndian.PutUint64(request[1:9], hello.SendBPS)
|
|
binary.BigEndian.PutUint64(request[9:17], hello.RecvBPS)
|
|
binary.BigEndian.PutUint16(request[17:19], uint16(len(hello.Auth)))
|
|
copy(request[19:], hello.Auth)
|
|
_, err := stream.Write(request)
|
|
return err
|
|
}
|
|
|
|
func ReadClientHello(stream io.Reader) (*ClientHello, error) {
|
|
var responseLen int
|
|
responseLen += 1 // ok
|
|
responseLen += 8 // sendBPS
|
|
responseLen += 8 // recvBPS
|
|
responseLen += 2 // auth len
|
|
response := make([]byte, responseLen)
|
|
_, err := io.ReadFull(stream, response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if response[0] != protocolVersion {
|
|
return nil, errors.New("unsupported client version")
|
|
}
|
|
var clientHello ClientHello
|
|
clientHello.SendBPS = binary.BigEndian.Uint64(response[1:9])
|
|
clientHello.RecvBPS = binary.BigEndian.Uint64(response[9:17])
|
|
authLen := binary.BigEndian.Uint16(response[17:19])
|
|
|
|
if clientHello.SendBPS == 0 || clientHello.RecvBPS == 0 {
|
|
return nil, errors.New("invalid rate from client")
|
|
}
|
|
|
|
authBytes := make([]byte, authLen)
|
|
_, err = io.ReadFull(stream, authBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
clientHello.Auth = 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 := make([]byte, responseLen)
|
|
_, err := io.ReadFull(stream, response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var serverHello ServerHello
|
|
serverHello.OK = response[0] == 1
|
|
serverHello.SendBPS = binary.BigEndian.Uint64(response[1:9])
|
|
serverHello.RecvBPS = binary.BigEndian.Uint64(response[9:17])
|
|
messageLen := binary.BigEndian.Uint16(response[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 := make([]byte, responseLen)
|
|
if hello.OK {
|
|
response[0] = 1
|
|
} else {
|
|
response[0] = 0
|
|
}
|
|
binary.BigEndian.PutUint64(response[1:9], hello.SendBPS)
|
|
binary.BigEndian.PutUint64(response[9:17], hello.RecvBPS)
|
|
binary.BigEndian.PutUint16(response[17:19], uint16(len(hello.Message)))
|
|
copy(response[19:], hello.Message)
|
|
_, err := stream.Write(response)
|
|
return err
|
|
}
|
|
|
|
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 := make([]byte, requestLen)
|
|
if request.UDP {
|
|
buffer[0] = 1
|
|
} else {
|
|
buffer[0] = 0
|
|
}
|
|
binary.BigEndian.PutUint16(buffer[1:3], uint16(len(request.Host)))
|
|
n := copy(buffer[3:], request.Host)
|
|
binary.BigEndian.PutUint16(buffer[3+n:3+n+2], request.Port)
|
|
_, err := stream.Write(buffer)
|
|
return err
|
|
}
|
|
|
|
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 := make([]byte, responseLen)
|
|
_, err := io.ReadFull(stream, response)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var serverResponse ServerResponse
|
|
serverResponse.OK = response[0] == 1
|
|
serverResponse.UDPSessionID = binary.BigEndian.Uint32(response[1:5])
|
|
messageLen := binary.BigEndian.Uint16(response[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 := make([]byte, responseLen)
|
|
if response.OK {
|
|
buffer[0] = 1
|
|
} else {
|
|
buffer[0] = 0
|
|
}
|
|
binary.BigEndian.PutUint32(buffer[1:5], response.UDPSessionID)
|
|
binary.BigEndian.PutUint16(buffer[5:7], uint16(len(response.Message)))
|
|
copy(buffer[7:], response.Message)
|
|
_, err := stream.Write(buffer)
|
|
return err
|
|
}
|
|
|
|
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 (m udpMessage) Pack() []byte {
|
|
data := make([]byte, m.Size())
|
|
buffer := bytes.NewBuffer(data)
|
|
_ = binary.Write(buffer, binary.BigEndian, m.SessionID)
|
|
_ = binary.Write(buffer, binary.BigEndian, uint16(len(m.Host)))
|
|
buffer.WriteString(m.Host)
|
|
_ = binary.Write(buffer, binary.BigEndian, m.Port)
|
|
_ = binary.Write(buffer, binary.BigEndian, m.MsgID)
|
|
_ = binary.Write(buffer, binary.BigEndian, m.FragID)
|
|
_ = binary.Write(buffer, binary.BigEndian, m.FragCount)
|
|
_ = binary.Write(buffer, binary.BigEndian, uint16(len(m.Data)))
|
|
buffer.Write(m.Data)
|
|
return buffer.Bytes()
|
|
}
|
|
|
|
func (m *udpMessage) Unpack(data []byte) error {
|
|
reader := bytes.NewReader(data)
|
|
err := binary.Read(reader, binary.BigEndian, &m.SessionID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var hostLen uint16
|
|
err = binary.Read(reader, binary.BigEndian, &hostLen)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hostBytes := make([]byte, hostLen)
|
|
_, err = io.ReadFull(reader, hostBytes)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
m.Host = string(hostBytes)
|
|
err = binary.Read(reader, binary.BigEndian, &m.Port)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = binary.Read(reader, binary.BigEndian, &m.MsgID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = binary.Read(reader, binary.BigEndian, &m.FragID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = binary.Read(reader, binary.BigEndian, &m.FragCount)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var dataLen uint16
|
|
err = binary.Read(reader, binary.BigEndian, &dataLen)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if reader.Len() != int(dataLen) {
|
|
return errors.New("invalid data length")
|
|
}
|
|
m.Data = data[len(data)-reader.Len():]
|
|
return nil
|
|
}
|