From d3bcda6268e1c0c7018fd1268152424d3a7da173 Mon Sep 17 00:00:00 2001 From: a1batross Date: Wed, 20 Jan 2016 00:32:16 +0300 Subject: [PATCH] First blood --- pymaster.py | 116 ++++++++++++++++++++++++++++++++++++++++++++++++ server_entry.py | 30 +++++++++++++ util.py | 2 + 3 files changed, 148 insertions(+) create mode 100644 pymaster.py create mode 100644 server_entry.py create mode 100644 util.py diff --git a/pymaster.py b/pymaster.py new file mode 100644 index 0000000..56664da --- /dev/null +++ b/pymaster.py @@ -0,0 +1,116 @@ +import socket +import random +import struct +import sys +from server_entry import ServerEntry +from util import getAttrOrNone + +UDP_IP = "127.0.0.1" +UDP_PORT = 27010 + +sock = [] + +def logPrint( msg ): + if DEBUG: + print( msg ) + +class PyMaster: + self.serverList = [] + self.sock = [] + + def __init__(self): + sock = socket.socket( socket.AF_INET, socket.SOCK_DGRAM ) + sock.bind( (UDP_IP, UDP_PORT) ) + + logprint("Welcome to PyMaster!") + logprint("I ask you again, are you my master?") + logprint("Running on %s:%i" % UDP_IP, UDP_PORT) + + def serverLoop(self) + data, addr = sock.recvfrom(1024)4 + logprint("received message: %s" % data) + + if data[0] == '1': + self.clientQuery(data, addr); + elif data[0] == 'q' + self.sendChallengeToServer(data, addr); + elif data[0] == '0' + self.addServerToList(data, addr); + elif data[0] == 'b' + self.removeServerFromList(data, addr); + else + logprint("Unknown message: %s from %s" % data, addr) + + def clientQuery(self, data, addr): + data = data.strip('1') + + region = data[0] + queryAddr, rawFilter = data.split('\0') + + rawFilter = rawFilter.split('\\') + queryFilter = namedtuple('QueryFilter', rawFilter[0::2])._make(rawFilter[1::2]) + + #nor = getAttrOrNone(queryFilter, 'nor') + #nand = getAttrOrNone(queryFilter, 'nand') + #dedicated = getAttrOrNone(queryFilter, 'dedicated') + #gamedir = getAttrOrNone(queryFilter, 'gamedir') + #gamemap = getAttrOrNone(queryFilter, 'map') + #linux = getAttrOrNone(queryFilter, 'linux') + #empty = getAttrOrNone(queryFilter, 'empty') + #full = getAttrOrNone(queryFilter, 'full') + #proxy = getAttrOrNone(queryFilter, 'proxy') + #noplayers = getAttrOrNone(queryFilter, 'noplayers') + #white = getAttrOrNone(queryFilter, 'white') + #name = getAttrOrNone(queryFilter, 'name') + #version = getAttrOrNone(queryFilter, 'version_match') + #gameaddr = getAttrOrNone(queryFilter, 'gameaddr') + secure = getAttrOrNone(queryFilter, 'secure') + + packet = '\xff\xff\xff\xff\x66\x0a' + for i in self.serverList: + if( !i.check ): + continue + + if( gamedir != None ): + if( gamedir != i.serverInfo.gamedir): + continue + + # Use pregenerated address string + packet += self.queryAddr + + def sendChallengeToServer(self, data, addr): + # Generate a 32 bit challenge number + challenge = random.randint(0, 2**32-1) + + # Add server to list + self.serverList.append(ServerEntry(addr, challenge)) + + # And send him a challenge + packet = '\xff\xff\xff\xff\x73\x0a' + packet += struct.pack('I', challenge) + socket.sendto(packet, addr) + + def addServerToList(self, data, addr): + # Remove the header + serverInfo = data.strip('\x30\x0a\x5c') + + # Find a server with + for serverEntry in self.serverList: + if (serverEntry.addr == addr): + serverEntry.setInfoString(serverInfo) + + def removeServerFromList(self, data, addr): + for i in self.serverList: + if (i.addr == addr): + self.serverList.remove(i) + +def main( argv = None ): + if argv is None: + argv = sys.argv + + masterMain = PyMaster() + while True: + masterMain.serverLoop() + +if __name__ == "__main__": + sys.exit( main( ) ) \ No newline at end of file diff --git a/server_entry.py b/server_entry.py new file mode 100644 index 0000000..9e0b57d --- /dev/null +++ b/server_entry.py @@ -0,0 +1,30 @@ +from collections import namedtuple +from time import time + +class ServerEntry: + def setInfoString(self, data): + infostring = data.translate(None, '\n\r\0') + split = infostring.split('\\') + self.serverInfo = namedtuple('ServerInfo', split[0::2])._make(split[1::2]) + self.check = int(self.serverInfo.challenge) == self.challenge + + def __init__(self, addr, challenge): + # Address + self.addr = addr + + # Shortcuts for generating query + self.queryAddr = "" + for i in addr[0].split('.'): + self.queryAddr += struct.pack('B', int(i)) + self.queryAddr += struct.pack('H', addr[1]) + + # Random number that server must return + self.challenge = challenge + + # This server is not checked + # So it will not get into queries + self.check = False + + # Remove server after this time. + # This maybe not instant + self.die = time() + 600.0 \ No newline at end of file diff --git a/util.py b/util.py new file mode 100644 index 0000000..475dc7c --- /dev/null +++ b/util.py @@ -0,0 +1,2 @@ +def getAttrOrNone(obj, prop): + return getattr(obj, prop, None) \ No newline at end of file