chore: remove depend on lunixbochs/struc
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

This commit is contained in:
wwqgtxx 2025-08-16 11:16:53 +08:00
parent 76e40baebc
commit 48c1b1cdb2
6 changed files with 280 additions and 109 deletions

1
go.mod
View File

@ -13,7 +13,6 @@ require (
github.com/gofrs/uuid/v5 v5.3.2 github.com/gofrs/uuid/v5 v5.3.2
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905
github.com/klauspost/compress v1.17.9 // lastest version compatible with golang1.20 github.com/klauspost/compress v1.17.9 // lastest version compatible with golang1.20
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/mdlayher/netlink v1.7.2 github.com/mdlayher/netlink v1.7.2
github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab github.com/metacubex/amneziawg-go v0.0.0-20240922133038-fdf3a4d5a4ab
github.com/metacubex/bart v0.20.5 github.com/metacubex/bart v0.20.5

2
go.sum
View File

@ -84,8 +84,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=

View File

@ -1,7 +1,6 @@
package core package core
import ( import (
"bytes"
"context" "context"
"errors" "errors"
"fmt" "fmt"
@ -16,7 +15,6 @@ import (
"github.com/metacubex/mihomo/transport/hysteria/transport" "github.com/metacubex/mihomo/transport/hysteria/transport"
"github.com/metacubex/mihomo/transport/hysteria/utils" "github.com/metacubex/mihomo/transport/hysteria/utils"
"github.com/lunixbochs/struc"
"github.com/metacubex/quic-go" "github.com/metacubex/quic-go"
"github.com/metacubex/quic-go/congestion" "github.com/metacubex/quic-go/congestion"
"github.com/metacubex/randv2" "github.com/metacubex/randv2"
@ -104,31 +102,23 @@ func (c *Client) connectToServer(dialer utils.PacketDialer) error {
} }
func (c *Client) handleControlStream(qs *quic.Conn, stream *quic.Stream) (bool, string, error) { func (c *Client) handleControlStream(qs *quic.Conn, stream *quic.Stream) (bool, string, error) {
// Send protocol version
_, err := stream.Write([]byte{protocolVersion})
if err != nil {
return false, "", err
}
// Send client hello // Send client hello
err = struc.Pack(stream, &clientHello{ err := WriteClientHello(stream, ClientHello{
Rate: transmissionRate{ SendBPS: c.sendBPS,
SendBPS: c.sendBPS, RecvBPS: c.recvBPS,
RecvBPS: c.recvBPS, Auth: c.auth,
},
Auth: c.auth,
}) })
if err != nil { if err != nil {
return false, "", err return false, "", err
} }
// Receive server hello // Receive server hello
var sh serverHello sh, err := ReadServerHello(stream)
err = struc.Unpack(stream, &sh)
if err != nil { if err != nil {
return false, "", err return false, "", err
} }
// Set the congestion accordingly // Set the congestion accordingly
if sh.OK && c.congestionFactory != nil { if sh.OK && c.congestionFactory != nil {
qs.SetCongestionControl(c.congestionFactory(sh.Rate.RecvBPS)) qs.SetCongestionControl(c.congestionFactory(sh.RecvBPS))
} }
return sh.OK, sh.Message, nil return sh.OK, sh.Message, nil
} }
@ -140,7 +130,7 @@ func (c *Client) handleMessage(qs *quic.Conn) {
break break
} }
var udpMsg udpMessage var udpMsg udpMessage
err = struc.Unpack(bytes.NewBuffer(msg), &udpMsg) err = udpMsg.Unpack(msg)
if err != nil { if err != nil {
continue continue
} }
@ -200,7 +190,7 @@ func (c *Client) DialTCP(host string, port uint16, dialer utils.PacketDialer) (n
return nil, err return nil, err
} }
// Send request // Send request
err = struc.Pack(stream, &clientRequest{ err = WriteClientRequest(stream, ClientRequest{
UDP: false, UDP: false,
Host: host, Host: host,
Port: port, Port: port,
@ -213,8 +203,8 @@ func (c *Client) DialTCP(host string, port uint16, dialer utils.PacketDialer) (n
// and defer the response handling to the first Read() call // and defer the response handling to the first Read() call
if !c.fastOpen { if !c.fastOpen {
// Read response // Read response
var sr serverResponse var sr *ServerResponse
err = struc.Unpack(stream, &sr) sr, err = ReadServerResponse(stream)
if err != nil { if err != nil {
_ = stream.Close() _ = stream.Close()
return nil, err return nil, err
@ -239,16 +229,16 @@ func (c *Client) DialUDP(dialer utils.PacketDialer) (UDPConn, error) {
return nil, err return nil, err
} }
// Send request // Send request
err = struc.Pack(stream, &clientRequest{ err = WriteClientRequest(stream, ClientRequest{
UDP: true, UDP: false,
}) })
if err != nil { if err != nil {
_ = stream.Close() _ = stream.Close()
return nil, err return nil, err
} }
// Read response // Read response
var sr serverResponse var sr *ServerResponse
err = struc.Unpack(stream, &sr) sr, err = ReadServerResponse(stream)
if err != nil { if err != nil {
_ = stream.Close() _ = stream.Close()
return nil, err return nil, err
@ -306,8 +296,8 @@ type quicConn struct {
func (w *quicConn) Read(b []byte) (n int, err error) { func (w *quicConn) Read(b []byte) (n int, err error) {
if !w.Established { if !w.Established {
var sr serverResponse var sr *ServerResponse
err := struc.Unpack(w.Orig, &sr) sr, err = ReadServerResponse(w.Orig)
if err != nil { if err != nil {
_ = w.Close() _ = w.Close()
return 0, err return 0, err
@ -401,9 +391,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error {
Data: p, Data: p,
} }
// try no frag first // try no frag first
var msgBuf bytes.Buffer err = c.Session.SendDatagram(msg.Pack())
_ = struc.Pack(&msgBuf, &msg)
err = c.Session.SendDatagram(msgBuf.Bytes())
if err != nil { if err != nil {
var errSize *quic.DatagramTooLargeError var errSize *quic.DatagramTooLargeError
if errors.As(err, &errSize) { if errors.As(err, &errSize) {
@ -411,9 +399,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error {
msg.MsgID = uint16(randv2.IntN(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 msg.MsgID = uint16(randv2.IntN(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1
fragMsgs := fragUDPMessage(msg, int(errSize.MaxDatagramPayloadSize)) fragMsgs := fragUDPMessage(msg, int(errSize.MaxDatagramPayloadSize))
for _, fragMsg := range fragMsgs { for _, fragMsg := range fragMsgs {
msgBuf.Reset() err = c.Session.SendDatagram(fragMsg.Pack())
_ = struc.Pack(&msgBuf, &fragMsg)
err = c.Session.SendDatagram(msgBuf.Bytes())
if err != nil { if err != nil {
return err return err
} }

View File

@ -18,7 +18,6 @@ func fragUDPMessage(m udpMessage, maxSize int) []udpMessage {
frag := m frag := m
frag.FragID = fragID frag.FragID = fragID
frag.FragCount = fragCount frag.FragCount = fragCount
frag.DataLen = uint16(payloadSize)
frag.Data = fullPayload[off : off+payloadSize] frag.Data = fullPayload[off : off+payloadSize]
frags = append(frags, frag) frags = append(frags, frag)
off += payloadSize off += payloadSize
@ -56,7 +55,6 @@ func (d *defragger) Feed(m udpMessage) *udpMessage {
for _, frag := range d.frags { for _, frag := range d.frags {
data = append(data, frag.Data...) data = append(data, frag.Data...)
} }
m.DataLen = uint16(len(data))
m.Data = data m.Data = data
m.FragID = 0 m.FragID = 0
m.FragCount = 1 m.FragCount = 1

View File

@ -20,13 +20,11 @@ func Test_fragUDPMessage(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 0, FragID: 0,
FragCount: 1, FragCount: 1,
DataLen: 5,
Data: []byte("hello"), Data: []byte("hello"),
}, },
100, 100,
@ -34,13 +32,11 @@ func Test_fragUDPMessage(t *testing.T) {
[]udpMessage{ []udpMessage{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 0, FragID: 0,
FragCount: 1, FragCount: 1,
DataLen: 5,
Data: []byte("hello"), Data: []byte("hello"),
}, },
}, },
@ -50,13 +46,11 @@ func Test_fragUDPMessage(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 0, FragID: 0,
FragCount: 1, FragCount: 1,
DataLen: 5,
Data: []byte("hello"), Data: []byte("hello"),
}, },
22, 22,
@ -64,24 +58,20 @@ func Test_fragUDPMessage(t *testing.T) {
[]udpMessage{ []udpMessage{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 0, FragID: 0,
FragCount: 2, FragCount: 2,
DataLen: 4,
Data: []byte("hell"), Data: []byte("hell"),
}, },
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 1, FragID: 1,
FragCount: 2, FragCount: 2,
DataLen: 1,
Data: []byte("o"), Data: []byte("o"),
}, },
}, },
@ -91,13 +81,11 @@ func Test_fragUDPMessage(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 0, FragID: 0,
FragCount: 1, FragCount: 1,
DataLen: 20,
Data: []byte("wow wow wow lol lmao"), Data: []byte("wow wow wow lol lmao"),
}, },
23, 23,
@ -105,46 +93,38 @@ func Test_fragUDPMessage(t *testing.T) {
[]udpMessage{ []udpMessage{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 0, FragID: 0,
FragCount: 4, FragCount: 4,
DataLen: 5,
Data: []byte("wow w"), Data: []byte("wow w"),
}, },
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 1, FragID: 1,
FragCount: 4, FragCount: 4,
DataLen: 5,
Data: []byte("ow wo"), Data: []byte("ow wo"),
}, },
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 2, FragID: 2,
FragCount: 4, FragCount: 4,
DataLen: 5,
Data: []byte("w lol"), Data: []byte("w lol"),
}, },
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 3, FragID: 3,
FragCount: 4, FragCount: 4,
DataLen: 5,
Data: []byte(" lmao"), Data: []byte(" lmao"),
}, },
}, },
@ -174,25 +154,21 @@ func Test_defragger_Feed(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 0, FragID: 0,
FragCount: 1, FragCount: 1,
DataLen: 5,
Data: []byte("hello"), Data: []byte("hello"),
}, },
}, },
&udpMessage{ &udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 123, MsgID: 123,
FragID: 0, FragID: 0,
FragCount: 1, FragCount: 1,
DataLen: 5,
Data: []byte("hello"), Data: []byte("hello"),
}, },
}, },
@ -201,13 +177,11 @@ func Test_defragger_Feed(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 666, MsgID: 666,
FragID: 0, FragID: 0,
FragCount: 3, FragCount: 3,
DataLen: 5,
Data: []byte("hello"), Data: []byte("hello"),
}, },
}, },
@ -218,13 +192,11 @@ func Test_defragger_Feed(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 666, MsgID: 666,
FragID: 1, FragID: 1,
FragCount: 3, FragCount: 3,
DataLen: 8,
Data: []byte(" shitty "), Data: []byte(" shitty "),
}, },
}, },
@ -235,25 +207,21 @@ func Test_defragger_Feed(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 666, MsgID: 666,
FragID: 2, FragID: 2,
FragCount: 3, FragCount: 3,
DataLen: 7,
Data: []byte("world!!"), Data: []byte("world!!"),
}, },
}, },
&udpMessage{ &udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 666, MsgID: 666,
FragID: 0, FragID: 0,
FragCount: 1, FragCount: 1,
DataLen: 20,
Data: []byte("hello shitty world!!"), Data: []byte("hello shitty world!!"),
}, },
}, },
@ -262,13 +230,11 @@ func Test_defragger_Feed(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 777, MsgID: 777,
FragID: 0, FragID: 0,
FragCount: 2, FragCount: 2,
DataLen: 5,
Data: []byte("hello"), Data: []byte("hello"),
}, },
}, },
@ -279,13 +245,11 @@ func Test_defragger_Feed(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 778, MsgID: 778,
FragID: 1, FragID: 1,
FragCount: 2, FragCount: 2,
DataLen: 5,
Data: []byte(" moto"), Data: []byte(" moto"),
}, },
}, },
@ -296,13 +260,11 @@ func Test_defragger_Feed(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 777, MsgID: 777,
FragID: 1, FragID: 1,
FragCount: 2, FragCount: 2,
DataLen: 5,
Data: []byte(" moto"), Data: []byte(" moto"),
}, },
}, },
@ -313,25 +275,21 @@ func Test_defragger_Feed(t *testing.T) {
args{ args{
udpMessage{ udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 777, MsgID: 777,
FragID: 0, FragID: 0,
FragCount: 2, FragCount: 2,
DataLen: 5,
Data: []byte("hello"), Data: []byte("hello"),
}, },
}, },
&udpMessage{ &udpMessage{
SessionID: 123, SessionID: 123,
HostLen: 4,
Host: "test", Host: "test",
Port: 123, Port: 123,
MsgID: 777, MsgID: 777,
FragID: 0, FragID: 0,
FragCount: 1, FragCount: 1,
DataLen: 10,
Data: []byte("hello moto"), Data: []byte("hello moto"),
}, },
}, },

