Add more verbose logging

This commit is contained in:
TheLordScruffy
2023-09-17 21:28:34 -04:00
parent 26617d96f3
commit 326b5f7227
8 changed files with 165 additions and 39 deletions

32
common/friend_code.go Normal file
View File

@@ -0,0 +1,32 @@
package common
import (
"crypto/md5"
"encoding/binary"
"fmt"
)
func CalcFriendCode(pid uint32, gameId string) uint64 {
if pid == 0 {
return 0
}
buffer := make([]byte, 8)
binary.LittleEndian.PutUint32(buffer, pid)
buffer[4] = gameId[3]
buffer[5] = gameId[2]
buffer[6] = gameId[1]
buffer[7] = gameId[0]
digest := md5.Sum(buffer)
return uint64(pid) | (uint64(digest[0]&0xfe) << 31)
}
func CalcFriendCodeString(pid uint32, gameId string) string {
return GetFriendCodeString(CalcFriendCode(pid, gameId))
}
func GetFriendCodeString(fc uint64) string {
s := fmt.Sprintf("%012d", fc)
return s[len(s)-12:len(s)-8] + "-" + s[len(s)-8:len(s)-4] + "-" + s[len(s)-4:]
}

View File

@@ -2,8 +2,12 @@ package database
import (
"context"
"github.com/jackc/pgx/v4/pgxpool"
"database/sql"
"wwfc/common"
"wwfc/logging"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
)
const (
@@ -12,9 +16,29 @@ const (
UpdateUserLogin = `UPDATE logins SET auth_token = $1 WHERE user_id = $2`
InsertUserLogin = `INSERT INTO logins (auth_token, user_id, gsbrcd) VALUES ($1, $2, $3)`
GetNASUserLogin = `SELECT user_id, gsbrcd FROM logins WHERE auth_token = $1 LIMIT 1`
GetUserAuthToken = `SELECT auth_token FROM logins WHERE user_id = $1`
)
func GenerateAuthToken(pool *pgxpool.Pool, ctx context.Context, userId int, gsbrcd string) string {
var exists bool
err := pool.QueryRow(ctx, DoesNASUserExist, userId).Scan(&exists)
if err != nil {
panic(err)
}
if exists {
// Temporary(?) workaround for multiple sessions with the same user ID (i.e. multiple Dolphin instances).
// Just don't change the user's auth token... ever.
// TODO: What do we actually do here? Do we even care about proper authentication at this stage?
var authToken string
err := pool.QueryRow(ctx, GetUserAuthToken, userId).Scan(&authToken)
if err != nil {
panic(err)
}
return authToken
}
authToken := "NDS" + common.RandomString(80)
for {
// We must make sure that the auth token doesn't exist before attempting to insert it into the database.
@@ -31,12 +55,6 @@ func GenerateAuthToken(pool *pgxpool.Pool, ctx context.Context, userId int, gsbr
authToken = "NDS" + common.RandomString(80)
}
var exists bool
err := pool.QueryRow(ctx, DoesNASUserExist, userId).Scan(&exists)
if err != nil {
panic(err)
}
if exists {
// UPDATE rather than INSERT
_, err = pool.Exec(ctx, UpdateUserLogin, authToken, userId)
@@ -58,15 +76,23 @@ func GetNASLogin(pool *pgxpool.Pool, ctx context.Context, authToken string) (int
var gsbrcd string
err := pool.QueryRow(ctx, GetNASUserLogin, authToken).Scan(&userId, &gsbrcd)
if err != nil {
panic(err)
if err == sql.ErrNoRows {
return 0, ""
} else {
panic(err)
}
}
return userId, gsbrcd
}
func LoginUserToGCPM(pool *pgxpool.Pool, ctx context.Context, authToken string) User {
func LoginUserToGPCM(pool *pgxpool.Pool, ctx context.Context, authToken string) (User, bool) {
// Query login table with the auth token.
userId, gsbrcd := GetNASLogin(pool, ctx, authToken)
if userId == 0 {
logging.Notice("DATABASE", "Invalid auth token:", aurora.Cyan(authToken).String())
return User{}, false
}
var exists bool
err := pool.QueryRow(ctx, DoesUserExist, userId, gsbrcd).Scan(&exists)
@@ -88,11 +114,11 @@ func LoginUserToGCPM(pool *pgxpool.Pool, ctx context.Context, authToken string)
// Create the GPCM account
user.CreateUser(pool, ctx)
} else {
err := pool.QueryRow(ctx, GetUserProfileID, userId).Scan(&user.ProfileId)
err := pool.QueryRow(ctx, GetUserProfileID, userId, gsbrcd).Scan(&user.ProfileId)
if err != nil {
panic(err)
}
}
return user
return user, true
}

View File

@@ -3,9 +3,10 @@ package database
import (
"context"
"errors"
"wwfc/common"
"github.com/jackc/pgx/v4"
"github.com/jackc/pgx/v4/pgxpool"
"wwfc/common"
)
const (
@@ -15,7 +16,7 @@ const (
CreateUserSession = `INSERT INTO sessions (session_key, profile_id, login_ticket) VALUES ($1, $2, $3)`
DoesUserExist = `SELECT EXISTS(SELECT 1 FROM users WHERE user_id = $1 AND gsbrcd = $2)`
DeleteUserSession = `DELETE FROM sessions WHERE profile_id = $1`
GetUserProfileID = `SELECT profile_id FROM users WHERE user_id = $1`
GetUserProfileID = `SELECT profile_id FROM users WHERE user_id = $1 AND gsbrcd = $2`
)
type User struct {

View File

@@ -5,12 +5,13 @@ import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
"github.com/jackc/pgx/v4/pgxpool"
"log"
"strconv"
"strings"
"wwfc/common"
"wwfc/database"
"github.com/jackc/pgx/v4/pgxpool"
)
func generateResponse(gpcmChallenge, nasChallenge, authToken, clientChallenge string) string {
@@ -43,9 +44,10 @@ func login(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyComman
proof := generateProof(challenge, "0qUekMb4", command.OtherValues["authtoken"], command.OtherValues["challenge"])
// Perform the login with the database.
user := database.LoginUserToGCPM(pool, ctx, authToken)
// TODO: Check valid result
user, _ := database.LoginUserToGPCM(pool, ctx, authToken)
loginTicket := strings.Replace(base64.StdEncoding.EncodeToString([]byte(common.RandomString(16))), "=", "_", -1)
// TODO: REMOVE!!!!!
// TODO: Remove in favour of proper thread safe holding
userId = user.UserId
// Now initiate the session
_ = database.CreateSession(pool, ctx, user.ProfileId, loginTicket)

View File

@@ -5,8 +5,6 @@ import (
"context"
"errors"
"fmt"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
"io"
"log"
"net"
@@ -14,6 +12,9 @@ import (
"time"
"wwfc/common"
"wwfc/logging"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
)
var (
@@ -66,16 +67,15 @@ func handleRequest(conn net.Conn) {
err := conn.(*net.TCPConn).SetKeepAlive(true)
if err != nil {
fmt.Printf("Unable to set keepalive - %s", err)
logging.Notice("GCSP", "Unable to set keepalive:", err.Error())
}
err = conn.(*net.TCPConn).SetKeepAlivePeriod(time.Hour * 1000)
if err != nil {
fmt.Printf("Unable to set keepalive - %s", err)
logging.Notice("GCSP", "Unable to set keepalive:", err.Error())
}
// log.Printf("%s: Connection established from %s. Sending challenge.", aurora.Green("[NOTICE]"), aurora.Yellow(conn.RemoteAddr()))
// conn.Write([]byte(fmt.Sprintf(`\lc\1\challenge\%s\id\1\final\`, challenge)))
logging.Notice("GCSP", "Connection established from", conn.RemoteAddr().String())
// Here we go into the listening loop
for {
@@ -94,7 +94,7 @@ func handleRequest(conn net.Conn) {
}
for _, command := range commands {
logging.Notice("GCSP", "Message received. Command:", aurora.Yellow(command.Command).String())
logging.Notice("GCSP", "Command:", aurora.Yellow(command.Command).String())
switch command.Command {
case "ka":
conn.Write([]byte(`\ka\\final\`))

View File

@@ -5,12 +5,13 @@ import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
"github.com/jackc/pgx/v4/pgxpool"
"log"
"strconv"
"strings"
"wwfc/common"
"wwfc/database"
"github.com/jackc/pgx/v4/pgxpool"
)
func generateResponse(gpcmChallenge, nasChallenge, authToken, clientChallenge string) string {
@@ -43,7 +44,8 @@ func login(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyComman
proof := generateProof(challenge, "0qUekMb4", command.OtherValues["authtoken"], command.OtherValues["challenge"])
// Perform the login with the database.
user := database.LoginUserToGCPM(pool, ctx, authToken)
// TODO: Check valid result
user, _ := database.LoginUserToGPCM(pool, ctx, authToken)
loginTicket := strings.Replace(base64.StdEncoding.EncodeToString([]byte(common.RandomString(16))), "=", "_", -1)
// TODO: Remove in favour of proper thread safe holding
userId = user.UserId

View File

@@ -5,14 +5,16 @@ import (
"context"
"errors"
"fmt"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
"io"
"log"
"net"
"os"
"time"
"wwfc/common"
"wwfc/logging"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
)
var (
@@ -68,17 +70,20 @@ func handleRequest(conn net.Conn) {
err := conn.(*net.TCPConn).SetKeepAlive(true)
if err != nil {
fmt.Printf("Unable to set keepalive - %s", err)
logging.Notice("GPCM", "Unable to set keepalive:", err.Error())
}
err = conn.(*net.TCPConn).SetKeepAlivePeriod(time.Hour * 1000)
if err != nil {
fmt.Printf("Unable to set keepalive - %s", err)
logging.Notice("GPCM", "Unable to set keepalive:", err.Error())
}
log.Printf("%s: Connection established from %s. Sending challenge.", aurora.Green("[NOTICE]"), aurora.Yellow(conn.RemoteAddr()))
conn.Write([]byte(fmt.Sprintf(`\lc\1\challenge\%s\id\1\final\`, challenge)))
logging.Notice("GPCM", "Connection established from", conn.RemoteAddr().String())
loggedIn := false
// Here we go into the listening loop
for {
buffer := make([]byte, 1024)
@@ -92,20 +97,60 @@ func handleRequest(conn net.Conn) {
commands, err := common.ParseGameSpyMessage(string(buffer))
if err != nil {
log.Fatal(err)
logging.Notice("GPCM", "Error parsing message:", err.Error())
logging.Notice("GPCM", "Raw data:", string(buffer))
return
}
for _, command := range commands {
log.Printf("%s: Message received. Command: %s", aurora.Green("[NOTICE]"), aurora.Yellow(command.Command))
logging.Notice("GPCM", "Command:", aurora.Yellow(command.Command).String())
if loggedIn == false {
if command.Command != "login" {
logging.Notice("GPCM", "Attempt to run command before login!!!")
return
}
payload := login(pool, ctx, command, challenge)
if userId != 0 {
loggedIn = true
}
conn.Write([]byte(payload))
}
}
// Make sure update profile runs before get profile
// 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":
// Nothing is written here.
updateProfile(pool, ctx, command)
break
case "status":
logging.Notice("GPCM", "statstring:", aurora.Cyan(command.OtherValues["statstring"]).String())
if command.OtherValues["locstring"] == "" {
logging.Notice("GPCM", "locstring: (empty)")
} else {
logging.Notice("GPCM", "locstring:", aurora.Cyan(command.OtherValues["locstring"]).String())
}
break
case "addbuddy":
addFriend(pool, ctx, command)
break
case "delbuddy":
removeFriend(pool, ctx, command)
break
}
}
@@ -114,10 +159,7 @@ func handleRequest(conn net.Conn) {
case "ka":
conn.Write([]byte(`\ka\\final\`))
break
case "login":
payload := login(pool, ctx, command, challenge)
conn.Write([]byte(payload))
break
case "getprofile":
payload := getProfile(pool, ctx, command)
conn.Write([]byte(payload))

View File

@@ -2,10 +2,13 @@ package gpcm
import (
"context"
"github.com/jackc/pgx/v4/pgxpool"
"strconv"
"wwfc/common"
"wwfc/database"
"wwfc/logging"
"github.com/jackc/pgx/v4/pgxpool"
"github.com/logrusorgru/aurora/v3"
)
func getProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) string {
@@ -50,6 +53,24 @@ func updateProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameS
database.UpdateUser(pool, ctx, firstName, lastName, userId)
}
func addFriend(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) {
profileid := command.OtherValues["newprofileid"]
profileid_int, err := strconv.ParseUint(profileid, 10, 32)
if err != nil {
logging.Notice("GPCM", "Error parsing profileid:", err.Error())
return
}
fc := common.CalcFriendCodeString(uint32(profileid_int), "RMCJ")
logging.Notice("GPCM", "Add friend:", aurora.Cyan(profileid).String(), aurora.Cyan(fc).String())
// TODO
}
func removeFriend(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) {
// TODO
}
func createStatus() string {
return common.CreateGameSpyMessage(common.GameSpyCommand{
Command: "bm",