package logingate import ( "crypto/aes" "crypto/cipher" "net" "testing" "time" "ananta-ps/internal/config" "ananta-ps/pkg/crypto/chacha8" "ananta-ps/pkg/protocol" ) func TestLoginGateServer(t *testing.T) { cfg := &config.Config{ Client: config.ClientConfig{ ServerID: 801, Aid: 5944, }, Network: config.NetworkConfig{ BindHost: "127.0.0.1", AdvertisedHost: "127.0.0.1", LoginPorts: []int{15200}, GamePort: 15202, }, Player: config.PlayerConfig{ Pid: 100, AccountId: "test_acc", UserName: "test_user", }, } srv := New(cfg) if err := srv.Start(); err != nil { t.Fatalf("failed to start login gate: %v", err) } defer srv.Stop() // Connect client conn, err := net.DialTimeout("tcp", "127.0.0.1:15200", 2*time.Second) if err != nil { t.Fatalf("failed to connect: %v", err) } defer conn.Close() // 1. Send client handshake (mode 0x01 plaintext) clientHandshake := make([]byte, 260) clientHandshake[0] = 0xAA clientHandshake[1] = 0xBB clientHandshake[2] = 0xCC clientHandshake[3] = 0xDD aesKey := []byte("0123456789abcdef") // 16 bytes copy(clientHandshake[4:20], aesKey) if err := protocol.WriteFrame(conn, 0x01, clientHandshake, nil); err != nil { t.Fatalf("failed to write client handshake: %v", err) } // 2. Read server handshake reply serverReplyFrame, err := protocol.ReadFrame(conn, nil) if err != nil { t.Fatalf("failed to read server handshake reply: %v", err) } if len(serverReplyFrame.Payload) < 112 { t.Fatalf("server reply too short: %d", len(serverReplyFrame.Payload)) } aesNonce := serverReplyFrame.Payload[8:20] encryptedHead := serverReplyFrame.Payload[20:96] tag := serverReplyFrame.Payload[96:112] cipherBlock, err := aes.NewCipher(aesKey) if err != nil { t.Fatalf("failed to create aes cipher: %v", err) } aesGcm, err := cipher.NewGCM(cipherBlock) if err != nil { t.Fatalf("failed to create aes gcm: %v", err) } fullCipher := append(encryptedHead, tag...) chaChaHead, err := aesGcm.Open(nil, aesNonce, fullCipher, nil) if err != nil { t.Fatalf("failed to decrypt chacha head: %v", err) } clientWriteKey := chaChaHead[0:32] clientReadKey := chaChaHead[32:64] streamNonce := chaChaHead[64:76] clientSendCipher, err := chacha8.New(clientWriteKey, streamNonce) if err != nil { t.Fatalf("failed to create client send cipher: %v", err) } clientReadCipher, err := chacha8.New(clientReadKey, streamNonce) if err != nil { t.Fatalf("failed to create client read cipher: %v", err) } // 3. Send CheckVersion Invoke invokePayload := protocol.BuildInvoke(protocol.MethodCheckVersion, 1, nil) if err := protocol.WriteFrame(conn, 0x09, invokePayload, clientSendCipher); err != nil { t.Fatalf("failed to write invoke frame: %v", err) } // 4. Read Return frame respFrame, err := protocol.ReadFrame(conn, clientReadCipher) if err != nil { t.Fatalf("failed to read response frame: %v", err) } pkt := protocol.ParseRpcPacket(respFrame.Payload) if pkt.Kind != protocol.PacketKindReturn { t.Fatalf("expected Return packet, got %v", pkt.Kind) } if pkt.InvokeID != 1 { t.Fatalf("expected InvokeID 1, got %d", pkt.InvokeID) } if pkt.Error != 0 { t.Fatalf("expected Error 0, got %d", pkt.Error) } }