From ecf8fcf32cb78e80032a23b05769d215a4294711 Mon Sep 17 00:00:00 2001 From: mkwcat Date: Fri, 20 Oct 2023 03:36:51 -0400 Subject: [PATCH] Matchmaking: Handle server list command --- matchmaking/main.go | 7 ++-- matchmaking/server.go | 91 +++++++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/matchmaking/main.go b/matchmaking/main.go index b21a425..94caab9 100644 --- a/matchmaking/main.go +++ b/matchmaking/main.go @@ -79,9 +79,6 @@ func handleRequest(conn net.Conn) { fmt.Printf("Unable to set keepalive - %s", err) } - // log.Printf("%s: Connection established from %s. Sending challenge.", aurora.Green("[NOTICE]"), aurora.Yellow(conn.RemoteAddr())) - // conn.Write([]byte(fmt.Sprintf(`\lc\1\challenge\%s\id\1\final\`, challenge))) - // Here we go into the listening loop for { buffer := make([]byte, 1024) @@ -95,11 +92,13 @@ func handleRequest(conn net.Conn) { switch buffer[2] { case ServerList: + logging.Notice(ModuleName, "Command:", aurora.Yellow("SERVER_LIST").String()) + serverList(conn, buffer) break default: - logging.Notice(ModuleName, "Command:", aurora.Yellow(buffer[2]).String()) + logging.Notice(ModuleName, "Unknown command:", aurora.Yellow(buffer[2]).String()) break } } diff --git a/matchmaking/server.go b/matchmaking/server.go index ed829ef..f85e5ff 100644 --- a/matchmaking/server.go +++ b/matchmaking/server.go @@ -2,6 +2,7 @@ package matchmaking import ( "encoding/binary" + "github.com/logrusorgru/aurora/v3" "net" "strconv" "strings" @@ -9,29 +10,36 @@ import ( "wwfc/logging" ) +func popString(buffer []byte, index int) (string, int) { + str := common.GetString(buffer[index:]) + return str, index + len(str) + 1 +} + +func popBytes(buffer []byte, index int, size int) ([]byte, int) { + return buffer[index : index+size], index + size +} + +func popUint32(buffer []byte, index int) (uint32, int) { + return binary.BigEndian.Uint32(buffer[index:]), index + 4 +} + func serverList(conn net.Conn, buffer []byte) { - logging.Notice(ModuleName, "Received server list command") + const ( + FlagNoServerList = 1 << 1 // 0x02 + FlagAlternateSourceIP = 1 << 3 // 0x08 + FlagLimitResultCount = 1 << 7 // 0x80 + ) index := 9 - queryGame := common.GetString(buffer[index:]) - index += len(queryGame) + 1 - gameName := common.GetString(buffer[index:]) - index += len(gameName) + 1 + queryGame, index := popString(buffer, index) + gameName, index := popString(buffer, index) + challenge, index := popBytes(buffer, index, 8) + filter, index := popString(buffer, index) + fields, index := popString(buffer, index) + flags, index := popUint32(buffer, index) - challenge := buffer[index : index+8] - index += 8 + logging.Notice(ModuleName, "queryGame:", aurora.Cyan(queryGame).String(), "- gameName:", aurora.Cyan(gameName).String(), "- filter:", aurora.Cyan(filter).String(), "- fields:", aurora.Cyan(fields).String()) - filter := common.GetString(buffer[index:]) - index += len(filter) + 1 - fields := common.GetString(buffer[index:]) - index += len(fields) + 1 - - options := binary.BigEndian.Uint32(buffer[index:]) - index += 4 - - logging.Notice(ModuleName, "Values", queryGame, gameName, string(challenge), string(options)) - - // TODO: Find a game if possible, but there is nobody to do that with yet! var output []byte for _, s := range strings.Split(strings.Split(conn.RemoteAddr().String(), ":")[0], ".") { val, err := strconv.Atoi(s) @@ -42,8 +50,49 @@ func serverList(conn net.Conn, buffer []byte) { output = append(output, byte(val)) } - output = binary.BigEndian.AppendUint16(output, 6500) + var fieldList []string + for _, field := range strings.Split(fields, "\\") { + if len(field) == 0 || field == " " { + continue + } - encrypted := common.EncryptTypeX([]byte("9r3Rmy"), challenge, output) - conn.Write(encrypted) + fieldList = append(fieldList, field) + } + + if flags&FlagNoServerList != 0 || len(fieldList) == 0 { + // The client requests its own public IP and game port + logging.Notice(ModuleName, "Reply without server list", aurora.Cyan(conn.RemoteAddr()).String()) + + // The default game port 6500 + output = binary.BigEndian.AppendUint16(output, 6500) + + // Write the encrypted reply + conn.Write(common.EncryptTypeX([]byte("9r3Rmy"), challenge, output)) + return + } + + logging.Notice(ModuleName, "Reply with server list", aurora.Cyan(conn.RemoteAddr()).String()) + + // The client's port + port, err := strconv.Atoi(strings.Split(conn.RemoteAddr().String(), ":")[1]) + if err != nil { + panic(err) + } + output = binary.BigEndian.AppendUint16(output, uint16(port)) + + output = append(output, byte(len(fieldList))) + for _, field := range fieldList { + output = append(output, 0x00) // Value? + output = append(output, []byte(field)...) + output = append(output, 0x00) // String end + } + output = append(output, 0x00) // Zero length string to end the list + + // TODO: Send server list here + + // Server with no flags and -1 tells the client the message has ended + output = append(output, []byte{0x00, 0xff, 0xff, 0xff, 0xff}...) + + // Write the encrypted reply + conn.Write(common.EncryptTypeX([]byte("9r3Rmy"), challenge, output)) }