View File

@ -1,60 +1,241 @@
package core package core
import ( import (
"bytes"
"encoding/binary"
"errors"
"io"
"time" "time"
) )
const ( const (
protocolVersion = uint8(3) protocolVersion = uint8(3)
protocolVersionV2 = uint8(2) protocolTimeout = 10 * time.Second
protocolTimeout = 10 * time.Second
closeErrorCodeGeneric = 0 closeErrorCodeGeneric = 0
closeErrorCodeProtocol = 1 closeErrorCodeProtocol = 1
closeErrorCodeAuth = 2 closeErrorCodeAuth = 2
) )
type transmissionRate struct { type ClientHello struct {
SendBPS uint64 SendBPS uint64
RecvBPS uint64 RecvBPS uint64
}
type clientHello struct {
Rate transmissionRate
AuthLen uint16 `struc:"sizeof=Auth"`
Auth []byte Auth []byte
} }
type serverHello struct { func WriteClientHello(stream io.Writer, hello ClientHello) error {
OK bool var requestLen int
Rate transmissionRate requestLen += 1 // version
MessageLen uint16 `struc:"sizeof=Message"` requestLen += 8 // sendBPS
Message string 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
} }
type clientRequest struct { func ReadClientHello(stream io.Reader) (*ClientHello, error) {
UDP bool var responseLen int
HostLen uint16 `struc:"sizeof=Host"` responseLen += 1 // ok
Host string responseLen += 8 // sendBPS
Port uint16 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 serverResponse struct { 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 OK bool
UDPSessionID uint32 UDPSessionID uint32
MessageLen uint16 `struc:"sizeof=Message"`
Message string 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 { type udpMessage struct {
SessionID uint32 SessionID uint32
HostLen uint16 `struc:"sizeof=Host"`
Host string Host string
Port uint16 Port uint16
MsgID uint16 // doesn't matter when not fragmented, but must not be 0 when fragmented 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 FragID uint8 // doesn't matter when not fragmented, starts at 0 when fragmented
FragCount uint8 // must be 1 when not fragmented FragCount uint8 // must be 1 when not fragmented
DataLen uint16 `struc:"sizeof=Data"`
Data []byte Data []byte
} }
@ -66,11 +247,62 @@ func (m udpMessage) Size() int {
return m.HeaderSize() + len(m.Data) return m.HeaderSize() + len(m.Data)
} }
type udpMessageV2 struct { func (m udpMessage) Pack() []byte {
SessionID uint32 data := make([]byte, m.Size())
HostLen uint16 `struc:"sizeof=Host"` buffer := bytes.NewBuffer(data)
Host string _ = binary.Write(buffer, binary.BigEndian, m.SessionID)
Port uint16 _ = binary.Write(buffer, binary.BigEndian, uint16(len(m.Host)))
DataLen uint16 `struc:"sizeof=Data"` buffer.WriteString(m.Host)
Data []byte _ = 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
} }