mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-03-21 17:44:58 -05:00
Allow extra user data in match commands
This commit is contained in:
parent
adaefd1cbb
commit
29a2e305d9
|
|
@ -58,6 +58,8 @@ type MatchCommandDataReservation struct {
|
|||
IsFriend bool
|
||||
LocalPlayerCount uint32
|
||||
ResvCheckValue uint32
|
||||
|
||||
UserData []byte
|
||||
}
|
||||
|
||||
type MatchCommandDataResvOK struct {
|
||||
|
|
@ -75,17 +77,20 @@ type MatchCommandDataResvOK struct {
|
|||
ClientCount uint32
|
||||
ResvCheckValue uint32
|
||||
|
||||
// Only exists in version 3 and 11
|
||||
// Version 3 and 11
|
||||
ProfileIDs []uint32
|
||||
|
||||
// Version 11
|
||||
IsFriend bool
|
||||
UserData uint32
|
||||
|
||||
UserData []byte
|
||||
}
|
||||
|
||||
type MatchCommandDataResvDeny struct {
|
||||
Reason uint32
|
||||
ReasonString string
|
||||
|
||||
UserData []byte
|
||||
}
|
||||
|
||||
type MatchCommandDataTellAddr struct {
|
||||
|
|
@ -173,13 +178,18 @@ func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandD
|
|||
return MatchCommandData{}, false
|
||||
}
|
||||
|
||||
// Match commands must be 4 byte aligned
|
||||
if (len(buffer) & 3) != 0 {
|
||||
return MatchCommandData{}, false
|
||||
}
|
||||
|
||||
switch command {
|
||||
case MatchReservation:
|
||||
if version == 3 && (len(buffer) != 0x04 && len(buffer) != 0x0C) {
|
||||
if version == 3 && len(buffer) < 0x0C {
|
||||
break
|
||||
}
|
||||
|
||||
if (version == 11 && len(buffer) != 0x14) || (version == 90 && len(buffer) != 0x24) {
|
||||
if (version == 11 && len(buffer) < 0x14) || (version == 90 && len(buffer) < 0x24) {
|
||||
break
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +198,7 @@ func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandD
|
|||
break
|
||||
}
|
||||
|
||||
if version == 3 && len(buffer) == 0x04 {
|
||||
if version == 3 && len(buffer) < 0x0C {
|
||||
return MatchCommandData{Reservation: &MatchCommandDataReservation{
|
||||
MatchType: byte(matchType),
|
||||
HasPublicIP: false,
|
||||
|
|
@ -207,6 +217,7 @@ func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandD
|
|||
HasPublicIP: true,
|
||||
PublicIP: binary.BigEndian.Uint32(buffer[0x04:0x08]),
|
||||
PublicPort: uint16(publicPort),
|
||||
UserData: buffer[0x0C:],
|
||||
}}, true
|
||||
|
||||
case 11:
|
||||
|
|
@ -223,6 +234,7 @@ func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandD
|
|||
PublicPort: uint16(publicPort),
|
||||
IsFriend: isFriend,
|
||||
LocalPlayerCount: binary.LittleEndian.Uint32(buffer[0x10:0x14]),
|
||||
UserData: buffer[0x14:],
|
||||
}}, true
|
||||
|
||||
case 90:
|
||||
|
|
@ -248,6 +260,7 @@ func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandD
|
|||
IsFriend: isFriend,
|
||||
LocalPlayerCount: binary.LittleEndian.Uint32(buffer[0x1C:0x20]),
|
||||
ResvCheckValue: binary.LittleEndian.Uint32(buffer[0x20:0x24]),
|
||||
UserData: buffer[0x24:],
|
||||
}}, true
|
||||
}
|
||||
|
||||
|
|
@ -258,49 +271,50 @@ func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandD
|
|||
}
|
||||
|
||||
clientCount := binary.LittleEndian.Uint32(buffer[0x00:0x04])
|
||||
if version == 3 && (clientCount > 29 || len(buffer) != int(0xC+clientCount*0x4)) {
|
||||
if version == 3 && (clientCount > 29 || len(buffer) < int(0x0C+clientCount*0x4)) {
|
||||
break
|
||||
}
|
||||
if version == 11 && (clientCount > 24 || len(buffer) != int(0x20+clientCount*0x4)) {
|
||||
if version == 11 && (clientCount > 24 || len(buffer) < int(0x20+clientCount*0x4)) {
|
||||
break
|
||||
}
|
||||
|
||||
var profileIDs []uint32
|
||||
for i := uint32(0); i < clientCount; i++ {
|
||||
profileIDs = append(profileIDs, binary.LittleEndian.Uint32(buffer[0x4+i*4:0x4+i*4+4]))
|
||||
profileIDs = append(profileIDs, binary.LittleEndian.Uint32(buffer[0x04+i*4:0x04+i*4+4]))
|
||||
}
|
||||
|
||||
index := 0x4 + clientCount*4
|
||||
index := 0x04 + clientCount*4
|
||||
|
||||
publicPort := binary.LittleEndian.Uint32(buffer[index+0x4 : index+0x8])
|
||||
publicPort := binary.LittleEndian.Uint32(buffer[index+0x04 : index+0x08])
|
||||
if publicPort > 0xffff {
|
||||
break
|
||||
}
|
||||
|
||||
if version == 3 {
|
||||
return MatchCommandData{ResvOK: &MatchCommandDataResvOK{
|
||||
PublicIP: binary.BigEndian.Uint32(buffer[index : index+0x4]),
|
||||
PublicIP: binary.BigEndian.Uint32(buffer[index : index+0x04]),
|
||||
PublicPort: uint16(publicPort),
|
||||
ClientCount: clientCount,
|
||||
ProfileIDs: profileIDs,
|
||||
UserData: buffer[index+0x8:],
|
||||
}}, true
|
||||
} else if version == 11 {
|
||||
isFriendValue := binary.LittleEndian.Uint32(buffer[index+0x8 : index+0xC])
|
||||
isFriendValue := binary.LittleEndian.Uint32(buffer[index+0x08 : index+0x0C])
|
||||
if isFriendValue > 1 {
|
||||
break
|
||||
}
|
||||
isFriend := isFriendValue != 0
|
||||
|
||||
return MatchCommandData{ResvOK: &MatchCommandDataResvOK{
|
||||
MaxPlayers: binary.LittleEndian.Uint32(buffer[0x14:0x18]),
|
||||
SenderAID: binary.LittleEndian.Uint32(buffer[index+0xC : index+0x10]),
|
||||
PublicIP: binary.BigEndian.Uint32(buffer[index : index+0x4]),
|
||||
MaxPlayers: binary.LittleEndian.Uint32(buffer[index+0x14 : index+0x18]),
|
||||
SenderAID: binary.LittleEndian.Uint32(buffer[index+0x0C : index+0x10]),
|
||||
PublicIP: binary.BigEndian.Uint32(buffer[index : index+0x04]),
|
||||
PublicPort: uint16(publicPort),
|
||||
GroupID: binary.LittleEndian.Uint32(buffer[0x10:0x14]),
|
||||
GroupID: binary.LittleEndian.Uint32(buffer[index+0x10 : index+0x14]),
|
||||
ClientCount: clientCount,
|
||||
ProfileIDs: profileIDs,
|
||||
IsFriend: isFriend,
|
||||
UserData: binary.LittleEndian.Uint32(buffer[0x18:0x1C]),
|
||||
UserData: buffer[index+0x18:],
|
||||
}}, true
|
||||
}
|
||||
break
|
||||
|
|
@ -335,6 +349,7 @@ func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandD
|
|||
ReceiverNewAID: binary.LittleEndian.Uint32(buffer[0x28:0x2C]),
|
||||
ClientCount: binary.LittleEndian.Uint32(buffer[0x2C:0x30]),
|
||||
ResvCheckValue: binary.LittleEndian.Uint32(buffer[0x30:0x34]),
|
||||
UserData: buffer[0x34:],
|
||||
}}, true
|
||||
|
||||
case MatchResvDeny:
|
||||
|
|
@ -359,6 +374,7 @@ func DecodeMatchCommand(command byte, buffer []byte, version int) (MatchCommandD
|
|||
return MatchCommandData{ResvDeny: &MatchCommandDataResvDeny{
|
||||
Reason: reason,
|
||||
ReasonString: reasonString,
|
||||
UserData: buffer[0x4:],
|
||||
}}, true
|
||||
|
||||
case MatchResvWait:
|
||||
|
|
@ -468,6 +484,12 @@ func EncodeMatchCommand(command byte, data MatchCommandData, version int) ([]byt
|
|||
message = binary.LittleEndian.AppendUint32(message, data.Reservation.LocalPlayerCount)
|
||||
message = binary.LittleEndian.AppendUint32(message, data.Reservation.ResvCheckValue)
|
||||
}
|
||||
|
||||
message = append(message, data.Reservation.UserData...)
|
||||
if (len(message) & 3) != 0 {
|
||||
return []byte{}, false
|
||||
}
|
||||
|
||||
return message, true
|
||||
|
||||
case MatchResvOK:
|
||||
|
|
@ -483,6 +505,11 @@ func EncodeMatchCommand(command byte, data MatchCommandData, version int) ([]byt
|
|||
message = binary.LittleEndian.AppendUint32(message, uint32(data.ResvOK.PublicPort))
|
||||
|
||||
if version == 3 {
|
||||
message = append(message, data.ResvOK.UserData...)
|
||||
if (len(message) & 3) != 0 {
|
||||
return []byte{}, false
|
||||
}
|
||||
|
||||
return message, true
|
||||
}
|
||||
|
||||
|
|
@ -496,7 +523,12 @@ func EncodeMatchCommand(command byte, data MatchCommandData, version int) ([]byt
|
|||
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.SenderAID)
|
||||
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.GroupID)
|
||||
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.MaxPlayers)
|
||||
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.UserData)
|
||||
|
||||
message = append(message, data.ResvOK.UserData...)
|
||||
if (len(message) & 3) != 0 {
|
||||
return []byte{}, false
|
||||
}
|
||||
|
||||
return message, true
|
||||
}
|
||||
|
||||
|
|
@ -514,10 +546,22 @@ func EncodeMatchCommand(command byte, data MatchCommandData, version int) ([]byt
|
|||
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.ReceiverNewAID)
|
||||
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.ClientCount)
|
||||
message = binary.LittleEndian.AppendUint32(message, data.ResvOK.ResvCheckValue)
|
||||
|
||||
message = append(message, data.ResvOK.UserData...)
|
||||
if (len(message) & 3) != 0 {
|
||||
return []byte{}, false
|
||||
}
|
||||
|
||||
return message, true
|
||||
|
||||
case MatchResvDeny:
|
||||
message := binary.LittleEndian.AppendUint32([]byte{}, data.ResvDeny.Reason)
|
||||
|
||||
message = append(message, data.ResvOK.UserData...)
|
||||
if (len(message) & 3) != 0 {
|
||||
return []byte{}, false
|
||||
}
|
||||
|
||||
return message, true
|
||||
|
||||
case MatchResvWait:
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package gpcm
|
|||
import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -263,6 +264,12 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
break
|
||||
}
|
||||
|
||||
if len(msgData) > 0x200 || (len(msgData)&3) != 0 {
|
||||
logging.Error(g.ModuleName, "Invalid length message data; message:", msg)
|
||||
g.replyError(ErrMessage)
|
||||
return
|
||||
}
|
||||
|
||||
msgMatchData, ok := common.DecodeMatchCommand(cmd, msgData, version)
|
||||
common.LogMatchCommand(g.ModuleName, strconv.FormatInt(int64(toProfileId), 10), cmd, msgMatchData)
|
||||
if !ok {
|
||||
|
|
@ -272,16 +279,23 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
}
|
||||
|
||||
if cmd == common.MatchReservation {
|
||||
if common.IPFormatNoPortToInt(g.Conn.RemoteAddr().String()) == int32(msgMatchData.Reservation.PublicIP) {
|
||||
g.QR2IP = uint64(msgMatchData.Reservation.PublicIP) | (uint64(msgMatchData.Reservation.PublicPort) << 32)
|
||||
if common.IPFormatNoPortToInt(g.Conn.RemoteAddr().String()) != int32(msgMatchData.Reservation.PublicIP) {
|
||||
logging.Error(g.ModuleName, "RESERVATION: Public IP mismatch")
|
||||
g.replyError(ErrMessage)
|
||||
return
|
||||
}
|
||||
} else if cmd == common.MatchResvOK {
|
||||
if common.IPFormatNoPortToInt(g.Conn.RemoteAddr().String()) == int32(msgMatchData.ResvOK.PublicIP) {
|
||||
g.QR2IP = uint64(msgMatchData.ResvOK.PublicIP) | (uint64(msgMatchData.ResvOK.PublicPort) << 32)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Replace public IP with QR2 search ID
|
||||
g.QR2IP = uint64(msgMatchData.Reservation.PublicIP) | (uint64(msgMatchData.Reservation.PublicPort) << 32)
|
||||
|
||||
} else if cmd == common.MatchResvOK {
|
||||
if common.IPFormatNoPortToInt(g.Conn.RemoteAddr().String()) != int32(msgMatchData.ResvOK.PublicIP) {
|
||||
logging.Error(g.ModuleName, "RESV_OK: Public IP mismatch")
|
||||
g.replyError(ErrMessage)
|
||||
return
|
||||
}
|
||||
|
||||
g.QR2IP = uint64(msgMatchData.ResvOK.PublicIP) | (uint64(msgMatchData.ResvOK.PublicPort) << 32)
|
||||
}
|
||||
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
|
@ -300,7 +314,10 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
}
|
||||
|
||||
if cmd == common.MatchReservation {
|
||||
g.ReservationPID = uint32(toProfileId)
|
||||
msgMatchData.Reservation.PublicIP = 0
|
||||
msgMatchData.Reservation.PublicPort = 0
|
||||
msgMatchData.Reservation.LocalIP = 0
|
||||
msgMatchData.Reservation.LocalPort = 0
|
||||
} else if cmd == common.MatchResvOK || cmd == common.MatchResvDeny || cmd == common.MatchResvWait {
|
||||
if toSession.ReservationPID != g.User.ProfileId {
|
||||
logging.Error(g.ModuleName, "Destination", aurora.Cyan(toProfileId), "has no reservation with the sender")
|
||||
|
|
@ -319,9 +336,32 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
g.replyError(ErrMessage)
|
||||
return
|
||||
}
|
||||
|
||||
if g.QR2IP&0xffffffff != toSession.QR2IP&0xffffffff {
|
||||
searchId := qr2.GetSearchID(g.QR2IP)
|
||||
if searchId == 0 {
|
||||
logging.Error(g.ModuleName, "Could not get QR2 search ID for IP", aurora.Cyan(fmt.Sprintf("%016x", g.QR2IP)))
|
||||
g.replyError(ErrMessage)
|
||||
return
|
||||
}
|
||||
|
||||
msgMatchData.ResvOK.PublicIP = uint32(searchId & 0xffffffff)
|
||||
msgMatchData.ResvOK.PublicPort = uint16(searchId >> 32)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newMsg, ok := common.EncodeMatchCommand(cmd, msgMatchData, version)
|
||||
if !ok || len(newMsg) > 0x200 {
|
||||
logging.Error(g.ModuleName, "Failed to encode match command; message:", msg)
|
||||
g.replyError(ErrMessage)
|
||||
return
|
||||
}
|
||||
|
||||
if cmd == common.MatchReservation {
|
||||
g.ReservationPID = uint32(toProfileId)
|
||||
}
|
||||
|
||||
sendMessageToSession("1", g.User.ProfileId, toSession, msg)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
|||
var receiver *Session
|
||||
senderIPInt, _ := common.IPFormatToInt(senderIP)
|
||||
|
||||
useSearchID := destSearchID < (0x400 << 32)
|
||||
useSearchID := destSearchID < (1 << 24)
|
||||
if useSearchID {
|
||||
receiver = sessionBySearchID[destSearchID]
|
||||
} else {
|
||||
|
|
@ -62,7 +62,7 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
|||
moduleName = "QR2/MSG:s" + strconv.FormatUint(uint64(natnegID), 10)
|
||||
} else if bytes.Equal(message[:4], []byte{0xbb, 0x49, 0xcc, 0x4d}) || bytes.Equal(message[:4], []byte("SBCM")) {
|
||||
// DWC match command
|
||||
if len(message) < 0x14 {
|
||||
if len(message) < 0x14 || len(message) > 0x94 {
|
||||
logging.Error(moduleName, "Received invalid length match command packet")
|
||||
return
|
||||
}
|
||||
|
|
@ -76,13 +76,7 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
|||
senderProfileID := binary.LittleEndian.Uint32(message[0x10:0x14])
|
||||
moduleName = "QR2/MSG:p" + strconv.FormatUint(uint64(senderProfileID), 10)
|
||||
|
||||
dataSize := message[9]
|
||||
if dataSize > 0x80 {
|
||||
logging.Error(moduleName, "Received malicious match command packet header")
|
||||
return
|
||||
}
|
||||
|
||||
if (int(dataSize) + 0x14) != len(message) {
|
||||
if (int(message[9]) + 0x14) != len(message) {
|
||||
logging.Error(moduleName, "Received invalid match command packet header")
|
||||
return
|
||||
}
|
||||
|
|
@ -204,7 +198,7 @@ func SendClientMessage(senderIP string, destSearchID uint64, message []byte) {
|
|||
|
||||
var matchMessage []byte
|
||||
matchMessage, ok = common.EncodeMatchCommand(message[8], matchData, version)
|
||||
if !ok {
|
||||
if !ok || len(matchMessage) > 0x80 {
|
||||
logging.Error(moduleName, "Failed to reencode match command:", aurora.Cyan(printHex(message)))
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ func setSessionData(moduleName string, addr net.Addr, sessionId uint32, payload
|
|||
|
||||
// Set search ID
|
||||
for {
|
||||
searchID := uint64(rand.Int63n((0x400<<32)-1) + 1)
|
||||
searchID := uint64(rand.Int63n((1<<24)-1) + 1)
|
||||
if _, exists := sessionBySearchID[searchID]; !exists {
|
||||
session.SearchID = searchID
|
||||
session.Data["+searchid"] = strconv.FormatUint(searchID, 10)
|
||||
|
|
@ -236,3 +236,14 @@ func GetSessionServers() []map[string]string {
|
|||
|
||||
return servers
|
||||
}
|
||||
|
||||
func GetSearchID(addr uint64) uint64 {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
if session := sessions[addr]; session != nil {
|
||||
return session.SearchID
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user