GPCM: Handle commands in a specific order

This commit is contained in:
mkwcat
2023-11-05 14:28:19 -05:00
parent a4f2c0802b
commit 257d4b7b89
4 changed files with 78 additions and 79 deletions

View File

@@ -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

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -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)
}