mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-09-14 11:35:50 -05:00
ServerBrowser: Use random search ID rather than public IP
This commit is contained in:
@@ -2,7 +2,6 @@ package serverbrowser
|
||||
|
||||
import (
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"regexp"
|
||||
"wwfc/logging"
|
||||
"wwfc/serverbrowser/filter"
|
||||
)
|
||||
@@ -15,8 +14,6 @@ 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)
|
||||
|
||||
var regexSelfLookup = regexp.MustCompile(`^dwc_pid ?= ?(\d{1,10})$`)
|
||||
|
||||
func filterServers(servers []map[string]string, queryGame string, expression string, publicIP string) []map[string]string {
|
||||
if match := regexSelfLookup.FindStringSubmatch(expression); match != nil {
|
||||
dwc_pid := match[1]
|
||||
@@ -36,7 +33,8 @@ func filterServers(servers []map[string]string, queryGame string, expression str
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "Self lookup from", aurora.Cyan(dwc_pid), "ok")
|
||||
return []map[string]string{server}
|
||||
filtered = []map[string]string{server}
|
||||
break
|
||||
}
|
||||
|
||||
// Alternatively, if the server hasn't set its dwc_pid field yet, we return servers matching the request's public IP.
|
||||
@@ -96,3 +94,47 @@ func filterServers(servers []map[string]string, queryGame string, expression str
|
||||
logging.Info(ModuleName, "Matched", aurora.BrightCyan(len(filtered)), "servers")
|
||||
return filtered
|
||||
}
|
||||
|
||||
func filterSelfLookup(servers []map[string]string, queryGame string, dwc_pid string, publicIP string) []map[string]string {
|
||||
filtered := []map[string]string{}
|
||||
|
||||
// Search for where the profile ID matches
|
||||
for _, server := range servers {
|
||||
if server["gamename"] != queryGame {
|
||||
continue
|
||||
}
|
||||
|
||||
if server["dwc_pid"] == dwc_pid {
|
||||
if server["publicip"] != publicIP {
|
||||
logging.Error(ModuleName, "Self lookup", aurora.Cyan(dwc_pid), "from wrong IP")
|
||||
return []map[string]string{}
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "Self lookup from", aurora.Cyan(dwc_pid), "ok")
|
||||
return []map[string]string{server}
|
||||
}
|
||||
|
||||
// Alternatively, if the server hasn't set its dwc_pid field yet, we return servers matching the request's public IP.
|
||||
// If multiple servers exist with the same public IP then the client will use the one with the matching port.
|
||||
// This is a bit of a hack to speed up server creation.
|
||||
if _, ok := server["dwc_pid"]; !ok && server["publicip"] == publicIP {
|
||||
// Create a copy of the map with some values changed
|
||||
newServer := map[string]string{}
|
||||
for k, v := range server {
|
||||
newServer[k] = v
|
||||
}
|
||||
newServer["dwc_pid"] = dwc_pid
|
||||
newServer["dwc_mtype"] = "0"
|
||||
newServer["dwc_mver"] = "0"
|
||||
filtered = append(filtered, newServer)
|
||||
}
|
||||
}
|
||||
|
||||
if len(filtered) == 0 {
|
||||
logging.Error(ModuleName, "Could not find server with dwc_pid", aurora.Cyan(dwc_pid))
|
||||
return []map[string]string{}
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "Self lookup for", aurora.Cyan(dwc_pid), "matched", aurora.BrightCyan(len(filtered)), "servers via public IP")
|
||||
return filtered
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/logrusorgru/aurora/v3"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"wwfc/common"
|
||||
@@ -51,6 +52,8 @@ func popUint32(buffer []byte, index int) (uint32, int) {
|
||||
return binary.BigEndian.Uint32(buffer[index:]), index + 4
|
||||
}
|
||||
|
||||
var regexSelfLookup = regexp.MustCompile(`^dwc_pid ?= ?(\d{1,10})$`)
|
||||
|
||||
func handleServerListRequest(conn net.Conn, buffer []byte) {
|
||||
index := 9
|
||||
queryGame, index := popString(buffer, index)
|
||||
@@ -84,6 +87,11 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip private fields
|
||||
if field == "publicip" || field == "publicport" || strings.HasPrefix(field, "localip") || field == "localport" {
|
||||
continue
|
||||
}
|
||||
|
||||
fieldList = append(fieldList, field)
|
||||
}
|
||||
|
||||
@@ -116,8 +124,15 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
||||
}
|
||||
output = append(output, 0x00) // Zero length string to end the list
|
||||
|
||||
publicIP, _ := common.IPFormatToString(conn.RemoteAddr().String())
|
||||
servers := filterServers(qr2.GetSessionServers(), queryGame, filter, publicIP)
|
||||
callerPublicIP, _ := common.IPFormatToString(conn.RemoteAddr().String())
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
for _, server := range servers {
|
||||
var flags byte
|
||||
@@ -138,82 +153,107 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
||||
continue
|
||||
}
|
||||
|
||||
ip, err := strconv.ParseInt(publicip, 10, 32)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid public IP value:", aurora.Cyan(publicip))
|
||||
}
|
||||
|
||||
flagsBuffer = binary.BigEndian.AppendUint32(flagsBuffer, uint32(ip))
|
||||
|
||||
var port string
|
||||
port, exists = server["publicport"]
|
||||
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()+")")
|
||||
continue
|
||||
if publicip == callerPublicIP {
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
||||
portValue, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid port value:", aurora.Cyan(port))
|
||||
continue
|
||||
}
|
||||
flagsBuffer = binary.BigEndian.AppendUint32(flagsBuffer, uint32(ip))
|
||||
|
||||
flags |= NonstandardPortFlag
|
||||
flagsBuffer = binary.BigEndian.AppendUint16(flagsBuffer, uint16(portValue))
|
||||
var port string
|
||||
port, exists = server["publicport"]
|
||||
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()+")")
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Use the first local IP if it exists, this is used to skip natneg if multiple players are on the same network
|
||||
if localip0, exists := server["localip0"]; exists {
|
||||
flags |= PrivateIPFlag
|
||||
|
||||
// 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))
|
||||
portValue, err := strconv.ParseUint(port, 10, 16)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid port value:", aurora.Cyan(port))
|
||||
continue
|
||||
}
|
||||
|
||||
err = nil
|
||||
for _, s := range ipSplit {
|
||||
val, err := strconv.ParseUint(s, 10, 8)
|
||||
if err != nil {
|
||||
break
|
||||
if portValue < 1024 {
|
||||
logging.Error(ModuleName, "Server uses reserved port:", aurora.Cyan(portValue))
|
||||
continue
|
||||
}
|
||||
|
||||
flags |= NonstandardPortFlag
|
||||
flagsBuffer = binary.BigEndian.AppendUint16(flagsBuffer, uint16(portValue))
|
||||
|
||||
// Use the first local IP if it exists
|
||||
if localip0, exists := server["localip0"]; exists {
|
||||
flags |= PrivateIPFlag
|
||||
|
||||
// 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))
|
||||
continue
|
||||
}
|
||||
|
||||
flagsBuffer = append(flagsBuffer, byte(val))
|
||||
err = nil
|
||||
for _, s := range ipSplit {
|
||||
val, err := strconv.ParseUint(s, 10, 8)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
flagsBuffer = append(flagsBuffer, byte(val))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid local IP value:", aurora.Cyan(localip0))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid local IP value:", aurora.Cyan(localip0))
|
||||
continue
|
||||
}
|
||||
}
|
||||
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))
|
||||
continue
|
||||
}
|
||||
|
||||
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))
|
||||
flags |= NonstandardPrivatePortFlag
|
||||
flagsBuffer = binary.BigEndian.AppendUint16(flagsBuffer, uint16(portValue))
|
||||
}
|
||||
|
||||
// Just a dummy IP? This is taken from dwc_network_server_emulator
|
||||
// TODO: Check if this is actually needed
|
||||
flags |= ICMPIPFlag
|
||||
flagsBuffer = append(flagsBuffer, []byte{0, 0, 0, 0}...)
|
||||
} else {
|
||||
// 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")
|
||||
continue
|
||||
}
|
||||
|
||||
flags |= NonstandardPrivatePortFlag
|
||||
flagsBuffer = binary.BigEndian.AppendUint16(flagsBuffer, uint16(portValue))
|
||||
searchID, err := strconv.ParseInt(searchIDStr, 10, 64)
|
||||
if err != nil {
|
||||
logging.Error(ModuleName, "Server has invalid search ID value:", aurora.Cyan(searchIDStr))
|
||||
}
|
||||
|
||||
// Append low value as public IP
|
||||
flagsBuffer = binary.BigEndian.AppendUint32(flagsBuffer, uint32(searchID&0xffffffff))
|
||||
// Append high value as public port
|
||||
flags |= NonstandardPortFlag
|
||||
flagsBuffer = binary.BigEndian.AppendUint16(flagsBuffer, uint16((searchID>>32)&0xffff))
|
||||
}
|
||||
|
||||
// Just a dummy IP? This is taken from dwc_network_server_emulator
|
||||
// TODO: Check if this is actually needed
|
||||
flags |= ICMPIPFlag
|
||||
flagsBuffer = append(flagsBuffer, []byte{0, 0, 0, 0}...)
|
||||
|
||||
// Finally, write the server buffer to the output
|
||||
// Append the server buffer to the output
|
||||
output = append(output, flags)
|
||||
output = append(output, flagsBuffer...)
|
||||
|
||||
if (flags & HasKeysFlag) == 0 {
|
||||
// Server does not have keys, so skip them
|
||||
logging.Info(ModuleName, "Wrote server without keys")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -225,11 +265,9 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
||||
output = append(output, []byte(str)...)
|
||||
}
|
||||
|
||||
// Add null terminator so the string will be empty if the field doesn't exist
|
||||
// Add null terminator
|
||||
output = append(output, 0x00)
|
||||
}
|
||||
|
||||
logging.Info(ModuleName, "Wrote server with keys")
|
||||
}
|
||||
|
||||
// Server with 0 flags and IP of 0xffffffff terminates the list
|
||||
@@ -240,10 +278,11 @@ func handleServerListRequest(conn net.Conn, buffer []byte) {
|
||||
}
|
||||
|
||||
func handleSendMessageRequest(conn net.Conn, buffer []byte) {
|
||||
// Read destination IP from buffer
|
||||
destIP := fmt.Sprintf("%d.%d.%d.%d:%d", buffer[3], buffer[4], buffer[5], buffer[6], binary.BigEndian.Uint16(buffer[7:9]))
|
||||
// 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.BrightCyan(destIP).String())
|
||||
logging.Notice(ModuleName, "Send message from", aurora.BrightCyan(conn.RemoteAddr()), "to", aurora.Cyan(fmt.Sprintf("%012x", searchID)))
|
||||
|
||||
qr2.SendClientMessage(destIP, buffer[9:])
|
||||
qr2.SendClientMessage(conn.RemoteAddr().String(), searchID, buffer[9:])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user