mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-07-13 07:01:02 -05:00
QR2: Ensure dwc_pid is set for client message
This commit is contained in:
parent
546557b119
commit
6afc0e2ae3
|
|
@ -25,11 +25,16 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
|||
receiver = sessionByPublicIP[destSearchID]
|
||||
}
|
||||
|
||||
if receiver == nil {
|
||||
if receiver == nil || !receiver.Authenticated {
|
||||
logging.Error(moduleName, "Destination", aurora.Cyan(destSearchID), "does not exist")
|
||||
return
|
||||
}
|
||||
|
||||
if destPid, ok := receiver.Data["dwc_pid"]; !ok || destPid == "" {
|
||||
logging.Error(moduleName, "Destination", aurora.Cyan(destSearchID), "has no profile ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Decode and validate the message
|
||||
isNatnegPacket := false
|
||||
if bytes.Equal(message[:2], []byte{0xfd, 0xfc}) {
|
||||
|
|
@ -65,11 +70,16 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
|||
}
|
||||
|
||||
sender = sessionByPublicIP[(uint64(qr2Port)<<32)|uint64(qr2IP)]
|
||||
if sender == nil {
|
||||
if sender == nil || !sender.Authenticated {
|
||||
logging.Error(moduleName, "Session does not exist with QR2 IP and port")
|
||||
return
|
||||
}
|
||||
|
||||
if !sender.setProfileID(moduleName, strconv.FormatUint(uint64(senderProfileID), 10)) {
|
||||
// Error already logged
|
||||
return
|
||||
}
|
||||
|
||||
var ok bool
|
||||
matchData, ok = common.DecodeMatchCommand(message[8], message[0x14:])
|
||||
if !ok {
|
||||
|
|
|
|||
161
qr2/session.go
161
qr2/session.go
|
|
@ -5,6 +5,7 @@ import (
|
|||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"wwfc/common"
|
||||
|
|
@ -62,89 +63,21 @@ func removeSession(sessionId uint32) {
|
|||
func setSessionData(sessionId uint32, payload map[string]string, addr net.Addr) (Session, bool) {
|
||||
moduleName := "QR2:" + strconv.FormatInt(int64(sessionId), 10)
|
||||
|
||||
// Perform sanity checks on the session data. This is a mess but
|
||||
|
||||
// This is an internal error that should not happen
|
||||
publicIP := ""
|
||||
var ok bool
|
||||
if publicIP, ok = payload["publicip"]; !ok || publicIP == "0" {
|
||||
logging.Error(moduleName, "Missing publicip in session data")
|
||||
return Session{}, false
|
||||
}
|
||||
|
||||
newPID, newPIDValid := payload["dwc_pid"]
|
||||
delete(payload, "dwc_pid")
|
||||
|
||||
// Moving into performing operations on the session data, so lock the mutex
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
session, sessionExists := sessions[sessionId]
|
||||
|
||||
if sessionExists && session.Data["publicip"] != publicIP {
|
||||
logging.Error(moduleName, "Public IP mismatch")
|
||||
if sessionExists && session.Addr.String() != addr.String() {
|
||||
logging.Error(moduleName, "Session IP mismatch")
|
||||
return Session{}, false
|
||||
}
|
||||
|
||||
if newPIDValid {
|
||||
var oldPID string
|
||||
oldPIDValid := false
|
||||
|
||||
if sessionExists {
|
||||
if oldPID, oldPIDValid = session.Data["dwc_pid"]; oldPIDValid && newPID != oldPID {
|
||||
logging.Error(moduleName, "New dwc_pid mismatch: new:", aurora.Cyan(newPID), "old:", aurora.Cyan(oldPID))
|
||||
return Session{}, false
|
||||
}
|
||||
}
|
||||
|
||||
if !oldPIDValid {
|
||||
// Setting a new PID so validate it
|
||||
profileID, err := strconv.ParseUint(newPID, 10, 32)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Invalid dwc_pid value:", aurora.Cyan(newPID))
|
||||
return Session{}, false
|
||||
}
|
||||
|
||||
// Reformat dwc_pid string
|
||||
newPID = strconv.FormatUint(profileID, 10)
|
||||
payload["dwc_pid"] = newPID
|
||||
|
||||
// Lookup the profile ID in GPCM and verify it's logged in.
|
||||
// Maybe we don't need this? It relies on GPCM being hosted in the same application, and
|
||||
// makes GPCM a dependency of QR2. Perhaps we could use the database.
|
||||
gpcmIP := gpcm.GetSessionIP(uint32(profileID))
|
||||
if gpcmIP == "" {
|
||||
logging.Error(moduleName, "Provided dwc_pid is not logged in:", aurora.Cyan(newPID))
|
||||
return Session{}, false
|
||||
}
|
||||
|
||||
gpcmIPStr, _ := common.IPFormatToString(gpcmIP)
|
||||
if gpcmIPStr != publicIP {
|
||||
logging.Error(moduleName, "Caller public IP does not match GPCM session")
|
||||
return Session{}, false
|
||||
}
|
||||
|
||||
// Constraint: only one session can exist with a profile ID
|
||||
var outdated []uint32
|
||||
for sessionID, otherSession := range sessions {
|
||||
if otherPID, ok := otherSession.Data["dwc_pid"]; !ok || otherPID != newPID {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove old sessions with the PID
|
||||
outdated = append(outdated, sessionID)
|
||||
}
|
||||
|
||||
for _, sessionID := range outdated {
|
||||
logging.Notice(moduleName, "Removing outdated session", aurora.BrightCyan(sessionID), "with PID", aurora.Cyan(newPID))
|
||||
delete(sessions, sessionID)
|
||||
}
|
||||
|
||||
logging.Notice(moduleName, "Opened session with PID", aurora.Cyan(newPID))
|
||||
}
|
||||
}
|
||||
|
||||
if !sessionExists {
|
||||
logging.Notice(moduleName, "Creating session", aurora.Cyan(sessionId).String())
|
||||
data := Session{
|
||||
session = &Session{
|
||||
SessionID: sessionId,
|
||||
Addr: addr,
|
||||
Challenge: "",
|
||||
|
|
@ -154,14 +87,22 @@ func setSessionData(sessionId uint32, payload map[string]string, addr net.Addr)
|
|||
Data: payload,
|
||||
PacketCount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
if newPIDValid && !session.setProfileID(moduleName, newPID) {
|
||||
return Session{}, false
|
||||
}
|
||||
|
||||
if !sessionExists {
|
||||
logging.Notice(moduleName, "Creating session", aurora.Cyan(sessionId).String())
|
||||
|
||||
// Set search ID
|
||||
for {
|
||||
searchID := uint64(rand.Int63n(0x400 << 32))
|
||||
if _, exists := sessionBySearchID[searchID]; !exists {
|
||||
data.SearchID = searchID
|
||||
data.Data["+searchid"] = strconv.FormatUint(searchID, 10)
|
||||
sessionBySearchID[searchID] = &data
|
||||
session.SearchID = searchID
|
||||
session.Data["+searchid"] = strconv.FormatUint(searchID, 10)
|
||||
sessionBySearchID[searchID] = session
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -169,18 +110,82 @@ func setSessionData(sessionId uint32, payload map[string]string, addr net.Addr)
|
|||
// Set public IP lookup
|
||||
ip, port := common.IPFormatToInt(addr.String())
|
||||
lookupIP := (uint64(port) << 32) | uint64(uint32(ip))
|
||||
sessionByPublicIP[lookupIP] = &data
|
||||
sessionByPublicIP[lookupIP] = session
|
||||
|
||||
sessions[sessionId] = &data
|
||||
return data, true
|
||||
sessions[sessionId] = session
|
||||
return *session, true
|
||||
}
|
||||
|
||||
if pid, ok := session.Data["dwc_pid"]; ok && pid != "" {
|
||||
payload["dwc_pid"] = pid
|
||||
}
|
||||
payload["+searchid"] = session.Data["+searchid"]
|
||||
|
||||
session.Data = payload
|
||||
session.LastKeepAlive = time.Now().Unix()
|
||||
return *session, true
|
||||
}
|
||||
|
||||
// Set the session's profile ID if it doesn't already exists.
|
||||
// Returns false if the profile ID is invalid.
|
||||
// Expects the global mutex to already be locked.
|
||||
func (session *Session) setProfileID(moduleName string, newPID string) bool {
|
||||
if oldPID, oldPIDValid := session.Data["dwc_pid"]; oldPIDValid && oldPID != "" {
|
||||
if newPID != oldPID {
|
||||
logging.Error(moduleName, "New dwc_pid mismatch: new:", aurora.Cyan(newPID), "old:", aurora.Cyan(oldPID))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Setting a new PID so validate it
|
||||
profileID, err := strconv.ParseUint(newPID, 10, 32)
|
||||
if err != nil || strconv.FormatUint(profileID, 10) != newPID {
|
||||
logging.Error(moduleName, "Invalid dwc_pid value:", aurora.Cyan(newPID))
|
||||
return false
|
||||
}
|
||||
|
||||
// Lookup the profile ID in GPCM and verify it's logged in.
|
||||
// Maybe we don't need this? It relies on GPCM being hosted in the same application, and
|
||||
// makes GPCM a dependency of QR2. Perhaps we could use the database.
|
||||
gpcmIP := gpcm.GetSessionIP(uint32(profileID))
|
||||
if gpcmIP == "" {
|
||||
logging.Error(moduleName, "Provided dwc_pid is not logged in:", aurora.Cyan(newPID))
|
||||
return false
|
||||
}
|
||||
|
||||
if strings.Split(gpcmIP, ":")[0] != strings.Split(session.Addr.String(), ":")[0] {
|
||||
logging.Error(moduleName, "Caller public IP does not match GPCM session")
|
||||
return false
|
||||
}
|
||||
|
||||
// Constraint: only one session can exist with a profile ID
|
||||
var outdated []uint32
|
||||
for sessionID, otherSession := range sessions {
|
||||
if sessionID == session.SessionID {
|
||||
continue
|
||||
}
|
||||
|
||||
if otherPID, ok := otherSession.Data["dwc_pid"]; !ok || otherPID != newPID {
|
||||
continue
|
||||
}
|
||||
|
||||
// Remove old sessions with the PID
|
||||
outdated = append(outdated, sessionID)
|
||||
}
|
||||
|
||||
for _, sessionID := range outdated {
|
||||
logging.Notice(moduleName, "Removing outdated session", aurora.BrightCyan(sessionID), "with PID", aurora.Cyan(newPID))
|
||||
delete(sessions, sessionID)
|
||||
}
|
||||
|
||||
session.Data["dwc_pid"] = newPID
|
||||
logging.Notice(moduleName, "Opened session with PID", aurora.Cyan(newPID))
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Get a copy of the list of servers
|
||||
func GetSessionServers() []map[string]string {
|
||||
var servers []map[string]string
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user