mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-03-21 17:44:58 -05:00
Add RPC support for GPSP, GameStats, ServerBrowser
This commit is contained in:
parent
22a3e1d98b
commit
fc69fe9a61
|
|
@ -1,11 +1,15 @@
|
|||
package gamestats
|
||||
|
||||
import (
|
||||
"wwfc/common"
|
||||
"wwfc/gpcm"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
func (g *GameStatsSession) replyError(err gpcm.GPError) {
|
||||
logging.Error(g.ModuleName, "Reply error:", err.ErrorString)
|
||||
g.Conn.Write([]byte(err.GetMessage()))
|
||||
common.SendPacket(ServerName, g.ConnIndex, []byte(err.GetMessage()))
|
||||
if err.Fatal {
|
||||
common.CloseConnection(ServerName, g.ConnIndex)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
package gamestats
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
"wwfc/database"
|
||||
"wwfc/gpcm"
|
||||
|
|
@ -15,10 +11,14 @@ import (
|
|||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
var ServerName = "gamestats"
|
||||
|
||||
type GameStatsSession struct {
|
||||
Conn net.Conn
|
||||
ConnIndex uint64
|
||||
RemoteAddr string
|
||||
ModuleName string
|
||||
Challenge string
|
||||
|
||||
|
|
@ -29,6 +29,7 @@ type GameStatsSession struct {
|
|||
LoginID int
|
||||
User database.User
|
||||
|
||||
ReadBuffer []byte
|
||||
WriteBuffer []byte
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +39,9 @@ var (
|
|||
|
||||
serverName string
|
||||
webSalt string
|
||||
webHashPad string
|
||||
|
||||
sessionsByConnIndex = make(map[uint64]*GameStatsSession)
|
||||
mutex = deadlock.RWMutex{}
|
||||
)
|
||||
|
||||
func StartServer() {
|
||||
|
|
@ -47,7 +50,6 @@ func StartServer() {
|
|||
|
||||
serverName = config.ServerName
|
||||
webSalt = common.RandomString(32)
|
||||
webHashPad = common.RandomString(8)
|
||||
|
||||
common.ReadGameList()
|
||||
|
||||
|
|
@ -62,51 +64,27 @@ func StartServer() {
|
|||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
address := *config.GameSpyAddress + ":29920"
|
||||
|
||||
l, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Close the listener when the application closes.
|
||||
defer l.Close()
|
||||
logging.Notice("GSTATS", "Listening on", address)
|
||||
|
||||
for {
|
||||
// Listen for an incoming connection.
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Handle connections in a new goroutine.
|
||||
go handleRequest(conn)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
session := GameStatsSession{
|
||||
Conn: conn,
|
||||
ModuleName: "GSTATS:" + conn.RemoteAddr().String(),
|
||||
func NewConnection(index uint64, address string) {
|
||||
session := &GameStatsSession{
|
||||
ConnIndex: index,
|
||||
RemoteAddr: address,
|
||||
ModuleName: "GSTATS:" + address,
|
||||
Challenge: common.RandomString(10),
|
||||
|
||||
SessionKey: 0,
|
||||
GameInfo: nil,
|
||||
|
||||
Authenticated: false,
|
||||
LoginID: 0,
|
||||
User: database.User{},
|
||||
|
||||
ReadBuffer: []byte{},
|
||||
WriteBuffer: []byte{},
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
err := conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
if err != nil {
|
||||
logging.Notice(session.ModuleName, "Unable to set keepalive:", err.Error())
|
||||
}
|
||||
|
||||
// Send challenge
|
||||
session.Write(common.GameSpyCommand{
|
||||
payload := common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "lc",
|
||||
CommandValue: "1",
|
||||
OtherValues: map[string]string{
|
||||
|
|
@ -114,104 +92,115 @@ func handleRequest(conn net.Conn) {
|
|||
"id": "1",
|
||||
},
|
||||
})
|
||||
conn.Write(session.WriteBuffer)
|
||||
session.WriteBuffer = []byte{}
|
||||
common.SendPacket(ServerName, index, []byte(payload))
|
||||
|
||||
logging.Notice(session.ModuleName, "Connection established from", conn.RemoteAddr())
|
||||
logging.Notice(session.ModuleName, "Connection established from", address)
|
||||
|
||||
// Here we go into the listening loop
|
||||
for {
|
||||
buffer := make([]byte, 0x4000)
|
||||
bufferSize := 0
|
||||
message := ""
|
||||
mutex.Lock()
|
||||
sessionsByConnIndex[index] = session
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
// Packets can be received in fragments, so this loop makes sure the packet has been fully received before continuing
|
||||
for {
|
||||
if bufferSize >= len(buffer) {
|
||||
logging.Error(session.ModuleName, "Buffer overflow")
|
||||
return
|
||||
}
|
||||
func CloseConnection(index uint64) {
|
||||
mutex.RLock()
|
||||
session := sessionsByConnIndex[index]
|
||||
mutex.RUnlock()
|
||||
|
||||
readSize, err := bufio.NewReader(conn).Read(buffer[bufferSize:])
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
logging.Info(session.ModuleName, "Connection closed")
|
||||
return
|
||||
}
|
||||
if session == nil {
|
||||
logging.Error("GSTATS", "Cannot find session for this connection index:", aurora.Cyan(index))
|
||||
return
|
||||
}
|
||||
|
||||
logging.Error(session.ModuleName, "Connection error:", err.Error())
|
||||
return
|
||||
}
|
||||
logging.Notice(session.ModuleName, "Connection closed")
|
||||
|
||||
bufferSize += readSize
|
||||
mutex.Lock()
|
||||
delete(sessionsByConnIndex, index)
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
if !bytes.Contains(buffer[max(0, bufferSize-readSize-6):bufferSize], []byte(`\final\`)) {
|
||||
continue
|
||||
}
|
||||
func HandlePacket(index uint64, data []byte) {
|
||||
mutex.RLock()
|
||||
session := sessionsByConnIndex[index]
|
||||
mutex.RUnlock()
|
||||
|
||||
// Decrypt the data
|
||||
decrypted := ""
|
||||
for i := 0; i < bufferSize; i++ {
|
||||
if i+7 <= bufferSize && bytes.Equal(buffer[i:i+7], []byte(`\final\`)) {
|
||||
// Append the decrypted content to the message
|
||||
message += decrypted + `\final\`
|
||||
decrypted = ""
|
||||
if session == nil {
|
||||
logging.Error("GSTATS", "Cannot find session for this connection index:", aurora.Cyan(index))
|
||||
return
|
||||
}
|
||||
|
||||
// Remove the processed data
|
||||
buffer = buffer[i+7:]
|
||||
bufferSize -= i + 7
|
||||
i = 0
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logging.Error(session.ModuleName, "Panic:", r)
|
||||
}
|
||||
}()
|
||||
|
||||
if bufferSize < 7 || !bytes.Contains(buffer[:bufferSize], []byte(`\final\`)) {
|
||||
break
|
||||
}
|
||||
continue
|
||||
}
|
||||
// Enforce maximum buffer size
|
||||
length := len(session.ReadBuffer) + len(data)
|
||||
if length > 0x4000 {
|
||||
logging.Error(session.ModuleName, "Buffer overflow")
|
||||
return
|
||||
}
|
||||
|
||||
decrypted += string(rune(buffer[i] ^ "GameSpy3D"[i%9]))
|
||||
}
|
||||
session.ReadBuffer = append(session.ReadBuffer, data...)
|
||||
|
||||
// Continue to processing the message if we have a full message and another message is not expected
|
||||
if len(message) > 0 && bufferSize <= 0 {
|
||||
break
|
||||
}
|
||||
// Packets can be received in fragments, so make sure we're at the end of a packet
|
||||
if string(session.ReadBuffer[max(0, length-7):length]) != `\final\` {
|
||||
return
|
||||
}
|
||||
|
||||
// Decrypt the data, can decrypt multiple packets
|
||||
decrypted := strings.Builder{}
|
||||
decrypted.Grow(length)
|
||||
p := 0
|
||||
for i := 0; i < length; i++ {
|
||||
if string(session.ReadBuffer[i:i+7]) == `\final\` {
|
||||
decrypted.WriteString(`\final\`)
|
||||
|
||||
i += 6
|
||||
p = 0
|
||||
continue
|
||||
}
|
||||
|
||||
commands, err := common.ParseGameSpyMessage(message)
|
||||
if err != nil {
|
||||
logging.Error(session.ModuleName, "Error parsing message:", err.Error())
|
||||
logging.Error(session.ModuleName, "Raw data:", message)
|
||||
session.replyError(gpcm.ErrParse)
|
||||
return
|
||||
}
|
||||
decrypted.WriteRune(rune(session.ReadBuffer[i] ^ "GameSpy3D"[p]))
|
||||
p = (p + 1) % 9
|
||||
}
|
||||
|
||||
commands = session.handleCommand("ka", commands, func(command common.GameSpyCommand) {
|
||||
session.Write(common.GameSpyCommand{
|
||||
Command: "ka",
|
||||
})
|
||||
message := decrypted.String()
|
||||
|
||||
commands, err := common.ParseGameSpyMessage(message)
|
||||
if err != nil {
|
||||
logging.Error(session.ModuleName, "Error parsing message:", err.Error())
|
||||
logging.Error(session.ModuleName, "Raw data:", message)
|
||||
session.replyError(gpcm.ErrParse)
|
||||
return
|
||||
}
|
||||
|
||||
commands = session.handleCommand("ka", commands, func(command common.GameSpyCommand) {
|
||||
session.Write(common.GameSpyCommand{
|
||||
Command: "ka",
|
||||
})
|
||||
})
|
||||
|
||||
commands = session.handleCommand("auth", commands, session.auth)
|
||||
commands = session.handleCommand("authp", commands, session.authp)
|
||||
commands = session.handleCommand("auth", commands, session.auth)
|
||||
commands = session.handleCommand("authp", commands, session.authp)
|
||||
|
||||
if len(commands) != 0 && !session.Authenticated {
|
||||
logging.Error(session.ModuleName, "Attempt to run command before authentication:", aurora.Cyan(commands[0]))
|
||||
session.replyError(gpcm.ErrNotLoggedIn)
|
||||
return
|
||||
}
|
||||
if len(commands) != 0 && !session.Authenticated {
|
||||
logging.Error(session.ModuleName, "Attempt to run command before authentication:", aurora.Cyan(commands[0]))
|
||||
session.replyError(gpcm.ErrNotLoggedIn)
|
||||
return
|
||||
}
|
||||
|
||||
commands = session.handleCommand("getpd", commands, session.getpd)
|
||||
commands = session.handleCommand("setpd", commands, session.setpd)
|
||||
common.UNUSED(session.ignoreCommand)
|
||||
commands = session.handleCommand("getpd", commands, session.getpd)
|
||||
commands = session.handleCommand("setpd", commands, session.setpd)
|
||||
common.UNUSED(session.ignoreCommand)
|
||||
|
||||
for _, command := range commands {
|
||||
logging.Error(session.ModuleName, "Unknown command:", aurora.Cyan(command))
|
||||
}
|
||||
for _, command := range commands {
|
||||
logging.Error(session.ModuleName, "Unknown command:", aurora.Cyan(command))
|
||||
}
|
||||
|
||||
if len(session.WriteBuffer) > 0 {
|
||||
conn.Write(session.WriteBuffer)
|
||||
session.WriteBuffer = []byte{}
|
||||
}
|
||||
if len(session.WriteBuffer) > 0 {
|
||||
common.SendPacket(ServerName, session.ConnIndex, session.WriteBuffer)
|
||||
session.WriteBuffer = []byte{}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -409,9 +409,9 @@ func (g *GameSpySession) replyError(err GPError) {
|
|||
if !g.LoginInfoSet {
|
||||
msg := err.GetMessage()
|
||||
// logging.Info(g.ModuleName, "Sending error message:", msg)
|
||||
common.SendPacket("gpcm", g.ConnIndex, []byte(msg))
|
||||
common.SendPacket(ServerName, g.ConnIndex, []byte(msg))
|
||||
if err.Fatal {
|
||||
common.CloseConnection("gpcm", g.ConnIndex)
|
||||
common.CloseConnection(ServerName, g.ConnIndex)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -423,8 +423,8 @@ func (g *GameSpySession) replyError(err GPError) {
|
|||
|
||||
msg := err.GetMessageTranslate(g.GameName, g.Region, g.Language, g.ConsoleFriendCode, deviceId)
|
||||
// logging.Info(g.ModuleName, "Sending error message:", msg)
|
||||
common.SendPacket("gpcm", g.ConnIndex, []byte(msg))
|
||||
common.SendPacket(ServerName, g.ConnIndex, []byte(msg))
|
||||
if err.Fatal {
|
||||
common.CloseConnection("gpcm", g.ConnIndex)
|
||||
common.CloseConnection(ServerName, g.ConnIndex)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ func sendMessageToSession(msgType string, from uint32, session *GameSpySession,
|
|||
"msg": msg,
|
||||
},
|
||||
})
|
||||
common.SendPacket("gpcm", session.ConnIndex, []byte(message))
|
||||
common.SendPacket(ServerName, session.ConnIndex, []byte(message))
|
||||
}
|
||||
|
||||
func sendMessageToSessionBuffer(msgType string, from uint32, session *GameSpySession, msg string) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func kickPlayer(profileID uint32, reason string) {
|
|||
|
||||
case "network_error":
|
||||
// No error message
|
||||
common.CloseConnection("gpcm", session.ConnIndex)
|
||||
common.CloseConnection(ServerName, session.ConnIndex)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ func kickPlayer(profileID uint32, reason string) {
|
|||
Fatal: true,
|
||||
WWFCMessage: errorMessage,
|
||||
})
|
||||
common.CloseConnection("gpcm", session.ConnIndex)
|
||||
common.CloseConnection(ServerName, session.ConnIndex)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
otherSession, exists := sessions[g.User.ProfileId]
|
||||
if exists {
|
||||
otherSession.replyError(ErrForcedDisconnect)
|
||||
common.CloseConnection("gpcm", otherSession.ConnIndex)
|
||||
common.CloseConnection(ServerName, otherSession.ConnIndex)
|
||||
|
||||
for i := 0; ; i++ {
|
||||
mutex.Unlock()
|
||||
|
|
@ -336,7 +336,7 @@ func (g *GameSpySession) login(command common.GameSpyCommand) {
|
|||
OtherValues: otherValues,
|
||||
})
|
||||
|
||||
common.SendPacket("gpcm", g.ConnIndex, []byte(payload))
|
||||
common.SendPacket(ServerName, g.ConnIndex, []byte(payload))
|
||||
}
|
||||
|
||||
func (g *GameSpySession) exLogin(command common.GameSpyCommand) {
|
||||
|
|
|
|||
14
gpcm/main.go
14
gpcm/main.go
|
|
@ -13,6 +13,8 @@ import (
|
|||
"github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
var ServerName = "gpcm"
|
||||
|
||||
type GameSpySession struct {
|
||||
ConnIndex uint64
|
||||
RemoteAddr string
|
||||
|
|
@ -113,7 +115,6 @@ func CloseConnection(index uint64) {
|
|||
}
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func NewConnection(index uint64, address string) {
|
||||
session := &GameSpySession{
|
||||
ConnIndex: index,
|
||||
|
|
@ -121,7 +122,7 @@ func NewConnection(index uint64, address string) {
|
|||
User: database.User{},
|
||||
ModuleName: "GPCM:" + address,
|
||||
LoggedIn: false,
|
||||
Challenge: "",
|
||||
Challenge: common.RandomString(10),
|
||||
StatusSet: false,
|
||||
Status: "",
|
||||
LocString: "",
|
||||
|
|
@ -129,9 +130,6 @@ func NewConnection(index uint64, address string) {
|
|||
AuthFriendList: []uint32{},
|
||||
}
|
||||
|
||||
// Set challenge
|
||||
session.Challenge = common.RandomString(10)
|
||||
|
||||
payload := common.CreateGameSpyMessage(common.GameSpyCommand{
|
||||
Command: "lc",
|
||||
CommandValue: "1",
|
||||
|
|
@ -140,7 +138,7 @@ func NewConnection(index uint64, address string) {
|
|||
"id": "1",
|
||||
},
|
||||
})
|
||||
common.SendPacket("gpcm", index, []byte(payload))
|
||||
common.SendPacket(ServerName, index, []byte(payload))
|
||||
|
||||
logging.Notice(session.ModuleName, "Connection established from", address)
|
||||
|
||||
|
|
@ -176,7 +174,7 @@ func HandlePacket(index uint64, data []byte) {
|
|||
// Commands must be handled in a certain order, not in the order supplied by the client
|
||||
|
||||
commands = session.handleCommand("ka", commands, func(command common.GameSpyCommand) {
|
||||
common.SendPacket("gpcm", session.ConnIndex, []byte(`\ka\\final\`))
|
||||
common.SendPacket(ServerName, session.ConnIndex, []byte(`\ka\\final\`))
|
||||
})
|
||||
commands = session.handleCommand("login", commands, session.login)
|
||||
commands = session.handleCommand("wwfc_exlogin", commands, session.exLogin)
|
||||
|
|
@ -202,7 +200,7 @@ func HandlePacket(index uint64, data []byte) {
|
|||
}
|
||||
|
||||
if session.WriteBuffer != "" {
|
||||
common.SendPacket("gpcm", session.ConnIndex, []byte(session.WriteBuffer))
|
||||
common.SendPacket(ServerName, session.ConnIndex, []byte(session.WriteBuffer))
|
||||
session.WriteBuffer = ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ func (g *GameSpySession) bestieMessage(command common.GameSpyCommand) {
|
|||
},
|
||||
})
|
||||
|
||||
common.SendPacket("gpcm", toSession.ConnIndex, []byte(message))
|
||||
common.SendPacket(ServerName, toSession.ConnIndex, []byte(message))
|
||||
|
||||
// Append sender's profile ID to dest's RecvStatusFromList
|
||||
toSession.RecvStatusFromList = append(toSession.RecvStatusFromList, g.User.ProfileId)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
package gpsp
|
||||
|
||||
import (
|
||||
"net"
|
||||
"wwfc/common"
|
||||
"wwfc/gpcm"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
func replyError(moduleName string, conn net.Conn, err gpcm.GPError) {
|
||||
func replyError(moduleName string, connIndex uint64, err gpcm.GPError) {
|
||||
logging.Error(moduleName, "Reply error:", err.ErrorString)
|
||||
conn.Write([]byte(err.GetMessage()))
|
||||
msg := err.GetMessage()
|
||||
common.SendPacket(ServerName, connIndex, []byte(msg))
|
||||
if err.Fatal {
|
||||
common.CloseConnection(ServerName, connIndex)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
93
gpsp/main.go
93
gpsp/main.go
|
|
@ -1,85 +1,54 @@
|
|||
package gpsp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"wwfc/common"
|
||||
"wwfc/gpcm"
|
||||
"wwfc/logging"
|
||||
)
|
||||
|
||||
var ServerName = "gpsp"
|
||||
|
||||
func StartServer() {
|
||||
// Get config
|
||||
config := common.GetConfig()
|
||||
|
||||
address := *config.GameSpyAddress + ":29901"
|
||||
l, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Close the listener when the application closes.
|
||||
defer l.Close()
|
||||
logging.Notice("GPSP", "Listening on", address)
|
||||
|
||||
for {
|
||||
// Listen for an incoming connection.
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Handle connections in a new goroutine.
|
||||
go handleRequest(conn)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
func NewConnection(index uint64, address string) {
|
||||
}
|
||||
|
||||
func CloseConnection(index uint64) {
|
||||
}
|
||||
|
||||
func HandlePacket(index uint64, data []byte) {
|
||||
moduleName := "GPSP"
|
||||
|
||||
err := conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
if err != nil {
|
||||
logging.Notice(moduleName, "Unable to set keepalive:", err.Error())
|
||||
// TODO: Handle split packets
|
||||
message := ""
|
||||
for _, b := range data {
|
||||
message += string(b)
|
||||
}
|
||||
|
||||
// Here we go into the listening loop
|
||||
for {
|
||||
// TODO: Handle split packets
|
||||
buffer := make([]byte, 1024)
|
||||
_, err := bufio.NewReader(conn).Read(buffer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
commands, err := common.ParseGameSpyMessage(message)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Error parsing message:", err.Error())
|
||||
logging.Error(moduleName, "Raw data:", message)
|
||||
replyError(moduleName, index, gpcm.ErrParse)
|
||||
return
|
||||
}
|
||||
|
||||
commands, err := common.ParseGameSpyMessage(string(buffer))
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Error parsing message:", err.Error())
|
||||
logging.Error(moduleName, "Raw data:", string(buffer))
|
||||
replyError(moduleName, conn, gpcm.ErrParse)
|
||||
return
|
||||
}
|
||||
for _, command := range commands {
|
||||
switch command.Command {
|
||||
default:
|
||||
logging.Error(moduleName, "Unknown command:", command.Command)
|
||||
logging.Error(moduleName, "Raw data:", message)
|
||||
replyError(moduleName, index, gpcm.ErrParse)
|
||||
|
||||
for _, command := range commands {
|
||||
switch command.Command {
|
||||
default:
|
||||
logging.Error(moduleName, "Unknown command:", command.Command)
|
||||
logging.Error(moduleName, "Raw data:", string(buffer))
|
||||
replyError(moduleName, conn, gpcm.ErrParse)
|
||||
case "ka":
|
||||
common.SendPacket(ServerName, index, []byte(`\ka\\final\`))
|
||||
|
||||
case "ka":
|
||||
conn.Write([]byte(`\ka\\final\`))
|
||||
case "otherslist":
|
||||
common.SendPacket(ServerName, index, []byte(handleOthersList(command)))
|
||||
|
||||
case "otherslist":
|
||||
conn.Write([]byte(handleOthersList(command)))
|
||||
|
||||
case "search":
|
||||
conn.Write([]byte(handleSearch(command)))
|
||||
}
|
||||
case "search":
|
||||
common.SendPacket(ServerName, index, []byte(handleSearch(command)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
34
main.go
34
main.go
|
|
@ -99,8 +99,14 @@ func backendMain() {
|
|||
// RPCPacket.NewConnection is called by the frontend to notify the backend of a new connection
|
||||
func (r *RPCPacket) NewConnection(args RPCPacket, _ *struct{}) error {
|
||||
switch args.Server {
|
||||
case "serverbrowser":
|
||||
serverbrowser.NewConnection(args.Index, args.Address)
|
||||
case "gpcm":
|
||||
gpcm.NewConnection(args.Index, args.Address)
|
||||
case "gpsp":
|
||||
gpsp.NewConnection(args.Index, args.Address)
|
||||
case "gamestats":
|
||||
gamestats.NewConnection(args.Index, args.Address)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -109,8 +115,14 @@ func (r *RPCPacket) NewConnection(args RPCPacket, _ *struct{}) error {
|
|||
// RPCPacket.HandlePacket is called by the frontend to forward a packet to the backend
|
||||
func (r *RPCPacket) HandlePacket(args RPCPacket, _ *struct{}) error {
|
||||
switch args.Server {
|
||||
case "serverbrowser":
|
||||
serverbrowser.HandlePacket(args.Index, args.Data, args.Address)
|
||||
case "gpcm":
|
||||
gpcm.HandlePacket(args.Index, args.Data)
|
||||
case "gpsp":
|
||||
gpsp.HandlePacket(args.Index, args.Data)
|
||||
case "gamestats":
|
||||
gamestats.HandlePacket(args.Index, args.Data)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -119,8 +131,14 @@ func (r *RPCPacket) HandlePacket(args RPCPacket, _ *struct{}) error {
|
|||
// rpcPacket.closeConnection is called by the frontend to notify the backend of a closed connection
|
||||
func (r *RPCPacket) CloseConnection(args RPCPacket, _ *struct{}) error {
|
||||
switch args.Server {
|
||||
case "serverbrowser":
|
||||
serverbrowser.CloseConnection(args.Index)
|
||||
case "gpcm":
|
||||
gpcm.CloseConnection(args.Index)
|
||||
case "gpsp":
|
||||
gpsp.CloseConnection(args.Index)
|
||||
case "gamestats":
|
||||
gamestats.CloseConnection(args.Index)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
@ -165,10 +183,10 @@ func frontendMain() {
|
|||
go startBackendProcess()
|
||||
|
||||
servers := []serverInfo{
|
||||
// {rpcName: "serverbrowser", protocol: "tcp", port: 28910},
|
||||
{rpcName: "serverbrowser", protocol: "tcp", port: 28910},
|
||||
{rpcName: "gpcm", protocol: "tcp", port: 29900},
|
||||
// {rpcName: "gpsp", protocol: "tcp", port: 29901},
|
||||
// {rpcName: "gamestats", protocol: "tcp", port: 29920},
|
||||
{rpcName: "gpsp", protocol: "tcp", port: 29901},
|
||||
{rpcName: "gamestats", protocol: "tcp", port: 29920},
|
||||
}
|
||||
|
||||
for _, server := range servers {
|
||||
|
|
@ -291,6 +309,10 @@ func handleConnection(server serverInfo, conn net.Conn, index uint64) {
|
|||
break
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
rpcMutex.Lock()
|
||||
rpcBusyCount.Add(1)
|
||||
rpcMutex.Unlock()
|
||||
|
|
@ -302,6 +324,9 @@ func handleConnection(server serverInfo, conn net.Conn, index uint64) {
|
|||
|
||||
if err != nil {
|
||||
logging.Error("FRONTEND", "Failed to forward packet to backend:", err)
|
||||
if err == rpc.ErrShutdown {
|
||||
os.Exit(1)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
@ -317,6 +342,9 @@ func handleConnection(server serverInfo, conn net.Conn, index uint64) {
|
|||
|
||||
if err != nil {
|
||||
logging.Error("FRONTEND", "Failed to forward close connection to backend:", err)
|
||||
if err == rpc.ErrShutdown {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ import (
|
|||
|
||||
// Example: dwc_mver = 90 and dwc_pid != 43 and maxplayers = 11 and numplayers < 11 and dwc_mtype = 0 and dwc_hoststate = 2 and dwc_suspend = 0 and (rk = 'vs' and ev >= 4250 and ev <= 5750 and p = 0)
|
||||
|
||||
func filterServers(servers []map[string]string, queryGame string, expression string, publicIP string) []map[string]string {
|
||||
func filterServers(moduleName string, servers []map[string]string, queryGame string, expression string, publicIP string) []map[string]string {
|
||||
// Matchmaking search
|
||||
tree, err := filter.Parse(expression)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Error parsing filter:", err.Error())
|
||||
logging.Error(moduleName, "Error parsing filter:", err.Error())
|
||||
return []map[string]string{}
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ func filterServers(servers []map[string]string, queryGame string, expression str
|
|||
|
||||
ret, err := filter.Eval(tree, server, queryGame)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Error evaluating filter:", err.Error())
|
||||
logging.Error(moduleName, "Error evaluating filter:", err.Error())
|
||||
return []map[string]string{}
|
||||
}
|
||||
|
||||
|
|
@ -49,11 +49,14 @@ func filterServers(servers []map[string]string, queryGame string, expression str
|
|||
}
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "Matched", aurora.BrightCyan(len(filtered)), "servers")
|
||||
if len(filtered) != 0 {
|
||||
logging.Info(moduleName, "Matched", aurora.BrightCyan(len(filtered)), "servers")
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
func filterSelfLookup(servers []map[string]string, queryGame string, dwcPid string, publicIP string) []map[string]string {
|
||||
func filterSelfLookup(moduleName string, servers []map[string]string, queryGame string, dwcPid string, publicIP string) []map[string]string {
|
||||
var filtered []map[string]string
|
||||
|
||||
// Search for where the profile ID matches
|
||||
|
|
@ -64,7 +67,7 @@ func filterSelfLookup(servers []map[string]string, queryGame string, dwcPid stri
|
|||
|
||||
if server["dwc_pid"] == dwcPid {
|
||||
// May not be a self lookup, some games search for friends like this
|
||||
logging.Info(ModuleName, "Lookup", aurora.Cyan(dwcPid), "ok")
|
||||
logging.Info(moduleName, "Lookup", aurora.Cyan(dwcPid), "ok")
|
||||
return []map[string]string{server}
|
||||
}
|
||||
|
||||
|
|
@ -85,10 +88,10 @@ func filterSelfLookup(servers []map[string]string, queryGame string, dwcPid stri
|
|||
}
|
||||
|
||||
if len(filtered) == 0 {
|
||||
logging.Error(ModuleName, "Could not find server with dwc_pid", aurora.Cyan(dwcPid))
|
||||
logging.Error(moduleName, "Could not find server with dwc_pid", aurora.Cyan(dwcPid))
|
||||
return []map[string]string{}
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "Self lookup for", aurora.Cyan(dwcPid), "matched", aurora.BrightCyan(len(filtered)), "servers via public IP")
|
||||
logging.Info(moduleName, "Self lookup for", aurora.Cyan(dwcPid), "matched", aurora.BrightCyan(len(filtered)), "servers via public IP")
|
||||
return filtered
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +1,17 @@
|
|||
package serverbrowser
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"wwfc/common"
|
||||
"wwfc/logging"
|
||||
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
var (
|
||||
ctx = context.Background()
|
||||
pool *pgxpool.Pool
|
||||
userId int
|
||||
)
|
||||
var ServerName = "serverbrowser"
|
||||
|
||||
const (
|
||||
ModuleName = "SB"
|
||||
|
||||
// Requests sent from the client
|
||||
ServerListRequest = 0x00
|
||||
ServerInfoRequest = 0x01
|
||||
|
|
@ -42,127 +29,99 @@ const (
|
|||
PlayerSearchMessage = 0x06
|
||||
)
|
||||
|
||||
var (
|
||||
connBuffers = map[uint64]*[]byte{}
|
||||
mutex = deadlock.RWMutex{}
|
||||
)
|
||||
|
||||
func StartServer() {
|
||||
// Get config
|
||||
config := common.GetConfig()
|
||||
|
||||
// Start SQL
|
||||
dbString := fmt.Sprintf("postgres://%s:%s@%s/%s", config.Username, config.Password, config.DatabaseAddress, config.DatabaseName)
|
||||
dbConf, err := pgxpool.ParseConfig(dbString)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pool, err = pgxpool.ConnectConfig(ctx, dbConf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
address := *config.GameSpyAddress + ":28910"
|
||||
l, err := net.Listen("tcp", address)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
// Close the listener when the application closes.
|
||||
defer l.Close()
|
||||
logging.Notice(ModuleName, "Listening on", address)
|
||||
|
||||
for {
|
||||
// Listen for an incoming connection.
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
fmt.Println("Error accepting: ", err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Handle connections in a new goroutine.
|
||||
go handleRequest(conn)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Handles incoming requests.
|
||||
func handleRequest(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
func NewConnection(index uint64, address string) {
|
||||
}
|
||||
|
||||
err := conn.(*net.TCPConn).SetKeepAlive(true)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Unable to set keepalive", err.Error())
|
||||
}
|
||||
func CloseConnection(index uint64) {
|
||||
mutex.Lock()
|
||||
delete(connBuffers, index)
|
||||
mutex.Unlock()
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "Connection established from", aurora.BrightCyan(conn.RemoteAddr()))
|
||||
func HandlePacket(index uint64, data []byte, address string) {
|
||||
moduleName := "SB:" + address
|
||||
|
||||
// Here we go into the listening loop
|
||||
bufferSize := 0
|
||||
packetSize := uint16(0)
|
||||
var buffer []byte
|
||||
for {
|
||||
// Remove stale data and remake the buffer
|
||||
buffer = append(buffer[packetSize:], make([]byte, 1024-packetSize)...)
|
||||
bufferSize -= int(packetSize)
|
||||
packetSize = 0
|
||||
mutex.RLock()
|
||||
buffer := connBuffers[index]
|
||||
mutex.RUnlock()
|
||||
|
||||
// Packets tend to be sent in fragments, so this loop makes sure the packets has been fully received before continuing
|
||||
for {
|
||||
if bufferSize > 2 {
|
||||
packetSize = binary.BigEndian.Uint16(buffer[:2])
|
||||
if packetSize < 3 || packetSize >= 1024 {
|
||||
logging.Error(ModuleName, "Invalid packet size - terminating")
|
||||
return
|
||||
}
|
||||
|
||||
if bufferSize >= int(packetSize) {
|
||||
// Got a full packet, break to continue
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
readSize, err := bufio.NewReader(conn).Read(buffer[bufferSize:])
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
logging.Info(ModuleName, "Connection closed")
|
||||
return
|
||||
}
|
||||
|
||||
logging.Error(ModuleName, "Connection error:", err.Error())
|
||||
if buffer == nil {
|
||||
buffer = &[]byte{}
|
||||
defer func() {
|
||||
if buffer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
bufferSize += readSize
|
||||
}
|
||||
mutex.Lock()
|
||||
connBuffers[index] = buffer
|
||||
mutex.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
switch buffer[2] {
|
||||
case ServerListRequest:
|
||||
logging.Info(ModuleName, "Command:", aurora.Yellow("SERVER_LIST_REQUEST"))
|
||||
handleServerListRequest(conn, buffer[:packetSize])
|
||||
break
|
||||
if len(*buffer)+len(data) > 0x1000 {
|
||||
logging.Error(moduleName, "Buffer overflow")
|
||||
common.CloseConnection(ServerName, index)
|
||||
buffer = nil
|
||||
return
|
||||
}
|
||||
|
||||
case ServerInfoRequest:
|
||||
logging.Info(ModuleName, "Command:", aurora.Yellow("SERVER_INFO_REQUEST"))
|
||||
break
|
||||
*buffer = append(*buffer, data...)
|
||||
|
||||
case SendMessageRequest:
|
||||
logging.Info(ModuleName, "Command:", aurora.Yellow("SEND_MESSAGE_REQUEST"))
|
||||
handleSendMessageRequest(conn, buffer[:packetSize])
|
||||
break
|
||||
// Packets can be sent in fragments, so we need to check if we have a full packet
|
||||
// The first two bytes signify the packet size
|
||||
if len(*buffer) < 2 {
|
||||
return
|
||||
}
|
||||
|
||||
case KeepaliveReply:
|
||||
logging.Info(ModuleName, "Command:", aurora.Yellow("KEEPALIVE_REPLY"))
|
||||
break
|
||||
packetSize := binary.BigEndian.Uint16((*buffer)[:2])
|
||||
if packetSize < 3 || packetSize > 0x1000 {
|
||||
logging.Error(moduleName, "Invalid packet size - terminating")
|
||||
common.CloseConnection(ServerName, index)
|
||||
buffer = nil
|
||||
return
|
||||
}
|
||||
|
||||
case MapLoopRequest:
|
||||
logging.Info(ModuleName, "Command:", aurora.Yellow("MAPLOOP_REQUEST"))
|
||||
break
|
||||
if len(*buffer) < int(packetSize) {
|
||||
return
|
||||
}
|
||||
|
||||
case PlayerSearchRequest:
|
||||
logging.Info(ModuleName, "Command:", aurora.Yellow("PLAYER_SEARCH_REQUEST"))
|
||||
break
|
||||
switch (*buffer)[2] {
|
||||
case ServerListRequest:
|
||||
// logging.Info(moduleName, "Command:", aurora.Yellow("SERVER_LIST_REQUEST"))
|
||||
handleServerListRequest(moduleName, index, address, (*buffer)[:packetSize])
|
||||
|
||||
default:
|
||||
logging.Error(ModuleName, "Unknown command:", aurora.Cyan(buffer[2]))
|
||||
break
|
||||
}
|
||||
case ServerInfoRequest:
|
||||
logging.Info(moduleName, "Command:", aurora.Yellow("SERVER_INFO_REQUEST"))
|
||||
|
||||
case SendMessageRequest:
|
||||
// logging.Info(moduleName, "Command:", aurora.Yellow("SEND_MESSAGE_REQUEST"))
|
||||
handleSendMessageRequest(moduleName, index, address, (*buffer)[:packetSize])
|
||||
|
||||
case KeepaliveReply:
|
||||
logging.Info(moduleName, "Command:", aurora.Yellow("KEEPALIVE_REPLY"))
|
||||
|
||||
case MapLoopRequest:
|
||||
logging.Info(moduleName, "Command:", aurora.Yellow("MAPLOOP_REQUEST"))
|
||||
|
||||
case PlayerSearchRequest:
|
||||
logging.Info(moduleName, "Command:", aurora.Yellow("PLAYER_SEARCH_REQUEST"))
|
||||
|
||||
default:
|
||||
logging.Error(moduleName, "Unknown command:", aurora.Cyan((*buffer)[2]))
|
||||
}
|
||||
|
||||
if len(*buffer) > int(packetSize) {
|
||||
*buffer = (*buffer)[packetSize:]
|
||||
} else {
|
||||
*buffer = []byte{}
|
||||
buffer = nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -82,40 +81,45 @@ func popUint32(buffer []byte, index int) (uint32, int, error) {
|
|||
|
||||
var regexSelfLookup = regexp.MustCompile(`^dwc_pid ?= ?(\d{1,10})$`)
|
||||
|
||||
func handleServerListRequest(conn net.Conn, buffer []byte) {
|
||||
func handleServerListRequest(moduleName string, connIndex uint64, address string, buffer []byte) {
|
||||
index := 9
|
||||
queryGame, index, err := popString(buffer, index)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Invalid queryGame")
|
||||
logging.Error(moduleName, "Invalid queryGame")
|
||||
return
|
||||
}
|
||||
|
||||
gameName, index, err := popString(buffer, index)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Invalid gameName")
|
||||
logging.Error(moduleName, "Invalid gameName")
|
||||
|
||||
return
|
||||
}
|
||||
challenge, index, err := popBytes(buffer, index, 8)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Invalid challenge")
|
||||
return
|
||||
}
|
||||
filter, index, err := popString(buffer, index)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Invalid filter")
|
||||
return
|
||||
}
|
||||
fields, index, err := popString(buffer, index)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Invalid fields")
|
||||
return
|
||||
}
|
||||
options, index, err := popUint32(buffer, index)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Invalid options")
|
||||
logging.Error(moduleName, "Invalid challenge")
|
||||
return
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "queryGame:", aurora.Cyan(queryGame).String(), "- gameName:", aurora.Cyan(gameName).String(), "- filter:", aurora.Cyan(filter).String(), "- fields:", aurora.Cyan(fields).String())
|
||||
filter, index, err := popString(buffer, index)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Invalid filter")
|
||||
return
|
||||
}
|
||||
|
||||
fields, index, err := popString(buffer, index)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Invalid fields")
|
||||
return
|
||||
}
|
||||
|
||||
options, index, err := popUint32(buffer, index)
|
||||
if err != nil {
|
||||
logging.Error(moduleName, "Invalid options")
|
||||
return
|
||||
}
|
||||
|
||||
logging.Info(moduleName, "Server list:", aurora.Cyan(queryGame), "/", aurora.Cyan(filter[:min(len(filter), 200)]))
|
||||
|
||||
gameInfo := common.GetGameInfoByName(gameName)
|
||||
if gameInfo == nil {
|
||||
|
|
@ -124,7 +128,7 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
}
|
||||
|
||||
var output []byte
|
||||
for _, s := range strings.Split(strings.Split(conn.RemoteAddr().String(), ":")[0], ".") {
|
||||
for _, s := range strings.Split(strings.Split(address, ":")[0], ".") {
|
||||
val, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -134,35 +138,25 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
}
|
||||
|
||||
var fieldList []string
|
||||
for _, field := range strings.Split(fields, "\\") {
|
||||
if len(field) == 0 || field == " " {
|
||||
continue
|
||||
if options&NoServerListOption == 0 {
|
||||
for _, field := range strings.Split(fields, "\\") {
|
||||
if len(field) == 0 || field == " " {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip private fields
|
||||
if field == "publicip" || field == "publicport" || strings.HasPrefix(field, "localip") || field == "localport" {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldList = append(fieldList, field)
|
||||
}
|
||||
|
||||
// Skip private fields
|
||||
if field == "publicip" || field == "publicport" || strings.HasPrefix(field, "localip") || field == "localport" {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldList = append(fieldList, field)
|
||||
} else {
|
||||
filter = ""
|
||||
}
|
||||
|
||||
if options&NoServerListOption != 0 || len(fieldList) == 0 {
|
||||
// The client requests its own public IP and game port
|
||||
logging.Info(ModuleName, "Reply without server list", aurora.Cyan(conn.RemoteAddr()))
|
||||
|
||||
// The default game port 6500
|
||||
output = binary.BigEndian.AppendUint16(output, 6500)
|
||||
|
||||
// Write the encrypted reply
|
||||
conn.Write(common.EncryptTypeX([]byte(gameInfo.SecretKey), challenge, output))
|
||||
return
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "Reply with server list", aurora.Cyan(conn.RemoteAddr()))
|
||||
|
||||
// The client's port
|
||||
port, err := strconv.Atoi(strings.Split(conn.RemoteAddr().String(), ":")[1])
|
||||
port, err := strconv.Atoi(strings.Split(address, ":")[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -176,14 +170,16 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
}
|
||||
output = append(output, 0x00) // Zero length string to end the list
|
||||
|
||||
callerPublicIP, _ := common.IPFormatToString(conn.RemoteAddr().String())
|
||||
callerPublicIP, _ := common.IPFormatToString(address)
|
||||
|
||||
var servers []map[string]string
|
||||
if match := regexSelfLookup.FindStringSubmatch(filter); match != nil {
|
||||
// Self lookup is handled differently
|
||||
servers = filterSelfLookup(qr2.GetSessionServers(), queryGame, match[1], callerPublicIP)
|
||||
} else {
|
||||
servers = filterServers(qr2.GetSessionServers(), queryGame, filter, callerPublicIP)
|
||||
servers := []map[string]string{}
|
||||
if options&NoServerListOption == 0 && filter != "" && filter != " " && filter != "0" {
|
||||
if match := regexSelfLookup.FindStringSubmatch(filter); match != nil {
|
||||
// Self lookup is handled differently
|
||||
servers = filterSelfLookup(moduleName, qr2.GetSessionServers(), queryGame, match[1], callerPublicIP)
|
||||
} else {
|
||||
servers = filterServers(moduleName, qr2.GetSessionServers(), queryGame, filter, callerPublicIP)
|
||||
}
|
||||
}
|
||||
|
||||
for _, server := range servers {
|
||||
|
|
@ -201,7 +197,7 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
|
||||
var publicip string
|
||||
if publicip, exists = server["publicip"]; !exists {
|
||||
logging.Error(ModuleName, "Server exists without public IP")
|
||||
logging.Error(moduleName, "Server exists without public IP")
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -209,7 +205,7 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
// Use the real public IP if it matches the caller's
|
||||
ip, err := strconv.ParseInt(publicip, 10, 32)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid public IP value:", aurora.Cyan(publicip))
|
||||
logging.Error(moduleName, "Server has invalid public IP value:", aurora.Cyan(publicip))
|
||||
}
|
||||
|
||||
flagsBuffer = binary.BigEndian.AppendUint32(flagsBuffer, uint32(ip))
|
||||
|
|
@ -219,19 +215,19 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
if !exists {
|
||||
// Fall back to local port if public port doesn't exist
|
||||
if port, exists = server["localport"]; !exists {
|
||||
logging.Error(ModuleName, "Server exists without port (publicip =", aurora.Cyan(publicip).String()+")")
|
||||
logging.Error(moduleName, "Server exists without port (publicip =", aurora.Cyan(publicip).String()+")")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
portValue, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid port value:", aurora.Cyan(port))
|
||||
logging.Error(moduleName, "Server has invalid port value:", aurora.Cyan(port))
|
||||
continue
|
||||
}
|
||||
|
||||
if portValue < 1024 {
|
||||
logging.Error(ModuleName, "Server uses reserved port:", aurora.Cyan(portValue))
|
||||
logging.Error(moduleName, "Server uses reserved port:", aurora.Cyan(portValue))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +241,7 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
// localip is written like "192.168.255.255" for example, so it needs to be parsed
|
||||
ipSplit := strings.Split(localip0, ".")
|
||||
if len(ipSplit) != 4 {
|
||||
logging.Error(ModuleName, "Server has invalid local IP:", aurora.Cyan(localip0))
|
||||
logging.Error(moduleName, "Server has invalid local IP:", aurora.Cyan(localip0))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -260,7 +256,7 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
}
|
||||
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid local IP value:", aurora.Cyan(localip0))
|
||||
logging.Error(moduleName, "Server has invalid local IP value:", aurora.Cyan(localip0))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -268,7 +264,7 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
if localport, exists := server["localport"]; exists {
|
||||
portValue, err = strconv.ParseUint(localport, 10, 16)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid local port value:", aurora.Cyan(localport))
|
||||
logging.Error(moduleName, "Server has invalid local port value:", aurora.Cyan(localport))
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -282,13 +278,13 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
// Regular server, hide the public IP until match reservation is made
|
||||
var searchIDStr string
|
||||
if searchIDStr, exists = server["+searchid"]; !exists {
|
||||
logging.Error(ModuleName, "Server exists without search ID")
|
||||
logging.Error(moduleName, "Server exists without search ID")
|
||||
continue
|
||||
}
|
||||
|
||||
searchID, err := strconv.ParseInt(searchIDStr, 10, 64)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid search ID value:", aurora.Cyan(searchIDStr))
|
||||
logging.Error(moduleName, "Server has invalid search ID value:", aurora.Cyan(searchIDStr))
|
||||
}
|
||||
|
||||
// Append low value as public IP
|
||||
|
|
@ -326,19 +322,21 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
// Server with 0 flags and IP of 0xffffffff terminates the list
|
||||
output = append(output, []byte{0x00, 0xff, 0xff, 0xff, 0xff}...)
|
||||
if options&NoServerListOption == 0 {
|
||||
// Server with 0 flags and IP of 0xffffffff terminates the list
|
||||
output = append(output, []byte{0x00, 0xff, 0xff, 0xff, 0xff}...)
|
||||
}
|
||||
|
||||
// Write the encrypted reply
|
||||
conn.Write(common.EncryptTypeX([]byte(gameInfo.SecretKey), challenge, output))
|
||||
common.SendPacket(ServerName, connIndex, common.EncryptTypeX([]byte(gameInfo.SecretKey), challenge, output))
|
||||
}
|
||||
|
||||
func handleSendMessageRequest(conn net.Conn, buffer []byte) {
|
||||
func handleSendMessageRequest(moduleName string, connIndex uint64, address string, buffer []byte) {
|
||||
// Read search ID from buffer
|
||||
searchID := uint64(binary.BigEndian.Uint32(buffer[3:7]))
|
||||
searchID |= uint64(binary.BigEndian.Uint16(buffer[7:9])) << 32
|
||||
|
||||
logging.Notice(ModuleName, "Send message from", aurora.BrightCyan(conn.RemoteAddr()), "to", aurora.Cyan(fmt.Sprintf("%012x", searchID)))
|
||||
logging.Notice(moduleName, "Send message from to", aurora.Cyan(fmt.Sprintf("%012x", searchID)))
|
||||
|
||||
go qr2.SendClientMessage(conn.RemoteAddr().String(), searchID, buffer[9:])
|
||||
go qr2.SendClientMessage(address, searchID, buffer[9:])
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user