Files
OpenGFW/analyzer/udp/easytier_test.go
T
Mxmilu666andClaude Fable 5.1 cc6ad72706 feat(udp): add EasyTier analyzer for tcp:// and udp:// transports
Port of NoBrand-Cloud/OpenGFW-RS commit 1177ff7 to Go.

EasyTier's ciphers cover only the payload: the 16-byte PeerManagerHeader
stays in the clear, so peer ids, packet type and two redundant length
fields are readable even on an encrypted mesh. The plaintext handshake
also carries the network name, which lets a rule block one mesh and
leave the rest alone (easytier.network_name). A Syn answered by a Sack
echoing the same connection id and magic, or a handshake carrying the
0xd1e1a5e1 magic, settles a flow on its own; otherwise two consistent
frames are required. Hole-punch probes are reported but never set yes.

The wg://, quic:// and wss:// transports are left to the wireguard, quic
and tls analyzers. fet does not cover the tcp:// transport because it
classifies the plaintext handshake segment and is then done.

Rules must be written as `easytier != nil && ...`: the rule engine only
exposes analyzers that have published properties.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RjQ1CTBn8j65cUZ2JKsY7L
2026-09-12 17:05:39 +08:00

563 lines
19 KiB
Go

package udp
import (
"bytes"
"encoding/binary"
"fmt"
"testing"
"github.com/apernet/OpenGFW/analyzer"
"github.com/apernet/OpenGFW/analyzer/tcp"
)
func etVarint(v uint64) []byte {
var out []byte
for {
b := byte(v & 0x7f)
v >>= 7
if v != 0 {
b |= 0x80
}
out = append(out, b)
if v == 0 {
return out
}
}
}
func etProtoVarint(field uint32, v uint64) []byte {
return append(etVarint(uint64(field)<<3), etVarint(v)...)
}
func etProtoBytes(field uint32, v []byte) []byte {
out := etVarint(uint64(field)<<3 | 2)
out = append(out, etVarint(uint64(len(v)))...)
return append(out, v...)
}
// etHandshakeRequest HandshakeRequest,按 send_handshake 的构造方式
func etHandshakeRequest(peerID uint32, networkName string) []byte {
var out []byte
out = append(out, etProtoVarint(1, easyTierHandshakeMagic)...)
out = append(out, etProtoVarint(2, uint64(peerID))...)
out = append(out, etProtoVarint(3, 1)...)
out = append(out, etProtoBytes(4, []byte("liveness-echo-v1"))...)
out = append(out, etProtoBytes(5, []byte(networkName))...)
out = append(out, etProtoBytes(6, make([]byte, easyTierSecretDigestLen))...)
return out
}
// etNoiseMsg1 Noise XX 先写的临时公钥,后面跟 PeerConnNoiseMsg1Pb
func etNoiseMsg1(networkName string) []byte {
out := bytes.Repeat([]byte{0xab}, easyTierNoiseEphemeralLen)
out = append(out, etProtoVarint(1, 1)...)
return append(out, etProtoBytes(2, []byte(networkName))...)
}
// etBody 帧体:16 字节 PeerManagerHeader 加载荷
func etBody(packetType uint8, from, to uint32, payload []byte) []byte {
return etBodyRaw(packetType, from, to, 0, 1, 0, uint32(len(payload)), payload)
}
func etBodyRaw(packetType uint8, from, to uint32, flags, forwardCounter, reserved uint8, declaredLen uint32, payload []byte) []byte {
out := make([]byte, 0, easyTierPeerManagerHeaderSize+len(payload))
out = binary.LittleEndian.AppendUint32(out, from)
out = binary.LittleEndian.AppendUint32(out, to)
out = append(out, packetType, flags, forwardCounter, reserved)
out = binary.LittleEndian.AppendUint32(out, declaredLen)
return append(out, payload...)
}
// etDatagram 8 字节 UDPTunnelHeader 加帧体
func etDatagram(connID uint32, msgType uint8, body []byte) []byte {
out := make([]byte, 0, easyTierUDPTunnelHeaderSize+len(body))
out = binary.LittleEndian.AppendUint32(out, connID)
out = append(out, msgType, 0)
out = binary.LittleEndian.AppendUint16(out, uint16(len(body)))
return append(out, body...)
}
func etSyn(connID uint32, magic uint64) []byte {
return etDatagram(connID, easyTierMsgSyn, binary.LittleEndian.AppendUint64(nil, magic))
}
func etSack(connID uint32, magic uint64) []byte {
return etDatagram(connID, easyTierMsgSack, binary.LittleEndian.AppendUint64(nil, magic))
}
// etSegment 4 字节 TCPTunnelHeader 加帧体
func etSegment(body []byte) []byte {
out := binary.LittleEndian.AppendUint32(nil, uint32(len(body)))
return append(out, body...)
}
func etUDP() analyzer.UDPStream {
return (&EasyTierAnalyzer{}).NewUDP(analyzer.UDPInfo{}, nil)
}
func etTCP() analyzer.TCPStream {
return (&EasyTierAnalyzer{}).NewTCP(analyzer.TCPInfo{}, nil)
}
func etFeedUDP(s analyzer.UDPStream, rev bool, data []byte) (analyzer.PropMap, bool) {
u, done := s.Feed(rev, data)
if u == nil {
return analyzer.PropMap{}, done
}
return u.M, done
}
func etFeedTCP(s analyzer.TCPStream, rev bool, data []byte) (analyzer.PropMap, bool) {
u, done := s.Feed(rev, false, false, 0, data)
if u == nil {
return analyzer.PropMap{}, done
}
return u.M, done
}
func etText(m analyzer.PropMap, key string) string {
v, ok := m[key]
if !ok {
return "<missing>"
}
return fmt.Sprint(v)
}
func etFlag(m analyzer.PropMap, key string) bool {
v, _ := m[key].(bool)
return v
}
func etExpect(t *testing.T, m analyzer.PropMap, key, want string) {
t.Helper()
if got := etText(m, key); got != want {
t.Fatalf("%s = %q, want %q (props: %v)", key, got, want, m)
}
}
func TestEasyTier_HandshakeMagicEncodesToDocumentedBytes(t *testing.T) {
want := []byte{0x08, 0xe1, 0xcb, 0x86, 0x8f, 0x0d}
if got := etProtoVarint(1, easyTierHandshakeMagic); !bytes.Equal(got, want) {
t.Fatalf("magic encodes to % x, want % x", got, want)
}
}
func TestEasyTier_UDPHandshakeNamesTheMeshOnFirstDatagram(t *testing.T) {
payload := etHandshakeRequest(0x1a2b3c4d, "office-mesh")
m, done := etFeedUDP(etUDP(), false, etDatagram(7, easyTierMsgData, etBody(easyTierPktHandshake, 0x1a2b3c4d, 0, payload)))
if done {
t.Fatal("stream done after a single handshake")
}
if !etFlag(m, "yes") {
t.Fatal("the magic alone should settle it")
}
if !etFlag(m, "magic_ok") {
t.Fatal("magic_ok not set")
}
etExpect(t, m, "network_name", "office-mesh")
etExpect(t, m, "version", "1")
etExpect(t, m, "transport", "udp")
etExpect(t, m, "msg_type_name", "data")
etExpect(t, m, "packet_type_name", "handshake")
etExpect(t, m, "from_peer_id", "439041101")
etExpect(t, m, "to_peer_id", "0")
etExpect(t, m, "secure", "false")
}
func TestEasyTier_TCPHandshakeNamesTheMeshOnFirstFrame(t *testing.T) {
payload := etHandshakeRequest(9, "office-mesh")
m, done := etFeedTCP(etTCP(), false, etSegment(etBody(easyTierPktHandshake, 9, 0, payload)))
if done {
t.Fatal("stream done after a single handshake")
}
if !etFlag(m, "yes") {
t.Fatal("yes not set")
}
etExpect(t, m, "network_name", "office-mesh")
etExpect(t, m, "transport", "tcp")
etExpect(t, m, "tx_pkt_cnt", "1")
}
func TestEasyTier_HandshakeWithWrongMagicIsNotConclusive(t *testing.T) {
payload := append(etProtoVarint(1, 0xd1e1a5e0), etProtoBytes(5, []byte("office-mesh"))...)
m, _ := etFeedUDP(etUDP(), false, etDatagram(7, easyTierMsgData, etBody(easyTierPktHandshake, 1, 0, payload)))
// 帧结构仍然自洽,字段照发;但一帧不够,而且对网络不做任何声明
if etFlag(m, "yes") {
t.Fatal("yes set on a wrong magic")
}
etExpect(t, m, "network_name", "<missing>")
etExpect(t, m, "magic_ok", "<missing>")
}
func TestEasyTier_HandshakeWithWrongDigestWidthIsRejected(t *testing.T) {
// 上游这种情况会拒连,报匹配等于报一个根本连不起来的 mesh 名
payload := etProtoVarint(1, easyTierHandshakeMagic)
payload = append(payload, etProtoBytes(5, []byte("office-mesh"))...)
payload = append(payload, etProtoBytes(6, make([]byte, 16))...)
m, _ := etFeedUDP(etUDP(), false, etDatagram(7, easyTierMsgData, etBody(easyTierPktHandshake, 1, 0, payload)))
if etFlag(m, "yes") {
t.Fatal("yes set on a wrong digest width")
}
etExpect(t, m, "network_name", "<missing>")
}
func TestEasyTier_NoiseFirstMessageStillGivesUpTheNetworkName(t *testing.T) {
// Noise XX 写 msg1 时还没有密钥,临时公钥后面的载荷是明文
s := etUDP()
m, _ := etFeedUDP(s, false, etDatagram(7, easyTierMsgData, etBody(easyTierPktNoiseMsg1, 5, 0, etNoiseMsg1("secure-mesh"))))
if !etFlag(m, "secure") {
t.Fatal("secure not set")
}
etExpect(t, m, "network_name", "secure-mesh")
if etFlag(m, "yes") {
t.Fatal("one frame with no magic to check should not settle it")
}
// 反方向再来一帧就到阈值了
m, _ = etFeedUDP(s, true, etDatagram(7, easyTierMsgData, etBody(14, 6, 5, bytes.Repeat([]byte{0xcd}, 48))))
if !etFlag(m, "yes") {
t.Fatal("two consistent frames should settle it")
}
etExpect(t, m, "packet_type_name", "noise_handshake_msg2")
}
func TestEasyTier_OpaqueNoiseMessageSaysNothingAboutTheNetwork(t *testing.T) {
m, _ := etFeedUDP(etUDP(), false, etDatagram(7, easyTierMsgData, etBody(easyTierPktNoiseMsg1, 5, 0, bytes.Repeat([]byte{0x5a}, 96))))
if !etFlag(m, "secure") {
t.Fatal("secure not set")
}
etExpect(t, m, "network_name", "<missing>")
}
func TestEasyTier_SynAnsweredByItsSackSettlesTheFlow(t *testing.T) {
s := etUDP()
m, _ := etFeedUDP(s, false, etSyn(0x12345678, 0x0102030405060708))
if etFlag(m, "yes") {
t.Fatal("a lone syn is 16 bytes of nearly nothing")
}
etExpect(t, m, "msg_type_name", "syn")
m, _ = etFeedUDP(s, true, etSack(0x12345678, 0x0102030405060708))
if !etFlag(m, "yes") {
t.Fatal("a matching sack should settle it")
}
etExpect(t, m, "msg_type_name", "sack")
etExpect(t, m, "conn_id", "305419896")
etExpect(t, m, "tx_pkt_cnt", "1")
etExpect(t, m, "rx_pkt_cnt", "1")
}
func TestEasyTier_SackEchoingTheWrongMagicSettlesNothing(t *testing.T) {
s := etUDP()
etFeedUDP(s, false, etSyn(0x12345678, 1))
m, _ := etFeedUDP(s, true, etSack(0x12345678, 2))
if etFlag(m, "yes") {
t.Fatal("yes set on a mismatched magic")
}
}
func TestEasyTier_SackDownTheSameDirectionSettlesNothing(t *testing.T) {
s := etUDP()
etFeedUDP(s, false, etSyn(9, 42))
m, _ := etFeedUDP(s, false, etSack(9, 42))
if etFlag(m, "yes") {
t.Fatal("yes set on a same-direction sack")
}
}
func TestEasyTier_HolePunchProbesAreReportedButDoNotSettleTheFlow(t *testing.T) {
// 往可能根本没跑 EasyTier 的地址上喷的,规则得点名才拦
s := etUDP()
var m analyzer.PropMap
for i := 0; i < 3; i++ {
m, _ = etFeedUDP(s, false, etDatagram(1, easyTierMsgHolePunch, bytes.Repeat([]byte{0x7f}, 16)))
}
if etFlag(m, "yes") {
t.Fatal("hole punches settled the flow")
}
etExpect(t, m, "msg_type_name", "hole_punch")
}
func TestEasyTier_BothHolePunchBodyLengthsAreRecognised(t *testing.T) {
// 连通性路径 16 字节,UDP 会话层 32 字节;只有长度能把它们和噪声分开,两个都得列
for _, n := range []int{16, 32} {
m, _ := etFeedUDP(etUDP(), false, etDatagram(1, easyTierMsgHolePunch, bytes.Repeat([]byte{0x7f}, n)))
if got := etText(m, "msg_type_name"); got != "hole_punch" {
t.Fatalf("body length %d: msg_type_name = %q", n, got)
}
}
m, _ := etFeedUDP(etUDP(), false, etDatagram(1, easyTierMsgHolePunch, bytes.Repeat([]byte{0x7f}, 24)))
etExpect(t, m, "msg_type_name", "<missing>")
}
func TestEasyTier_DirectedHolePunchMustEchoTheDestinationPortInConnID(t *testing.T) {
punch := []byte{203, 0, 113, 9}
punch = binary.LittleEndian.AppendUint16(punch, 11010)
m, _ := etFeedUDP(etUDP(), false, etDatagram(11010, easyTierMsgV4HolePunch, punch))
etExpect(t, m, "msg_type_name", "v4_hole_punch")
m, done := etFeedUDP(etUDP(), false, etDatagram(11011, easyTierMsgV4HolePunch, punch))
etExpect(t, m, "msg_type_name", "<missing>")
if done {
t.Fatal("one bad datagram is not enough to give up")
}
}
func TestEasyTier_UDPLengthFieldMustMatchTheDatagram(t *testing.T) {
wrong := etSyn(1, 2)
wrong[6] = 9 // 声明 9,体是 8
m, _ := etFeedUDP(etUDP(), false, wrong)
etExpect(t, m, "msg_type_name", "<missing>")
}
func TestEasyTier_UDPPaddingByteMustBeZero(t *testing.T) {
wrong := etSyn(1, 2)
wrong[5] = 1
m, _ := etFeedUDP(etUDP(), false, wrong)
etExpect(t, m, "msg_type_name", "<missing>")
}
func TestEasyTier_FourUnparseableDatagramsEndAUDPFlow(t *testing.T) {
s := etUDP()
for i := 0; i < 3; i++ {
if _, done := etFeedUDP(s, false, []byte("not easytier at all")); done {
t.Fatalf("gave up after %d datagrams", i+1)
}
}
if _, done := etFeedUDP(s, false, []byte("not easytier at all")); !done {
t.Fatal("did not give up after four unparseable datagrams")
}
}
func TestEasyTier_TwoLengthFieldsOfATCPFrameMustAgree(t *testing.T) {
// 16 字节头后面声明载荷 4,实际到了 8
frame := etBodyRaw(1, 1, 2, 0, 1, 0, 4, bytes.Repeat([]byte{0xaa}, 8))
m, done := etFeedTCP(etTCP(), false, etSegment(frame))
if !done {
t.Fatal("stream not done on a length mismatch")
}
etExpect(t, m, "packet_type_name", "<missing>")
}
func TestEasyTier_AEADTailIsAllowedToOutrunTheDeclaredLength(t *testing.T) {
// 加密器追加 16 字节 tag 和 12 字节 nonce,不改头里声明的长度
payload := bytes.Repeat([]byte{0x11}, 40+easyTierAEADTailSize)
frame := etBodyRaw(1, 1, 2, easyTierFlagEncrypted, 1, 0, 40, payload)
m, _ := etFeedTCP(etTCP(), false, etSegment(frame))
if !etFlag(m, "encrypted") {
t.Fatal("encrypted not set")
}
etExpect(t, m, "payload_len", "40")
}
func TestEasyTier_CompressedPayloadMayComeInUnderTheDeclaredLength(t *testing.T) {
frame := etBodyRaw(1, 1, 2, easyTierFlagCompressed, 1, 0, 900, bytes.Repeat([]byte{0x22}, 61))
m, _ := etFeedTCP(etTCP(), false, etSegment(frame))
if !etFlag(m, "compressed") {
t.Fatal("compressed not set")
}
etExpect(t, m, "payload_len", "900")
}
func TestEasyTier_UnencryptedPayloadMustMatchTheDeclaredLengthExactly(t *testing.T) {
frame := etBodyRaw(1, 1, 2, 0, 1, 0, 40, bytes.Repeat([]byte{0x11}, 40+easyTierAEADTailSize))
if _, done := etFeedTCP(etTCP(), false, etSegment(frame)); !done {
t.Fatal("stream not done on an unencrypted length mismatch")
}
}
func TestEasyTier_ReservedByteIsZeroUnlessALivenessFlagIsSet(t *testing.T) {
withFlag := etBodyRaw(1, 1, 2, 0b0010_0000, 1, 0x5c, 4, make([]byte, 4))
m, _ := etFeedTCP(etTCP(), false, etSegment(withFlag))
etExpect(t, m, "packet_type_name", "data")
withoutFlag := etBodyRaw(1, 1, 2, 0, 1, 0x5c, 4, make([]byte, 4))
m, done := etFeedTCP(etTCP(), false, etSegment(withoutFlag))
if !done {
t.Fatal("stream not done on a non-zero reserved byte")
}
etExpect(t, m, "packet_type_name", "<missing>")
}
func TestEasyTier_ForwardCounterPastTheRoutersOwnCapIsRejected(t *testing.T) {
frame := etBodyRaw(1, 1, 2, 0, 9, 0, 4, make([]byte, 4))
m, done := etFeedTCP(etTCP(), false, etSegment(frame))
if !done {
t.Fatal("stream not done on forward counter 9")
}
etExpect(t, m, "packet_type_name", "<missing>")
}
func TestEasyTier_UnnamedPacketTypeIsRejected(t *testing.T) {
frame := etBodyRaw(22, 1, 2, 0, 1, 0, 4, make([]byte, 4))
if _, done := etFeedTCP(etTCP(), false, etSegment(frame)); !done {
t.Fatal("stream not done on packet type 22")
}
}
func TestEasyTier_FrameSplitAcrossSegmentsIsReassembled(t *testing.T) {
wire := etSegment(etBody(easyTierPktHandshake, 9, 0, etHandshakeRequest(9, "office-mesh")))
s := etTCP()
m, done := etFeedTCP(s, false, wire[:3])
if done {
t.Fatal("gave up on a length field that had not fully arrived")
}
etExpect(t, m, "packet_type_name", "<missing>")
m, done = etFeedTCP(s, false, wire[3:])
if done {
t.Fatal("stream done after a valid handshake")
}
if !etFlag(m, "yes") {
t.Fatal("yes not set after reassembly")
}
etExpect(t, m, "network_name", "office-mesh")
}
func TestEasyTier_ByteAtATimeDeliveryStillReadsTheFrame(t *testing.T) {
wire := etSegment(etBody(easyTierPktHandshake, 9, 0, etHandshakeRequest(9, "mesh")))
s := etTCP()
last := analyzer.PropMap{}
for _, b := range wire {
m, done := etFeedTCP(s, false, []byte{b})
if done {
t.Fatal("stream done mid-frame")
}
if len(m) != 0 {
last = m
}
}
if !etFlag(last, "yes") {
t.Fatal("yes not set after byte-at-a-time delivery")
}
etExpect(t, last, "network_name", "mesh")
}
func TestEasyTier_SeveralFramesPipelinedIntoOneSegmentAreAllRead(t *testing.T) {
wire := etSegment(etBody(1, 9, 8, bytes.Repeat([]byte{0x01}, 32)))
wire = append(wire, etSegment(etBody(4, 9, 8, nil))...)
wire = append(wire, etSegment(etBody(1, 9, 8, bytes.Repeat([]byte{0x03}, 64)))...)
m, done := etFeedTCP(etTCP(), false, wire)
if done {
t.Fatal("stream done after valid frames")
}
if !etFlag(m, "yes") {
t.Fatal("yes not set after three consistent frames")
}
etExpect(t, m, "tx_pkt_cnt", "3")
// map 描述的是最新一帧
etExpect(t, m, "payload_len", "64")
}
func TestEasyTier_NetworkNameSurvivesLaterFrames(t *testing.T) {
// 只发一次。按 mesh 名写的规则得在握手滚过去之后还能用
s := etTCP()
etFeedTCP(s, false, etSegment(etBody(easyTierPktHandshake, 9, 0, etHandshakeRequest(9, "office-mesh"))))
m, _ := etFeedTCP(s, false, etSegment(etBody(1, 9, 8, bytes.Repeat([]byte{0xee}, 128))))
etExpect(t, m, "network_name", "office-mesh")
etExpect(t, m, "packet_type_name", "data")
}
func TestEasyTier_GapInTheTCPStreamEndsIt(t *testing.T) {
u, done := etTCP().Feed(false, false, false, 7, []byte("whatever"))
if u != nil {
t.Fatal("update published on a gap")
}
if !done {
t.Fatal("stream not done on a gap")
}
}
func TestEasyTier_OrdinaryTrafficIsNotMistakenForATunnel(t *testing.T) {
m, done := etFeedTCP(etTCP(), false, []byte("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"))
if !done || etFlag(m, "yes") {
t.Fatalf("HTTP: done=%v yes=%v", done, etFlag(m, "yes"))
}
// TLS ClientHello 记录:16 03 01 <len> ... 读出来是 21 MB 的帧体长度,直接拒绝
m, done = etFeedTCP(etTCP(), false, []byte{0x16, 0x03, 0x01, 0x01, 0x4a, 0x01, 0x00, 0x01, 0x46})
if !done || etFlag(m, "yes") {
t.Fatalf("TLS: done=%v yes=%v", done, etFlag(m, "yes"))
}
}
func TestEasyTier_FETDoesNotCatchTheTCPHandshake(t *testing.T) {
// fet 只看客户端第一个分段,判完就 done。EasyTier 连接的第一个分段永远是明文握手,
// 整条连接上唯一低熵的东西,所以 fet 读作"非全加密",引擎放行,后面真正加密的帧再也不看。
// 而且能不能中取决于握手里碰巧有多少随机内容(ex1 落在 3.4 豁免线附近,随网络名长短摇摆),
// 一个随运营者起的名字长短而变的检测器不是检测器。
digest := make([]byte, 32)
for i := range digest {
digest[i] = byte(uint32(i) * 2654435761)
}
payload := etProtoVarint(1, easyTierHandshakeMagic)
payload = append(payload, etProtoVarint(2, 0x1a2b3c4d)...)
payload = append(payload, etProtoVarint(3, 1)...)
payload = append(payload, etProtoBytes(4, []byte("liveness-echo-v1"))...)
payload = append(payload, etProtoBytes(5, []byte("mesh"))...)
payload = append(payload, etProtoBytes(6, digest)...)
wire := etSegment(etBody(easyTierPktHandshake, 0x1a2b3c4d, 0, payload))
fet := (&tcp.FETAnalyzer{}).NewTCP(analyzer.TCPInfo{}, nil)
u, done := fet.Feed(false, true, false, 0, wire)
if !done {
t.Fatal("fet should decide on the first segment and not look again")
}
if u != nil && etFlag(u.M, "yes") {
t.Fatal("fet claimed this handshake, so this analyzer would be redundant")
}
// 同样的字节,给本分析器
m, _ := etFeedTCP(etTCP(), false, wire)
if !etFlag(m, "yes") {
t.Fatal("easytier did not claim its own handshake")
}
etExpect(t, m, "network_name", "mesh")
}
func TestEasyTier_WireGuardAnalyzerDoesNotClaimUDPTransport(t *testing.T) {
// wireguard 覆盖的是 EasyTier 的 wg:// 监听,六分之一。udp:// 上没有任何东西读起来像 WireGuard 报文
datagrams := [][]byte{
etSyn(0x12345678, 0x0102030405060708),
etDatagram(7, easyTierMsgData, etBody(easyTierPktHandshake, 0x1a2b3c4d, 0, etHandshakeRequest(9, "mesh"))),
etDatagram(1, easyTierMsgHolePunch, bytes.Repeat([]byte{0x7f}, 16)),
}
for i, data := range datagrams {
wg := (&WireGuardAnalyzer{}).NewUDP(analyzer.UDPInfo{}, nil)
if u, _ := wg.Feed(false, data); u != nil {
t.Fatalf("wireguard claimed easytier datagram %d: %v", i, u.M)
}
}
}
func TestEasyTier_STUNOnTheSameSocketIsNeitherEvidenceNorHeldAgainstTheFlow(t *testing.T) {
// EasyTier 把 STUN 复用在自己的 UDP socket 上,并在自己的帧解析之前分流;四个 STUN 不能结束流
stun := append([]byte{0x00, 0x01, 0x00, 0x00, 0x21, 0x12, 0xa4, 0x42}, bytes.Repeat([]byte{0x11}, 12)...)
s := etUDP()
for i := 0; i < 8; i++ {
m, done := etFeedUDP(s, false, stun)
if done {
t.Fatalf("STUN datagram %d ended the flow", i+1)
}
if len(m) != 0 {
t.Fatalf("STUN datagram %d published props: %v", i+1, m)
}
}
m, _ := etFeedUDP(s, false, etSyn(1, 2))
etExpect(t, m, "msg_type_name", "syn")
}