feat(tcp): add Minecraft analyzer for handshake detection
feat(tcp): implement FETAnalyzer with segment buffering feat(tcp): enhance TCP engine with window scale detection test(tcp): add tests for Minecraft and FET analyzers docs: add documentation for Minecraft analyzer and TCP evasion
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/apernet/OpenGFW/analyzer"
|
||||
)
|
||||
|
||||
func TestMinecraftParsing_HandshakeLogin(t *testing.T) {
|
||||
handshake := buildMinecraftHandshake(t, 767, "211.136.162.178", 25565, minecraftNextStateLogin)
|
||||
want := analyzer.PropMap{
|
||||
"yes": true,
|
||||
"candidate": false,
|
||||
"protocol": 767,
|
||||
"server_addr": "211.136.162.178",
|
||||
"server_port": uint16(25565),
|
||||
"next_state": minecraftNextStateLogin,
|
||||
"next_state_name": "login",
|
||||
"packet_length": len(handshake) - 1,
|
||||
}
|
||||
|
||||
u, done := newMinecraftStream(nil).Feed(false, false, false, 0, handshake)
|
||||
if !done {
|
||||
t.Fatal("stream not done after full handshake")
|
||||
}
|
||||
if !reflect.DeepEqual(u.M, want) {
|
||||
t.Errorf("parsed = %#v, want %#v", u.M, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinecraftParsing_FragmentedHandshake(t *testing.T) {
|
||||
handshake := buildMinecraftHandshake(t, 767, "mc.example.com", 25565, minecraftNextStateStatus)
|
||||
s := newMinecraftStream(nil)
|
||||
|
||||
u, done := s.Feed(false, false, false, 0, handshake[:2])
|
||||
if done {
|
||||
t.Fatal("stream done on partial handshake")
|
||||
}
|
||||
if got := u.M.Get("candidate"); got != true {
|
||||
t.Fatalf("candidate = %v, want true", got)
|
||||
}
|
||||
|
||||
u, done = s.Feed(false, false, false, 0, handshake[2:])
|
||||
if !done {
|
||||
t.Fatal("stream not done after remaining handshake")
|
||||
}
|
||||
if got := u.M.Get("yes"); got != true {
|
||||
t.Fatalf("yes = %v, want true", got)
|
||||
}
|
||||
if got := u.M.Get("server_addr"); got != "mc.example.com" {
|
||||
t.Fatalf("server_addr = %v, want mc.example.com", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinecraftParsing_InvalidAfterCandidate(t *testing.T) {
|
||||
s := newMinecraftStream(nil)
|
||||
u, done := s.Feed(false, false, false, 0, []byte{0x04})
|
||||
if done {
|
||||
t.Fatal("stream done on partial packet")
|
||||
}
|
||||
if got := u.M.Get("candidate"); got != true {
|
||||
t.Fatalf("candidate = %v, want true", got)
|
||||
}
|
||||
|
||||
u, done = s.Feed(false, false, false, 0, []byte{0x01, 0x02, 0x03, 0x04})
|
||||
if !done {
|
||||
t.Fatal("stream not done on invalid packet")
|
||||
}
|
||||
if got := u.M.Get("candidate"); got != false {
|
||||
t.Fatalf("candidate = %v, want false", got)
|
||||
}
|
||||
if got := u.M.Get("yes"); got != false {
|
||||
t.Fatalf("yes = %v, want false", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMinecraftParsing_RejectsInvalidUTF8Address(t *testing.T) {
|
||||
packet := []byte{0x00}
|
||||
packet = append(packet, encodeMinecraftVarInt(767)...)
|
||||
packet = append(packet, 0x01, 0xff)
|
||||
packet = append(packet, 0x63, 0xdd)
|
||||
packet = append(packet, encodeMinecraftVarInt(minecraftNextStateLogin)...)
|
||||
data := append(encodeMinecraftVarInt(int32(len(packet))), packet...)
|
||||
|
||||
u, done := newMinecraftStream(nil).Feed(false, false, false, 0, data)
|
||||
if !done {
|
||||
t.Fatal("stream not done on invalid UTF-8 address")
|
||||
}
|
||||
if got := u.M.Get("yes"); got != false {
|
||||
t.Fatalf("yes = %v, want false", got)
|
||||
}
|
||||
}
|
||||
|
||||
func buildMinecraftHandshake(t *testing.T, protocol int32, addr string, port uint16, nextState int32) []byte {
|
||||
t.Helper()
|
||||
packet := []byte{0x00}
|
||||
packet = append(packet, encodeMinecraftVarInt(protocol)...)
|
||||
packet = append(packet, encodeMinecraftVarInt(int32(len(addr)))...)
|
||||
packet = append(packet, []byte(addr)...)
|
||||
packet = append(packet, byte(port>>8), byte(port))
|
||||
packet = append(packet, encodeMinecraftVarInt(nextState)...)
|
||||
return append(encodeMinecraftVarInt(int32(len(packet))), packet...)
|
||||
}
|
||||
|
||||
func encodeMinecraftVarInt(value int32) []byte {
|
||||
var out []byte
|
||||
for {
|
||||
b := byte(value & 0x7f)
|
||||
value = int32(uint32(value) >> 7)
|
||||
if value != 0 {
|
||||
b |= 0x80
|
||||
}
|
||||
out = append(out, b)
|
||||
if value == 0 {
|
||||
return out
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user