diff --git a/common/encoding.go b/common/encoding.go index e42e549..7a3a43b 100644 --- a/common/encoding.go +++ b/common/encoding.go @@ -2,7 +2,7 @@ package common import "fmt" -func Base32Encode(value int) string { +func Base32Encode(value int64) string { alpha := "0123456789abcdefghijklmnopqrstuv" encoded := "" diff --git a/database/login.go b/database/login.go index a3b583f..fb6022b 100644 --- a/database/login.go +++ b/database/login.go @@ -19,7 +19,7 @@ const ( GetUserAuthToken = `SELECT auth_token FROM logins WHERE user_id = $1 AND gsbrcd = $2` ) -func GenerateAuthToken(pool *pgxpool.Pool, ctx context.Context, userId int, gsbrcd string) string { +func GenerateAuthToken(pool *pgxpool.Pool, ctx context.Context, userId int64, gsbrcd string) string { var authToken string err := pool.QueryRow(ctx, GetUserAuthToken, userId, gsbrcd).Scan(&authToken) @@ -63,8 +63,8 @@ func GenerateAuthToken(pool *pgxpool.Pool, ctx context.Context, userId int, gsbr return authToken } -func GetNASLogin(pool *pgxpool.Pool, ctx context.Context, authToken string) (int, string) { - var userId int +func GetNASLogin(pool *pgxpool.Pool, ctx context.Context, authToken string) (int64, string) { + var userId int64 var gsbrcd string err := pool.QueryRow(ctx, GetNASUserLogin, authToken).Scan(&userId, &gsbrcd) if err != nil { diff --git a/database/user.go b/database/user.go index af234a4..5ea4c3a 100644 --- a/database/user.go +++ b/database/user.go @@ -3,6 +3,7 @@ package database import ( "context" "errors" + "math/rand" "wwfc/common" "github.com/jackc/pgx/v4" @@ -20,8 +21,8 @@ const ( ) type User struct { - ProfileId int - UserId int + ProfileId int64 + UserId int64 GsbrCode string Password string Email string @@ -37,7 +38,12 @@ func (user *User) CreateUser(pool *pgxpool.Pool, ctx context.Context) { } } -func UpdateUser(pool *pgxpool.Pool, ctx context.Context, firstName string, lastName string, userId int) User { +func GetUniqueUserID() int64 { + // Not guaranteed unique but doesn't matter in practice if multiple people have the same user ID. + return rand.Int63n(100000000000000) +} + +func UpdateUser(pool *pgxpool.Pool, ctx context.Context, firstName string, lastName string, userId int64) User { user := User{} _, err := pool.Exec(ctx, UpdateUserTable, firstName, lastName, userId) if err != nil { @@ -47,7 +53,7 @@ func UpdateUser(pool *pgxpool.Pool, ctx context.Context, firstName string, lastN return user } -func CreateSession(pool *pgxpool.Pool, ctx context.Context, profileId int, loginTicket string) string { +func CreateSession(pool *pgxpool.Pool, ctx context.Context, profileId int64, loginTicket string) string { // Delete session first. deleteSession(pool, ctx, profileId) @@ -60,14 +66,14 @@ func CreateSession(pool *pgxpool.Pool, ctx context.Context, profileId int, login return sessionKey } -func deleteSession(pool *pgxpool.Pool, ctx context.Context, profileId int) { +func deleteSession(pool *pgxpool.Pool, ctx context.Context, profileId int64) { _, err := pool.Exec(ctx, DeleteUserSession, profileId) if err != nil && !errors.Is(err, pgx.ErrNoRows) { panic(err) } } -func GetProfile(pool *pgxpool.Pool, ctx context.Context, profileId int) User { +func GetProfile(pool *pgxpool.Pool, ctx context.Context, profileId int64) User { user := User{} row := pool.QueryRow(ctx, GetUser, profileId) err := row.Scan(&user.UserId, &user.GsbrCode, &user.Password, &user.Email, &user.UniqueNick, &user.FirstName, &user.LastName) diff --git a/gcsp/login.go b/gcsp/login.go index d24fb90..c580d45 100644 --- a/gcsp/login.go +++ b/gcsp/login.go @@ -58,8 +58,8 @@ func login(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyComman OtherValues: map[string]string{ "sesskey": "199714190", "proof": proof, - "userid": strconv.Itoa(user.UserId), - "profileid": strconv.Itoa(user.ProfileId), + "userid": strconv.FormatInt(user.UserId, 10), + "profileid": strconv.FormatInt(user.ProfileId, 10), "uniquenick": user.UniqueNick, "lt": loginTicket, "id": command.OtherValues["id"], diff --git a/gcsp/main.go b/gcsp/main.go index eea3e4a..3a063af 100644 --- a/gcsp/main.go +++ b/gcsp/main.go @@ -20,7 +20,7 @@ import ( var ( ctx = context.Background() pool *pgxpool.Pool - userId int + userId int64 ) func checkError(err error) { diff --git a/gcsp/profile.go b/gcsp/profile.go index 6f2025a..6198a98 100644 --- a/gcsp/profile.go +++ b/gcsp/profile.go @@ -10,7 +10,7 @@ import ( func getProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) string { strProfileId := command.OtherValues["profileid"] - profileId, _ := strconv.Atoi(strProfileId) + profileId, _ := strconv.ParseInt(strProfileId, 10, 0) user := database.GetProfile(pool, ctx, profileId) @@ -21,7 +21,7 @@ func getProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyC OtherValues: map[string]string{ "profileid": command.OtherValues["profileid"], "nick": user.UniqueNick, - "userid": strconv.Itoa(user.UserId), + "userid": strconv.FormatInt(user.UserId, 10), "email": user.Email, "sig": "b126556e5ee62d4da9629dfad0f6b2a8", "uniquenick": user.UniqueNick, diff --git a/gpcm/login.go b/gpcm/login.go index d327ade..39279d8 100644 --- a/gpcm/login.go +++ b/gpcm/login.go @@ -58,8 +58,8 @@ func login(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyComman OtherValues: map[string]string{ "sesskey": "199714190", "proof": proof, - "userid": strconv.Itoa(user.UserId), - "profileid": strconv.Itoa(user.ProfileId), + "userid": strconv.FormatInt(user.UserId, 10), + "profileid": strconv.FormatInt(user.ProfileId, 10), "uniquenick": user.UniqueNick, "lt": loginTicket, "id": command.OtherValues["id"], diff --git a/gpcm/main.go b/gpcm/main.go index edd8d36..d543b1e 100644 --- a/gpcm/main.go +++ b/gpcm/main.go @@ -20,7 +20,7 @@ import ( var ( ctx = context.Background() pool *pgxpool.Pool - userId int + userId int64 ) func checkError(err error) { diff --git a/gpcm/profile.go b/gpcm/profile.go index 1c68ab4..713886e 100644 --- a/gpcm/profile.go +++ b/gpcm/profile.go @@ -13,7 +13,7 @@ import ( func getProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyCommand) string { strProfileId := command.OtherValues["profileid"] - profileId, _ := strconv.Atoi(strProfileId) + profileId, _ := strconv.ParseInt(strProfileId, 10, 0) user := database.GetProfile(pool, ctx, profileId) @@ -24,7 +24,7 @@ func getProfile(pool *pgxpool.Pool, ctx context.Context, command common.GameSpyC OtherValues: map[string]string{ "profileid": command.OtherValues["profileid"], "nick": user.UniqueNick, - "userid": strconv.Itoa(user.UserId), + "userid": strconv.FormatInt(user.UserId, 10), "email": user.Email, "sig": "b126556e5ee62d4da9629dfad0f6b2a8", "uniquenick": user.UniqueNick, diff --git a/nas/acctcreate.go b/nas/acctcreate.go new file mode 100644 index 0000000..5545910 --- /dev/null +++ b/nas/acctcreate.go @@ -0,0 +1,20 @@ +package nas + +import ( + "encoding/base64" + "net/url" + "strconv" + "strings" + "wwfc/database" +) + +func acctcreate(r *Response) { + param := url.Values{} + param.Set("retry", strings.Replace(base64.StdEncoding.EncodeToString([]byte("0")), "=", "*", -1)) + param.Set("returncd", strings.Replace(base64.StdEncoding.EncodeToString([]byte("002")), "=", "*", -1)) + param.Set("userid", strings.Replace(base64.StdEncoding.EncodeToString([]byte(strconv.FormatInt(database.GetUniqueUserID(), 10))), "=", "*", -1)) + + // Encode and send off to be written! + r.payload = []byte(param.Encode()) + r.payload = []byte(strings.Replace(string(r.payload), "%2A", "*", -1)) +} diff --git a/nas/login.go b/nas/login.go index 94238af..1e132d1 100644 --- a/nas/login.go +++ b/nas/login.go @@ -14,7 +14,7 @@ const Challenge = "0qUekMb4" func login(r *Response) { // Validate the user id. It must be an integer. strUserId, _ := base64.StdEncoding.DecodeString(strings.Replace(r.request.PostForm.Get("userid"), "*", "=", -1)) - userId, err := strconv.Atoi(string(strUserId)) + userId, err := strconv.ParseInt(string(strUserId), 10, 0) if err != nil { panic(err) } @@ -32,6 +32,5 @@ func login(r *Response) { // Encode and send off to be written! r.payload = []byte(param.Encode()) - r.payload = []byte(param.Encode()) r.payload = []byte(strings.Replace(string(r.payload), "%2A", "*", -1)) } diff --git a/nas/main.go b/nas/main.go index 518fed0..6f3872a 100644 --- a/nas/main.go +++ b/nas/main.go @@ -36,6 +36,7 @@ func StartServer() { r := NewRoute() ac := r.HandleGroup("ac") { + ac.HandleAction("acctcreate", acctcreate) ac.HandleAction("login", login) } diff --git a/nas/route.go b/nas/route.go index 74da67f..785db29 100644 --- a/nas/route.go +++ b/nas/route.go @@ -3,10 +3,10 @@ package nas import ( "encoding/base64" "github.com/logrusorgru/aurora/v3" - "log" "net/http" "strconv" "strings" + "wwfc/logging" ) type Route struct { @@ -48,17 +48,26 @@ func (r *RoutingGroup) HandleAction(action string, function func(*Response)) { func (route *Route) Handle() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - log.Printf("%s %s via %s", aurora.Yellow(r.Method), aurora.Cyan(r.URL), aurora.Cyan(r.Host)) + logging.Notice("NAS", aurora.Yellow(r.Method).String(), aurora.Cyan(r.URL).String(), "via", aurora.Cyan(r.Host).String()) err := r.ParseForm() if err != nil { - log.Printf(aurora.Red("failed to parse form").String()) + logging.Notice("NAS", aurora.Red("Failed to parse form").String()) return } - // While generally a bad idea, we can only have a path with a depth of one. - path := strings.Replace(r.URL.Path, "/", "", -1) + if !strings.HasPrefix(r.URL.Path, "/") { + logging.Notice("NAS", aurora.Red("Invalid URL").String()) + return + } + + path := r.URL.Path[1:] actionName, _ := base64.StdEncoding.DecodeString(strings.Replace(r.PostForm.Get("action"), "*", "=", -1)) + if string(actionName) == "" { + logging.Notice("NAS", aurora.Red("No action in form").String()) + return + } + var action Action for _, _action := range route.Actions { if path == _action.ServiceType && string(actionName) == _action.ActionName { @@ -68,7 +77,7 @@ func (route *Route) Handle() http.Handler { // Make sure we found an action if action.ActionName == "" && action.ServiceType == "" { - log.Printf(aurora.Red("no action found").String()) + logging.Notice("NAS", aurora.Red("No action for").String(), aurora.Yellow(string(actionName)).String()) return }