mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-04-25 08:07:58 -05:00
Accept players connecting via DNS exploit
This commit is contained in:
parent
e130fdc60f
commit
357a3d5b14
|
|
@ -29,7 +29,7 @@ func generateRandom(n int) []byte {
|
|||
var (
|
||||
authTokenKey = generateRandom(16)
|
||||
authTokenIV = generateRandom(16)
|
||||
authTokenMagic = generateRandom(16)
|
||||
authTokenMagic = generateRandom(15)
|
||||
|
||||
loginTicketKey = generateRandom(16)
|
||||
loginTicketIV = generateRandom(16)
|
||||
|
|
@ -46,7 +46,7 @@ func appendString(blob []byte, value string, maxlen int) []byte {
|
|||
return blob
|
||||
}
|
||||
|
||||
func MarshalNASAuthToken(gamecd string, userid uint64, gsbrcd string, cfc uint64, region byte, lang byte, ingamesn string) (string, string) {
|
||||
func MarshalNASAuthToken(gamecd string, userid uint64, gsbrcd string, cfc uint64, region byte, lang byte, ingamesn string, isLocalhost bool) (string, string) {
|
||||
blob := binary.LittleEndian.AppendUint64([]byte{}, uint64(time.Now().Unix()))
|
||||
|
||||
blob = appendString(blob, gamecd, 4)
|
||||
|
|
@ -64,6 +64,13 @@ func MarshalNASAuthToken(gamecd string, userid uint64, gsbrcd string, cfc uint64
|
|||
|
||||
challenge := RandomString(8)
|
||||
blob = append(blob, []byte(challenge)...)
|
||||
|
||||
if isLocalhost {
|
||||
blob = append(blob, 0x01)
|
||||
} else {
|
||||
blob = append(blob, 0x00)
|
||||
}
|
||||
|
||||
blob = append(blob, authTokenMagic...)
|
||||
|
||||
block, err := aes.NewCipher(authTokenKey)
|
||||
|
|
@ -75,7 +82,7 @@ func MarshalNASAuthToken(gamecd string, userid uint64, gsbrcd string, cfc uint64
|
|||
return "NDS" + Base64DwcEncoding.EncodeToString(blob), challenge
|
||||
}
|
||||
|
||||
func UnmarshalNASAuthToken(token string) (err error, gamecd string, issuetime time.Time, userid uint64, gsbrcd string, cfc uint64, region byte, lang byte, ingamesn string, challenge string) {
|
||||
func UnmarshalNASAuthToken(token string) (err error, gamecd string, issuetime time.Time, userid uint64, gsbrcd string, cfc uint64, region byte, lang byte, ingamesn string, challenge string, isLocalhost bool) {
|
||||
if !strings.HasPrefix(token, "NDS") {
|
||||
err = errors.New("invalid auth token prefix")
|
||||
return
|
||||
|
|
@ -98,7 +105,7 @@ func UnmarshalNASAuthToken(token string) (err error, gamecd string, issuetime ti
|
|||
|
||||
cipher.NewCBCDecrypter(block, authTokenIV).CryptBlocks(blob, blob)
|
||||
|
||||
if !bytes.Equal(blob[0x80:0x90], authTokenMagic) {
|
||||
if !bytes.Equal(blob[0x90-len(authTokenMagic):0x90], authTokenMagic) {
|
||||
err = errors.New("invalid auth token magic")
|
||||
return
|
||||
}
|
||||
|
|
@ -112,6 +119,7 @@ func UnmarshalNASAuthToken(token string) (err error, gamecd string, issuetime ti
|
|||
lang = blob[0x2B]
|
||||
ingamesn = string(blob[0x2D : 0x2D+min(blob[0x2C], 75)])
|
||||
challenge = string(blob[0x78:0x80])
|
||||
isLocalhost = blob[0x80] == 0x01
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,13 @@ import (
|
|||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"strconv"
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
"wwfc/qr2"
|
||||
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
func (g *GameSpySession) isFriendAdded(profileId uint32) bool {
|
||||
|
|
@ -198,7 +199,18 @@ func (g *GameSpySession) setStatus(command common.GameSpyCommand) {
|
|||
mutex.Unlock()
|
||||
}
|
||||
|
||||
const (
|
||||
resvDenyVer3 = "GPCM3vMAT\x0316"
|
||||
resvDenyVer11 = "GPCM11vMAT\x0300000010"
|
||||
resvDenyVer90 = "GPCM90vMAT\x03EAAAAA**"
|
||||
|
||||
resvWaitVer3 = "GPCM3vMAT\x04"
|
||||
resvWaitVer11 = "GPCM11vMAT\x04"
|
||||
resvWaitVer90 = "GPCM90vMAT\x04"
|
||||
)
|
||||
|
||||
func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
||||
// TODO: There are other command values that mean the same thing
|
||||
if command.CommandValue != "1" {
|
||||
logging.Notice(g.ModuleName, "Received unknown bestie message type:", aurora.Cyan(command.CommandValue))
|
||||
return
|
||||
|
|
@ -228,16 +240,24 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
// Parse message for security and room tracking purposes
|
||||
var version int
|
||||
var msgDataIndex int
|
||||
var resvDenyMsg string
|
||||
var resvWaitMsg string
|
||||
|
||||
if strings.HasPrefix(msg, "GPCM3vMAT") {
|
||||
version = 3
|
||||
resvDenyMsg = resvDenyVer3
|
||||
resvWaitMsg = resvWaitVer3
|
||||
msgDataIndex = 9
|
||||
} else if strings.HasPrefix(msg, "GPCM11vMAT") {
|
||||
// Only used for Brawl
|
||||
version = 11
|
||||
resvDenyMsg = resvDenyVer11
|
||||
resvWaitMsg = resvWaitVer11
|
||||
msgDataIndex = 10
|
||||
} else if strings.HasPrefix(msg, "GPCM90vMAT") {
|
||||
version = 90
|
||||
resvDenyMsg = resvDenyVer90
|
||||
resvWaitMsg = resvWaitVer90
|
||||
msgDataIndex = 10
|
||||
} else {
|
||||
logging.Error(g.ModuleName, "Invalid message prefix; message:", msg)
|
||||
|
|
@ -245,6 +265,13 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
return
|
||||
}
|
||||
|
||||
if !g.DeviceAuthenticated {
|
||||
logging.Notice(g.ModuleName, "Sender is not device authenticated yet")
|
||||
// g.replyError(ErrMessage)
|
||||
sendMessageToSession("1", uint32(toProfileId), g, resvWaitMsg)
|
||||
return
|
||||
}
|
||||
|
||||
if len(msg) < msgDataIndex+1 {
|
||||
logging.Error(g.ModuleName, "Invalid message length; message:", msg)
|
||||
g.replyError(ErrMessage)
|
||||
|
|
@ -268,7 +295,6 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
|
||||
msgData = binary.LittleEndian.AppendUint32(msgData, uint32(intValue))
|
||||
}
|
||||
break
|
||||
|
||||
case 11:
|
||||
for _, stringValue := range strings.Split(msg[msgDataIndex:], "/") {
|
||||
|
|
@ -281,7 +307,6 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
|
||||
msgData = append(msgData, byteValue...)
|
||||
}
|
||||
break
|
||||
|
||||
case 90:
|
||||
msgData, err = common.Base64DwcEncoding.DecodeString(msg[msgDataIndex:])
|
||||
|
|
@ -290,7 +315,11 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
g.replyError(ErrMessage)
|
||||
return
|
||||
}
|
||||
break
|
||||
|
||||
default:
|
||||
logging.Error(g.ModuleName, "Invalid message version; message:", msg)
|
||||
g.replyError(ErrMessage)
|
||||
return
|
||||
}
|
||||
|
||||
if len(msgData) > 0x200 || (len(msgData)&3) != 0 {
|
||||
|
|
@ -332,7 +361,8 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
var toSession *GameSpySession
|
||||
if toSession, ok = sessions[uint32(toProfileId)]; !ok || !toSession.LoggedIn {
|
||||
logging.Error(g.ModuleName, "Destination", aurora.Cyan(toProfileId), "is not online")
|
||||
g.replyError(ErrMessageFriendOffline)
|
||||
// g.replyError(ErrMessageFriendOffline)
|
||||
sendMessageToSession("1", toSession.User.ProfileId, g, resvDenyMsg)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -342,6 +372,12 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
return
|
||||
}
|
||||
|
||||
if !toSession.DeviceAuthenticated {
|
||||
logging.Error(g.ModuleName, "Destination", aurora.Cyan(toProfileId), "is not device authenticated")
|
||||
sendMessageToSession("1", toSession.User.ProfileId, g, resvDenyMsg)
|
||||
return
|
||||
}
|
||||
|
||||
if cmd == common.MatchReservation {
|
||||
msgMatchData.Reservation.PublicIP = 0
|
||||
msgMatchData.Reservation.PublicPort = 0
|
||||
|
|
@ -419,6 +455,12 @@ func sendMessageToProfileId(msgType string, from uint32, to uint32, msg string)
|
|||
func (g *GameSpySession) sendFriendStatus(profileId uint32) {
|
||||
if g.isFriendAdded(profileId) {
|
||||
if session, ok := sessions[profileId]; ok && session.LoggedIn && session.isFriendAdded(g.User.ProfileId) {
|
||||
// Prevent players abusing a stack overflow exploit with the locstring in Mario Kart Wii
|
||||
if session.NeedsExploit && strings.HasPrefix(session.GameCode, "RMC") && len(g.LocString) > 0x14 {
|
||||
logging.Warn("GPCM", "Blocked message from", aurora.Cyan(g.User.ProfileId), "to", aurora.Cyan(session.User.ProfileId), "due to a stack overflow exploit")
|
||||
return
|
||||
}
|
||||
|
||||
sendMessageToSession("100", g.User.ProfileId, session, g.Status)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -15,6 +14,8 @@ import (
|
|||
"wwfc/database"
|
||||
"wwfc/logging"
|
||||
"wwfc/qr2"
|
||||
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
func generateResponse(gpcmChallenge, nasChallenge, authToken, clientChallenge string) string {
|
||||
|
|
@ -110,42 +111,13 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
return
|
||||
}
|
||||
|
||||
if command.OtherValues["payload_ver"] != "1" {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "The payload version is invalid.",
|
||||
Fatal: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
authToken := command.OtherValues["authtoken"]
|
||||
if authToken == "" {
|
||||
g.replyError(ErrLogin)
|
||||
return
|
||||
}
|
||||
|
||||
signature, exists := command.OtherValues["wwfc_sig"]
|
||||
if !exists {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "Missing authentication signature.",
|
||||
Fatal: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var deviceId uint32
|
||||
if deviceId = verifySignature(authToken, signature); deviceId == 0 {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "The authentication signature is invalid.",
|
||||
Fatal: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err, _, issueTime, userId, gsbrcd, cfc, _, _, ingamesn, challenge := common.UnmarshalNASAuthToken(authToken)
|
||||
err, gamecd, issueTime, userId, gsbrcd, cfc, _, _, ingamesn, challenge, isLocalhost := common.UnmarshalNASAuthToken(authToken)
|
||||
if err != nil {
|
||||
g.replyError(ErrLogin)
|
||||
return
|
||||
|
|
@ -157,6 +129,43 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
return
|
||||
}
|
||||
|
||||
payloadVer, payloadVerExists := command.OtherValues["payload_ver"]
|
||||
signature, signatureExists := command.OtherValues["wwfc_sig"]
|
||||
deviceId := uint32(0)
|
||||
|
||||
if isLocalhost && !payloadVerExists && !signatureExists {
|
||||
// Players using the DNS exploit, need patching using a QR2 exploit
|
||||
// TODO: Check that the game is compatible with the DNS
|
||||
g.NeedsExploit = true
|
||||
} else {
|
||||
if !payloadVerExists || payloadVer != "1" {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "The payload version is invalid.",
|
||||
Fatal: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if !signatureExists {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "Missing authentication signature.",
|
||||
Fatal: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if deviceId = verifySignature(authToken, signature); deviceId == 0 {
|
||||
g.replyError(GPError{
|
||||
ErrorCode: ErrLogin.ErrorCode,
|
||||
ErrorString: "The authentication signature is invalid.",
|
||||
Fatal: true,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
response := generateResponse(g.Challenge, challenge, authToken, command.OtherValues["challenge"])
|
||||
if response != command.OtherValues["response"] {
|
||||
g.replyError(ErrLogin)
|
||||
|
|
@ -194,11 +203,10 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
|
||||
// Check to see if a session is already open with this profile ID
|
||||
mutex.Lock()
|
||||
_, exists = sessions[g.User.ProfileId]
|
||||
_, exists := sessions[g.User.ProfileId]
|
||||
if exists {
|
||||
mutex.Unlock()
|
||||
// Original GPCM would've force kicked the other logged in client,
|
||||
// but we just kick this client
|
||||
// TODO: Kick the other client, not this one
|
||||
g.replyError(ErrForcedDisconnect)
|
||||
return
|
||||
}
|
||||
|
|
@ -207,15 +215,16 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
|
||||
g.LoginTicket = common.MarshalGPCMLoginTicket(g.User.ProfileId)
|
||||
g.SessionKey = rand.Int31n(290000000) + 10000000
|
||||
g.GameCode = gamecd
|
||||
g.InGameName = ingamesn
|
||||
|
||||
g.DeviceAuthenticated = !g.NeedsExploit
|
||||
g.LoggedIn = true
|
||||
g.ModuleName = "GPCM:" + strconv.FormatInt(int64(g.User.ProfileId), 10)
|
||||
g.ModuleName += "/" + common.CalcFriendCodeString(g.User.ProfileId, "RMCJ")
|
||||
|
||||
// Notify QR2 of the login
|
||||
// TODO: Get ingamesn and cfc from NAS
|
||||
qr2.Login(g.User.ProfileId, ingamesn, cfc, g.Conn.RemoteAddr().String())
|
||||
qr2.Login(g.User.ProfileId, ingamesn, cfc, g.Conn.RemoteAddr().String(), g.NeedsExploit, g.DeviceAuthenticated)
|
||||
|
||||
payload := common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "lc",
|
||||
|
|
|
|||
33
gpcm/main.go
33
gpcm/main.go
|
|
@ -5,8 +5,6 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
|
|
@ -14,24 +12,31 @@ import (
|
|||
"wwfc/database"
|
||||
"wwfc/logging"
|
||||
"wwfc/qr2"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
type GameSpySession struct {
|
||||
Conn net.Conn
|
||||
User database.User
|
||||
ModuleName string
|
||||
LoggedIn bool
|
||||
Challenge string
|
||||
LoginTicket string
|
||||
SessionKey int32
|
||||
InGameName string
|
||||
Status string
|
||||
LocString string
|
||||
FriendList []uint32
|
||||
AuthFriendList []uint32
|
||||
Conn net.Conn
|
||||
User database.User
|
||||
ModuleName string
|
||||
LoggedIn bool
|
||||
DeviceAuthenticated bool
|
||||
Challenge string
|
||||
LoginTicket string
|
||||
SessionKey int32
|
||||
GameCode string
|
||||
InGameName string
|
||||
Status string
|
||||
LocString string
|
||||
FriendList []uint32
|
||||
AuthFriendList []uint32
|
||||
|
||||
QR2IP uint64
|
||||
ReservationPID uint32
|
||||
|
||||
NeedsExploit bool
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
|
|||
10
nas/auth.go
10
nas/auth.go
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
|
@ -16,6 +15,8 @@ import (
|
|||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
"wwfc/logging"
|
||||
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -76,7 +77,8 @@ func handleAuthRequest(moduleName string, w http.ResponseWriter, r *http.Request
|
|||
break
|
||||
|
||||
case "login":
|
||||
reply = login(moduleName, fields)
|
||||
isLocalhost := strings.HasPrefix(r.RemoteAddr, "127.0.0.1:") || strings.HasPrefix(r.RemoteAddr, "[::1]:")
|
||||
reply = login(moduleName, fields, isLocalhost)
|
||||
break
|
||||
|
||||
case "svcloc":
|
||||
|
|
@ -158,7 +160,7 @@ func acctcreate() map[string]string {
|
|||
}
|
||||
}
|
||||
|
||||
func login(moduleName string, fields map[string]string) map[string]string {
|
||||
func login(moduleName string, fields map[string]string, isLocalhost bool) map[string]string {
|
||||
param := map[string]string{
|
||||
"retry": "0",
|
||||
"datetime": getDateTime(),
|
||||
|
|
@ -231,7 +233,7 @@ func login(moduleName string, fields map[string]string) map[string]string {
|
|||
return param
|
||||
}
|
||||
|
||||
authToken, challenge := common.MarshalNASAuthToken(gamecd, userId, gsbrcd, cfcInt, regionByte[0], langByte[0], fields["ingamesn"])
|
||||
authToken, challenge := common.MarshalNASAuthToken(gamecd, userId, gsbrcd, cfcInt, regionByte[0], langByte[0], fields["ingamesn"], isLocalhost)
|
||||
|
||||
param["returncd"] = "001"
|
||||
param["challenge"] = challenge
|
||||
|
|
|
|||
32
nas/https.go
32
nas/https.go
|
|
@ -315,12 +315,11 @@ func startHTTPSProxy(address string, nasAddr string) {
|
|||
|
||||
// Read bytes from the HTTP server and forward them through the TLS connection
|
||||
go func() {
|
||||
buf := make([]byte, 0x1000)
|
||||
recvBuf := make([]byte, 0x100)
|
||||
|
||||
seq := uint64(1)
|
||||
index := 0
|
||||
for {
|
||||
n, err := newConn.Read(buf[index:])
|
||||
n, err := newConn.Read(recvBuf)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) || strings.Contains(err.Error(), "use of closed network connection") {
|
||||
return
|
||||
|
|
@ -330,9 +329,9 @@ func startHTTPSProxy(address string, nasAddr string) {
|
|||
return
|
||||
}
|
||||
|
||||
// fmt.Printf("Sent:\n% X ", buf[index:index+n])
|
||||
// fmt.Printf("Sent:\n% X ", recvBuf[:n])
|
||||
var record []byte
|
||||
record, seq = encryptTLS(macFn, cipher, buf[index:index+n], seq, []byte{0x17, 0x03, 0x01, byte(n >> 8), byte(n)})
|
||||
record, seq = encryptTLS(macFn, cipher, recvBuf[:n], seq, []byte{0x17, 0x03, 0x01, byte(n >> 8), byte(n)})
|
||||
|
||||
_, err = conn.Write(record)
|
||||
if err != nil {
|
||||
|
|
@ -349,6 +348,11 @@ func startHTTPSProxy(address string, nasAddr string) {
|
|||
for {
|
||||
n, err := conn.Read(buf[index:])
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) || strings.Contains(err.Error(), "use of closed network connection") {
|
||||
logging.Info(moduleName, "Connection closed by client after", aurora.BrightCyan(total), "bytes")
|
||||
return
|
||||
}
|
||||
|
||||
logging.Error(moduleName, "Failed to read from client:", err)
|
||||
return
|
||||
}
|
||||
|
|
@ -387,18 +391,20 @@ func startHTTPSProxy(address string, nasAddr string) {
|
|||
// fmt.Printf("\nDecrypted content:\n% X \n", buf[5:5+recordLength])
|
||||
|
||||
if buf[0] != 0x17 {
|
||||
if buf[0] == 0x15 && buf[5] == 0x01 && buf[6] == 0x00 {
|
||||
if buf[0] == 0x15 || buf[5] == 0x01 || buf[6] == 0x00 {
|
||||
logging.Info(moduleName, "Alert connection close by client after", aurora.BrightCyan(total), "bytes")
|
||||
return
|
||||
}
|
||||
logging.Error(moduleName, "Non-application data received")
|
||||
return
|
||||
}
|
||||
|
||||
// Send the decrypted content to the HTTP server
|
||||
_, err = newConn.Write(buf[5 : 5+recordLength-16])
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Failed to write to HTTP server:", err)
|
||||
logging.Error(moduleName, "Non-application data received:", aurora.Cyan(fmt.Sprintf("% X ", buf[:5+recordLength])))
|
||||
return
|
||||
} else {
|
||||
// Send the decrypted content to the HTTP server
|
||||
_, err = newConn.Write(buf[5 : 5+recordLength-16])
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Failed to write to HTTP server:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[5+recordLength:]
|
||||
|
|
|
|||
|
|
@ -1,27 +1,57 @@
|
|||
package qr2
|
||||
|
||||
type LoginInfo struct {
|
||||
ProfileID uint32
|
||||
InGameName string
|
||||
ConsoleFriendCode uint64
|
||||
GPPublicIP string
|
||||
ProfileID uint32
|
||||
InGameName string
|
||||
ConsoleFriendCode uint64
|
||||
GPPublicIP string
|
||||
NeedsExploit bool
|
||||
DeviceAuthenticated bool
|
||||
Session *Session
|
||||
}
|
||||
|
||||
var logins = map[uint32]LoginInfo{}
|
||||
var logins = map[uint32]*LoginInfo{}
|
||||
|
||||
func Login(profileID uint32, inGameName string, consoleFriendCode uint64, publicIP string) {
|
||||
func Login(profileID uint32, inGameName string, consoleFriendCode uint64, publicIP string, needsExploit bool, deviceAuthenticated bool) {
|
||||
mutex.Lock()
|
||||
logins[profileID] = LoginInfo{
|
||||
ProfileID: profileID,
|
||||
InGameName: inGameName,
|
||||
ConsoleFriendCode: consoleFriendCode,
|
||||
GPPublicIP: publicIP,
|
||||
|
||||
logins[profileID] = &LoginInfo{
|
||||
ProfileID: profileID,
|
||||
InGameName: inGameName,
|
||||
ConsoleFriendCode: consoleFriendCode,
|
||||
GPPublicIP: publicIP,
|
||||
NeedsExploit: needsExploit,
|
||||
DeviceAuthenticated: deviceAuthenticated,
|
||||
Session: nil,
|
||||
}
|
||||
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func SetDeviceAuthenticated(profileID uint32) {
|
||||
mutex.Lock()
|
||||
|
||||
if login, exists := logins[profileID]; exists {
|
||||
login.DeviceAuthenticated = true
|
||||
if login.Session != nil {
|
||||
login.Session.Data["+deviceauth"] = "1"
|
||||
}
|
||||
}
|
||||
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
func Logout(profileID uint32) {
|
||||
mutex.Lock()
|
||||
|
||||
// Delete login's session
|
||||
if login, exists := logins[profileID]; exists {
|
||||
if login.Session != nil {
|
||||
removeSession(makeLookupAddr(login.Session.Addr.String()))
|
||||
}
|
||||
}
|
||||
|
||||
delete(logins, profileID)
|
||||
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ import (
|
|||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"strconv"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
func printHex(data []byte) string {
|
||||
|
|
@ -48,6 +49,10 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
|||
return
|
||||
}
|
||||
|
||||
if receiver.Login == nil || !receiver.Login.DeviceAuthenticated {
|
||||
logging.Error(moduleName, "Destination", aurora.Cyan(destSearchID), "is not device authenticated")
|
||||
}
|
||||
|
||||
// Decode and validate the message
|
||||
isNatnegPacket := false
|
||||
if bytes.Equal(message[:2], []byte{0xfd, 0xfc}) {
|
||||
|
|
@ -100,6 +105,11 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
|||
return
|
||||
}
|
||||
|
||||
if sender.Login == nil || !sender.Login.DeviceAuthenticated {
|
||||
logging.Error(moduleName, "Sender is not device authenticated")
|
||||
return
|
||||
}
|
||||
|
||||
var ok bool
|
||||
matchData, ok = common.DecodeMatchCommand(message[8], message[0x14:], version)
|
||||
if !ok {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package qr2
|
||||
|
||||
import (
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
|
|
@ -10,6 +9,8 @@ import (
|
|||
"time"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -24,7 +25,7 @@ type Session struct {
|
|||
Addr net.Addr
|
||||
Challenge string
|
||||
Authenticated bool
|
||||
Login LoginInfo
|
||||
Login *LoginInfo
|
||||
LastKeepAlive int64
|
||||
Endianness byte // Some fields depend on the client's endianness
|
||||
Data map[string]string
|
||||
|
|
@ -60,6 +61,13 @@ func removeSession(addr uint64) {
|
|||
session.GroupPointer.Server = nil
|
||||
// TODO: Search for new host via dwc_hoststate
|
||||
}
|
||||
|
||||
session.GroupPointer = nil
|
||||
}
|
||||
|
||||
if session.Login != nil {
|
||||
session.Login.Session = nil
|
||||
session.Login = nil
|
||||
}
|
||||
|
||||
// Delete search ID lookup
|
||||
|
|
@ -156,7 +164,7 @@ func (session *Session) setProfileID(moduleName string, newPID string) bool {
|
|||
|
||||
// Check if the public IP matches the one used for the GPCM session
|
||||
var gpPublicIP string
|
||||
var loginInfo LoginInfo
|
||||
var loginInfo *LoginInfo
|
||||
var ok bool
|
||||
if loginInfo, ok = logins[uint32(profileID)]; ok {
|
||||
gpPublicIP = loginInfo.GPPublicIP
|
||||
|
|
@ -172,24 +180,18 @@ func (session *Session) setProfileID(moduleName string, newPID string) bool {
|
|||
|
||||
session.Login = loginInfo
|
||||
|
||||
// Constraint: only one session can exist with a profile ID
|
||||
var outdated []uint64
|
||||
for sessionAddr, otherSession := range sessions {
|
||||
if otherSession == session {
|
||||
continue
|
||||
}
|
||||
|
||||
if otherPID, ok := otherSession.Data["dwc_pid"]; !ok || otherPID != newPID {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove old sessions with the PID
|
||||
outdated = append(outdated, sessionAddr)
|
||||
// Constraint: only one session can exist with a given profile ID
|
||||
if loginInfo.Session != nil {
|
||||
logging.Notice(moduleName, "Removing outdated session", aurora.BrightCyan(loginInfo.Session.Addr.String()), "with PID", aurora.Cyan(newPID))
|
||||
removeSession(makeLookupAddr(loginInfo.Session.Addr.String()))
|
||||
}
|
||||
|
||||
for _, sessionAddr := range outdated {
|
||||
logging.Notice(moduleName, "Removing outdated session", aurora.BrightCyan(sessions[sessionAddr].Addr.String()), "with PID", aurora.Cyan(newPID))
|
||||
removeSession(sessionAddr)
|
||||
loginInfo.Session = session
|
||||
|
||||
if loginInfo.DeviceAuthenticated {
|
||||
session.Data["+deviceauth"] = "1"
|
||||
} else {
|
||||
session.Data["+deviceauth"] = "0"
|
||||
}
|
||||
|
||||
session.Data["dwc_pid"] = newPID
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package serverbrowser
|
||||
|
||||
import (
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"wwfc/logging"
|
||||
"wwfc/serverbrowser/filter"
|
||||
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
)
|
||||
|
||||
// DWC makes requests in the following formats:
|
||||
|
|
@ -29,6 +30,10 @@ func filterServers(servers []map[string]string, queryGame string, expression str
|
|||
continue
|
||||
}
|
||||
|
||||
if server["+deviceauth"] != "1" {
|
||||
continue
|
||||
}
|
||||
|
||||
if server["dwc_mver"] == "90" && (server["dwc_hoststate"] != "0" && server["dwc_hoststate"] != "2") {
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user