125 lines
3.2 KiB
Go
125 lines
3.2 KiB
Go
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)
|
|
}
|
|
}
|