From 257d4b7b893e246901bdc9673ece87ee2b6794c2 Mon Sep 17 00:00:00 2001 From: mkwcat Date: Sun, 5 Nov 2023 14:28:19 -0500 Subject: [PATCH] GPCM: Handle commands in a specific order --- gpcm/friend.go | 12 ++--- gpcm/login.go | 14 +++--- gpcm/main.go | 118 ++++++++++++++++++++++++------------------------ gpcm/profile.go | 13 +++--- 4 files changed, 78 insertions(+), 79 deletions(-) diff --git a/gpcm/friend.go b/gpcm/friend.go index 65751f5..ce31e9e 100644 --- a/gpcm/friend.go +++ b/gpcm/friend.go @@ -1,8 +1,6 @@ package gpcm import ( - "context" - "github.com/jackc/pgx/v4/pgxpool" "github.com/logrusorgru/aurora/v3" "strconv" "strings" @@ -19,7 +17,7 @@ func (g *GameSpySession) isFriendAdded(profileId uint32) bool { return false } -func (g *GameSpySession) addFriend(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) { +func (g *GameSpySession) addFriend(command common.GameSpyCommand) { strNewProfileId := command.OtherValues["newprofileid"] newProfileId, err := strconv.ParseUint(strNewProfileId, 10, 32) if err != nil { @@ -56,11 +54,11 @@ func (g *GameSpySession) addFriend(pool *pgxpool.Pool, ctx context.Context, comm sendMessageToProfileId("2", g.User.ProfileId, uint32(newProfileId), "\r\n\r\n|signed|"+common.RandomHexString(32)) } -func (g *GameSpySession) removeFriend(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) { +func (g *GameSpySession) removeFriend(command common.GameSpyCommand) { // TODO } -func (g *GameSpySession) authAddFriend(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) { +func (g *GameSpySession) authAddFriend(command common.GameSpyCommand) { strFromProfileId := command.OtherValues["fromprofileid"] fromProfileId, err := strconv.ParseUint(strFromProfileId, 10, 32) if err != nil { @@ -77,7 +75,7 @@ func (g *GameSpySession) authAddFriend(pool *pgxpool.Pool, ctx context.Context, g.exchangeFriendStatus(uint32(fromProfileId)) } -func (g *GameSpySession) setStatus(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) { +func (g *GameSpySession) setStatus(command common.GameSpyCommand) { status := command.CommandValue statstring, ok := command.OtherValues["statstring"] @@ -119,7 +117,7 @@ func (g *GameSpySession) setStatus(pool *pgxpool.Pool, ctx context.Context, comm mutex.Unlock() } -func (g *GameSpySession) bestieMessage(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) { +func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) { if command.CommandValue != "1" { logging.Notice(g.ModuleName, "Received unknown bestie message type:", aurora.Cyan(command.CommandValue)) return diff --git a/gpcm/login.go b/gpcm/login.go index 349c32a..d970149 100644 --- a/gpcm/login.go +++ b/gpcm/login.go @@ -1,11 +1,9 @@ package gpcm import ( - "context" "crypto/md5" "encoding/base64" "encoding/hex" - "github.com/jackc/pgx/v4/pgxpool" "log" "strconv" "strings" @@ -32,20 +30,20 @@ func generateProof(gpcmChallenge, nasChallenge, authToken, clientChallenge strin return generateResponse(clientChallenge, nasChallenge, authToken, gpcmChallenge) } -func (g *GameSpySession) Login(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand, challenge string) (string, bool) { +func (g *GameSpySession) login(command common.GameSpyCommand) { if g.LoggedIn { log.Fatalf("Attempt to login twice") } // TODO: Validate login token with one in database authToken := command.OtherValues["authtoken"] - response := generateResponse(challenge, "0qUekMb4", authToken, command.OtherValues["challenge"]) + response := generateResponse(g.Challenge, "0qUekMb4", authToken, command.OtherValues["challenge"]) if response != command.OtherValues["response"] { // TODO: Return an error log.Fatalf("response mismatch") } - proof := generateProof(challenge, "0qUekMb4", command.OtherValues["authtoken"], command.OtherValues["challenge"]) + proof := generateProof(g.Challenge, "0qUekMb4", command.OtherValues["authtoken"], command.OtherValues["challenge"]) // Perform the login with the database. // TODO: Check valid result @@ -76,7 +74,7 @@ func (g *GameSpySession) Login(pool *pgxpool.Pool, ctx context.Context, command g.ModuleName += ":" + strconv.FormatInt(int64(g.User.ProfileId), 10) g.ModuleName += "/" + common.CalcFriendCodeString(g.User.ProfileId, "RMCJ") - return common.CreateGameSpyMessage(common.GameSpyCommand{ + payload := common.CreateGameSpyMessage(common.GameSpyCommand{ Command: "lc", CommandValue: "2", OtherValues: map[string]string{ @@ -88,7 +86,9 @@ func (g *GameSpySession) Login(pool *pgxpool.Pool, ctx context.Context, command "lt": loginTicket, "id": command.OtherValues["id"], }, - }), true + }) + + g.Conn.Write([]byte(payload)) } func IsLoggedIn(profileID uint32) bool { diff --git a/gpcm/main.go b/gpcm/main.go index 42f4f7f..3a500b9 100644 --- a/gpcm/main.go +++ b/gpcm/main.go @@ -21,6 +21,7 @@ type GameSpySession struct { User database.User ModuleName string LoggedIn bool + Challenge string Status string LocString string FriendList []uint32 @@ -93,14 +94,16 @@ func handleRequest(conn net.Conn) { User: database.User{}, ModuleName: "GPCM", LoggedIn: false, + Challenge: "", Status: "", + LocString: "", FriendList: []uint32{}, } defer session.closeSession() // Set session ID and challenge - challenge := common.RandomString(10) + session.Challenge = common.RandomString(10) err := conn.(*net.TCPConn).SetKeepAlive(true) if err != nil { @@ -112,7 +115,15 @@ func handleRequest(conn net.Conn) { logging.Notice(session.ModuleName, "Unable to set keepalive (2):", err.Error()) } - conn.Write([]byte(fmt.Sprintf(`\lc\1\challenge\%s\id\1\final\`, challenge))) + payload := common.CreateGameSpyMessage(common.GameSpyCommand{ + Command: "lc", + CommandValue: "1", + OtherValues: map[string]string{ + "challenge": session.Challenge, + "id": "1", + }, + }) + conn.Write([]byte(payload)) logging.Notice(session.ModuleName, "Connection established from", conn.RemoteAddr()) @@ -127,79 +138,68 @@ func handleRequest(conn net.Conn) { return } - logging.Notice(session.ModuleName, "Connection lost") + logging.Error(session.ModuleName, "Connection lost") return } commands, err := common.ParseGameSpyMessage(string(buffer)) if err != nil { - logging.Notice(session.ModuleName, "Error parsing message:", err.Error()) - logging.Notice(session.ModuleName, "Raw data:", string(buffer)) + logging.Error(session.ModuleName, "Error parsing message:", err.Error()) + logging.Error(session.ModuleName, "Raw data:", string(buffer)) return } - for _, command := range commands { - logging.Notice(session.ModuleName, "Command:", aurora.Yellow(command.Command)) + // Commands must be handled in a certain order, not in the order supplied by the client - if command.Command == "login" { - payload, _ := session.Login(pool, ctx, command, challenge) - conn.Write([]byte(payload)) - } - } + commands = session.handleCommand("ka", commands, func(command common.GameSpyCommand) { + session.Conn.Write([]byte(`\ka\\final\`)) + }) + commands = session.handleCommand("login", commands, session.login) + commands = session.ignoreCommand("logout", commands) if session.LoggedIn == false { - logging.Notice(session.ModuleName, "Attempt to run command before login!") + logging.Error(session.ModuleName, "Attempt to run command before login!") return } - // Make sure commands that update the profile run before getprofile - for _, command := range commands { - switch command.Command { - case "login": - // User should already be authenticated - break - - case "logout": - // Bye - return - - case "updatepro": - session.updateProfile(pool, ctx, command) - break - - case "status": - session.setStatus(pool, ctx, command) - break - - case "addbuddy": - session.addFriend(pool, ctx, command) - break - - case "delbuddy": - session.removeFriend(pool, ctx, command) - break - - case "bm": - session.bestieMessage(pool, ctx, command) - break - - case "authadd": - session.authAddFriend(pool, ctx, command) - break - } - } + commands = session.handleCommand("updatepro", commands, session.updateProfile) + commands = session.handleCommand("status", commands, session.setStatus) + commands = session.handleCommand("addbuddy", commands, session.addFriend) + commands = session.handleCommand("delbuddy", commands, session.removeFriend) + commands = session.handleCommand("authadd", commands, session.authAddFriend) + commands = session.handleCommand("bm", commands, session.bestieMessage) + commands = session.handleCommand("getprofile", commands, session.getProfile) for _, command := range commands { - switch command.Command { - case "ka": - conn.Write([]byte(`\ka\\final\`)) - break - - case "getprofile": - payload := session.getProfile(pool, ctx, command) - conn.Write([]byte(payload)) - break - } + logging.Error(session.ModuleName, "Unknown command:", aurora.Cyan(command.Command)) } } } + +func (g *GameSpySession) handleCommand(name string, commands []common.GameSpyCommand, handler func(command common.GameSpyCommand)) []common.GameSpyCommand { + unhandled := []common.GameSpyCommand{} + + for _, command := range commands { + if command.Command != name { + unhandled = append(unhandled, command) + continue + } + + logging.Notice(g.ModuleName, "Command:", aurora.Yellow(command.Command)) + handler(command) + } + + return unhandled +} + +func (g *GameSpySession) ignoreCommand(name string, commands []common.GameSpyCommand) []common.GameSpyCommand { + unhandled := []common.GameSpyCommand{} + + for _, command := range commands { + if command.Command != name { + unhandled = append(unhandled, command) + } + } + + return unhandled +} diff --git a/gpcm/profile.go b/gpcm/profile.go index 8627965..4ed0c80 100644 --- a/gpcm/profile.go +++ b/gpcm/profile.go @@ -1,8 +1,6 @@ package gpcm import ( - "context" - "github.com/jackc/pgx/v4/pgxpool" "github.com/logrusorgru/aurora/v3" "strconv" "wwfc/common" @@ -10,11 +8,12 @@ import ( "wwfc/logging" ) -func (g *GameSpySession) getProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) string { +func (g *GameSpySession) getProfile(command common.GameSpyCommand) { strProfileId := command.OtherValues["profileid"] profileId, err := strconv.ParseUint(strProfileId, 10, 32) if err != nil { - return createGameSpyError(2560) + g.replyError(2560) + return } logging.Notice(g.ModuleName, "Looking up the profile of", aurora.Cyan(profileId).String()) @@ -32,7 +31,7 @@ func (g *GameSpySession) getProfile(pool *pgxpool.Pool, ctx context.Context, com user, _ = database.GetProfile(pool, ctx, uint32(profileId)) } - return common.CreateGameSpyMessage(common.GameSpyCommand{ + response := common.CreateGameSpyMessage(common.GameSpyCommand{ Command: "pi", CommandValue: "", OtherValues: map[string]string{ @@ -51,8 +50,10 @@ func (g *GameSpySession) getProfile(pool *pgxpool.Pool, ctx context.Context, com "id": command.OtherValues["id"], }, }) + + g.Conn.Write([]byte(response)) } -func (g *GameSpySession) updateProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) { +func (g *GameSpySession) updateProfile(command common.GameSpyCommand) { g.User = database.UpdateProfile(pool, ctx, g.User.ProfileId, command.OtherValues) }