예제 #1
0
    def __init__(self, sessions, address):
        self.setRawMode()  # We're dealing with binary data so set to raw mode

        self.db = gs_database.GamespyDatabase()

        self.sessions = sessions
        self.address = address
        # Stores any unparsable/incomplete commands until the next
        # rawDataReceived
        self.remaining_message = ""

        self.profileid = 0
        self.gameid = ""

        self.buddies = []
        self.blocked = []

        self.status = ""
        self.statstring = ""
        self.locstring = ""

        self.keepalive = int(time.time())
        self.sesskey = ""

        self.sdkrevision = "0"
    def __init__(self):
        self.sessions = {}

        # Generate a dictionary "secret_key_list" containing the secret game keys associated with their game IDs.
        # The dictionary key will be the game's ID, and the value will be the secret key.
        self.secret_key_list = gs_utils.generate_secret_keys("gslist.cfg")
        #self.log(logging.DEBUG, address, "Generated list of secret game keys...")

        self.db = gs_database.GamespyDatabase()

        GameSpyServerDatabase.register("update_server_list")
        GameSpyServerDatabase.register("delete_server")
예제 #3
0
    def __init__(self, sessions, address):
        self.setRawMode()  # We're dealing with binary data so set to raw mode

        self.db = gs_database.GamespyDatabase()

        self.sessions = sessions
        self.address = address
        self.remaining_message = ""  # Stores any unparsable/incomplete commands until the next rawDataReceived

        self.session = ""
        self.gameid = ""

        self.lid = "0"
예제 #4
0
    def do_GET(self):
        self.server = lambda:None
        self.server.db = gs_database.GamespyDatabase()

        try:
            # conntest server
            self.send_response(200)
            self.send_header("Content-type", "text/html")
            self.send_header("X-Organization", "Nintendo")
            self.send_header("Server", "BigIP")
            self.end_headers()
            self.wfile.write("ok")
        except:
            logger.log(logging.ERROR, "Unknown exception: %s" % traceback.format_exc())
    def start(self):
        try:
            manager_address = dwc_config.get_ip_port('GameSpyManager')
            manager_password = ""

            self.server_manager = GameSpyServerDatabase(
                address=manager_address, authkey=manager_password)
            self.server_manager.connect()

            # Start QR server
            # Accessible to outside connections (use this if you don't know
            # what you're doing)
            address = dwc_config.get_ip_port('GameSpyQRServer')

            self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.socket.bind(address)
            self.socket.setblocking(0)

            logger.log(logging.INFO, "Server is now listening on %s:%s...",
                       address[0], address[1])

            # Dependencies! I don't really like this solution but it's easier
            # than trying to manage it another way.
            server_browser_server = GameSpyServerBrowserServer(self)
            server_browser_server_thread = threading.Thread(
                target=server_browser_server.start)
            server_browser_server_thread.start()

            self.write_queue = Queue.Queue()
            self.db = gs_database.GamespyDatabase()
            threading.Thread(target=self.write_queue_worker).start()

            while True:
                ready = select.select([self.socket], [], [], 15)

                if ready[0]:
                    try:
                        recv_data, address = self.socket.recvfrom(2048)
                        self.handle_packet(self.socket, recv_data, address)
                    except:
                        logger.log(logging.ERROR,
                                   "Failed to handle client: %s",
                                   traceback.format_exc())

                self.keepalive_check()
        except:
            logger.log(logging.ERROR, "Unknown exception: %s",
                       traceback.format_exc())
def handle_ac(handler, addr, post):
    """Handle ac POST request."""
    logger.log(logging.DEBUG, "Ac request to %s from %s:%d",
               handler.path, *addr)
    logger.log(logging.DEBUG, "%s", post)

    action = str(post["action"]).lower()
    command = handler.ac_actions.get(action, handle_ac_action)
    ret = command(handler, gs_database.GamespyDatabase(), addr, post)

    ret.update({"datetime": time.strftime("%Y%m%d%H%M%S")})
    handler.send_response(200)
    handler.send_header("Content-type", "text/plain")
    handler.send_header("NODE", "wifiappe1")

    return utils.dict_to_qs(ret)
예제 #7
0
from gamestats_server_http import GameStatsServer
from register_page import RegPageServer

import gamespy.gs_database as gs_database

import threading


if __name__ == "__main__":
    """Let database initialize before starting any servers.

     This fixes any conflicts where two servers find an uninitialized database
     at the same time and both try to initialize it.
     """

    db = gs_database.GamespyDatabase()
    db.initialize_database()
    db.close()

    servers = [
        GameSpyBackendServer,
        GameSpyQRServer,
        GameSpyProfileServer,
        GameSpyPlayerSearchServer,
        GameSpyGamestatsServer,
        # GameSpyServerBrowserServer,
        GameSpyNatNegServer,
        NasServer,
        Dls1Server,
        InternalStatsServer,
        AdminPageServer,
    def __init__(self, server_address, RequestHandlerClass):
        BaseHTTPServer.HTTPServer.__init__(self, server_address,
                                           RequestHandlerClass)

        self.gamespydb = gs_database.GamespyDatabase()

        self.db = sqlite3.connect('storage.db')
        self.tables = {}
        self.valid_sql_terms = ['LIKE', '=', 'AND', 'OR']

        logger.log(logging.INFO,
                   "Checking for and creating database tables...")

        cursor = self.db.cursor()
        if not self.table_exists('typedata'):
            cursor.execute(
                'CREATE TABLE typedata (tbl TEXT, col TEXT, type TEXT)')
        if not self.table_exists('filepaths'):
            cursor.execute(
                'CREATE TABLE filepaths (fileid INTEGER PRIMARY KEY AUTOINCREMENT, gameid INT, playerid INT, path TEXT)'
            )

        PK = 'INTEGER PRIMARY KEY AUTOINCREMENT'

        self.create_or_alter_table_if_not_exists('g1443_bbdx_player',
                                                 ['recordid', 'stat'],
                                                 [PK, 'INT'], ['int', 'int'])
        self.create_or_alter_table_if_not_exists(
            'g1443_bbdx_info', ['serialid', 'stat', 'message'],
            ['INT', 'INT', 'TEXT'], ['int', 'int', 'unicodeString'])
        self.create_or_alter_table_if_not_exists('g1443_bbdx_search', [
            'recordid', 'song_name', 'creator_name', 'average_rating',
            'serialid', 'filestore', 'is_lyric', 'num_ratings', 'jasrac_code',
            'artist_name'
        ], [
            PK, 'TEXT', 'TEXT', 'REAL', 'INT', 'INT', 'INT', 'INT', 'TEXT',
            'TEXT'
        ], [
            'int', 'asciiString', 'asciiString', 'float', 'int', 'int',
            'boolean', 'int', 'asciiString', 'asciiString'
        ])

        # Mario Kart Wii
        self.create_or_alter_table_if_not_exists(
            'g1687_FriendInfo', ['recordid', 'ownerid', 'info'],
            [PK, 'INT', 'TEXT'], ['int', 'int', 'binaryData'])

        self.create_or_alter_table_if_not_exists(
            'g1687_StoredGhostData',
            ['recordid', 'fileid', 'profile', 'region', 'gameid', 'course'],
            [PK, 'INT', 'INT', 'INT', 'INT', 'INT'],
            ['int', 'int', 'int', 'int', 'int', 'int'])

        # WarioWare DIY
        self.create_or_alter_table_if_not_exists(
            'g2050_contest', ['recordid', 'ownerid', 'm_no', 'm_file_id'],
            [PK, 'INT', 'INT', 'INT'], ['int', 'int', 'int', 'int'])
        self.create_or_alter_table_if_not_exists(
            'g2050_contest_eu', ['recordid', 'ownerid', 'm_no', 'm_file_id'],
            [PK, 'INT', 'INT', 'INT'], ['int', 'int', 'int', 'int'])
        self.create_or_alter_table_if_not_exists(
            'g2050_contest_us', ['recordid', 'ownerid', 'm_no', 'm_file_id'],
            [PK, 'INT', 'INT', 'INT'], ['int', 'int', 'int', 'int'])
        self.create_or_alter_table_if_not_exists('g2050_box', [
            'recordid', 'ownerid', 'm_enable', 'm_type', 'm_index',
            'm_file_id', 'm_header', 'm_file_id___size',
            'm_file_id___create_time', 'm_file_id___downloads'
        ], [
            PK, 'INT', 'INT', 'INT', 'INT', 'INT', 'TEXT', 'INT', 'DATETIME',
            'INT'
        ], [
            'int', 'int', 'boolean', 'int', 'int', 'int', 'binaryData', 'int',
            'dateAndTime', 'int'
        ])
        cursor.execute(
            'CREATE TRIGGER IF NOT EXISTS g2050ti_box AFTER INSERT ON g2050_box BEGIN UPDATE g2050_box SET m_file_id___create_time = strftime(\'%Y-%m-%dT%H:%M:%f\', \'now\'), m_file_id___size = 0, m_file_id___downloads = 0 WHERE recordid = NEW.recordid; END'
        )
        cursor.execute(
            'CREATE TRIGGER IF NOT EXISTS g2050tu_box AFTER UPDATE ON g2050_box BEGIN UPDATE g2050_box SET m_file_id___create_time = strftime(\'%Y-%m-%dT%H:%M:%f\', \'now\') WHERE recordid = NEW.recordid; END'
        )
        self.create_or_alter_table_if_not_exists('g2050_box_us_eu', [
            'recordid', 'ownerid', 'm_enable', 'm_type', 'm_index',
            'm_file_id', 'm_header', 'm_file_id___size',
            'm_file_id___create_time', 'm_file_id___downloads'
        ], [
            PK, 'INT', 'INT', 'INT', 'INT', 'INT', 'TEXT', 'INT', 'DATETIME',
            'INT'
        ], [
            'int', 'int', 'boolean', 'int', 'int', 'int', 'binaryData', 'int',
            'dateAndTime', 'int'
        ])
        cursor.execute(
            'CREATE TRIGGER IF NOT EXISTS g2050ti_box_us_eu AFTER INSERT ON g2050_box_us_eu BEGIN UPDATE g2050_box_us_eu SET m_file_id___create_time = strftime(\'%Y-%m-%dT%H:%M:%f\', \'now\'), m_file_id___size = 0, m_file_id___downloads = 0 WHERE recordid = NEW.recordid; END'
        )
        cursor.execute(
            'CREATE TRIGGER IF NOT EXISTS g2050tu_box_us_eu AFTER UPDATE ON g2050_box_us_eu BEGIN UPDATE g2050_box_us_eu SET m_file_id___create_time = strftime(\'%Y-%m-%dT%H:%M:%f\', \'now\') WHERE recordid = NEW.recordid; END'
        )

        self.create_or_alter_table_if_not_exists('g2649_bbdx_player',
                                                 ['recordid', 'stat'],
                                                 [PK, 'INT'], ['int', 'int'])
        self.create_or_alter_table_if_not_exists(
            'g2649_bbdx_info', ['serialid', 'stat', 'message'],
            ['INT', 'INT', 'TEXT'], ['int', 'int', 'unicodeString'])
        self.create_or_alter_table_if_not_exists('g2649_bbdx_search', [
            'recordid', 'song_name', 'creator_name', 'average_rating',
            'serialid', 'filestore', 'is_lyric', 'num_ratings', 'song_code',
            'artist_name'
        ], [
            PK, 'TEXT', 'TEXT', 'REAL', 'INT', 'INT', 'INT', 'INT', 'TEXT',
            'TEXT'
        ], [
            'int', 'asciiString', 'asciiString', 'float', 'int', 'int',
            'boolean', 'int', 'asciiString', 'asciiString'
        ])

        # Playground
        self.create_or_alter_table_if_not_exists(
            'g2999_tblRegionInfo',
            ['recordid', 'region', 'allowed_regions', 'min_ratings'],
            [PK, 'INT', 'INT', 'INT'], ['int', 'byte', 'int', 'int'])

        # Super Smash Bros. Brawl
        self.create_or_alter_table_if_not_exists(
            'g1658_submit', ['recordid', 'ownerid', 'data'],
            [PK, 'INT', 'INT'], ['int', 'int', 'int'])
        self.create_or_alter_table_if_not_exists(
            'g1658_watching', ['recordid', 'ownerid', 'data'],
            [PK, 'INT', 'INT'], ['int', 'int', 'int'])

        # load column info into memory, unfortunately there's no simple way
        # to check for column-existence so get that data in advance
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
        tabledata = cursor.fetchall()
        for t in tabledata:
            cursor.execute(
                "PRAGMA table_info(%s)" % t[0]
            )  # yeah I know but parameters don't work in pragmas, and inserting table names like that should be safe
            columns = cursor.fetchall()
            self.tables[t[0]] = [c[1] for c in columns]

        self.db.commit()
 def __init__(self, server_address, RequestHandlerClass):
     self.db = gs_database.GamespyDatabase()
     BaseHTTPServer.HTTPServer.__init__(self, server_address,
                                        RequestHandlerClass)
    def __init__(self, server_address, RequestHandlerClass):
        BaseHTTPServer.HTTPServer.__init__(self, server_address,
                                           RequestHandlerClass)

        self.gamespydb = gs_database.GamespyDatabase()

        self.db = sqlite3.connect('storage.db')
        self.tables = {}

        cursor = self.db.cursor()

        if not self.table_exists('typedata'):
            cursor.execute(
                'CREATE TABLE typedata (tbl TEXT, col TEXT, type TEXT)')

        if not self.table_exists('g2050_box_us_eu'):
            cursor.execute(
                'CREATE TABLE g2050_box_us_eu (recordid INTEGER PRIMARY KEY AUTOINCREMENT, ownerid INT, m_enable INT, m_type INT, m_index INT, m_file_id INT, m_header TEXT, m_file_id___size INT, m_file_id___create_time DATETIME, m_file_id___downloads INT)'
            )
            cursor.execute(
                'INSERT INTO typedata VALUES ("g2050_box_us_eu", "recordid", "intValue"), ("g2050_box_us_eu", "ownerid", "intValue"), ("g2050_box_us_eu", "m_enable", "booleanValue"), ("g2050_box_us_eu", "m_type", "intValue"), ("g2050_box_us_eu", "m_index", "intValue"), ("g2050_box_us_eu", "m_file_id", "intValue"), ("g2050_box_us_eu", "m_header", "binaryDataValue"), ("g2050_box_us_eu", "m_file_id___size", "intValue"), ("g2050_box_us_eu", "m_file_id___create_time", "dateAndTimeValue"), ("g2050_box_us_eu", "m_file_id___downloads", "intValue")'
            )
            cursor.execute(
                'CREATE TRIGGER g2050ti_box_us_eu AFTER INSERT ON g2050_box_us_eu BEGIN UPDATE g2050_box_us_eu SET m_file_id___create_time = date(\'now\'), m_file_id___size = 0, m_file_id___downloads = 0 WHERE recordid = NEW.recordid; END'
            )
            cursor.execute(
                'CREATE TRIGGER g2050tu_box_us_eu AFTER UPDATE ON g2050_box_us_eu BEGIN UPDATE g2050_box_us_eu SET m_file_id___create_time = date(\'now\') WHERE recordid = NEW.recordid; END'
            )
        if not self.table_exists('g2050_contest_us'):
            cursor.execute(
                'CREATE TABLE g2050_contest_us (recordid INTEGER PRIMARY KEY AUTOINCREMENT, ownerid INT, m_no INT, m_file_id INT)'
            )
            cursor.execute(
                'INSERT INTO typedata VALUES ("g2050_contest_us", "recordid", "intValue"), ("g2050_contest_us", "ownerid", "intValue"), ("g2050_contest_us", "m_no", "intValue"), ("g2050_contest_us", "m_file_id", "intValue")'
            )

        if not self.table_exists('g2649_bbdx_player'):
            cursor.execute(
                'CREATE TABLE g2649_bbdx_player (recordid INTEGER PRIMARY KEY AUTOINCREMENT, stat INT)'
            )
            cursor.execute(
                'INSERT INTO typedata VALUES ("g2649_bbdx_player", "recordid", "intValue"), ("g2649_bbdx_player", "stat", "intValue")'
            )
        if not self.table_exists('g2649_bbdx_info'):
            cursor.execute(
                'CREATE TABLE g2649_bbdx_info (serialid INT, stat INT, message TEXT)'
            )
            cursor.execute(
                'INSERT INTO typedata VALUES ("g2649_bbdx_info", "serialid", "intValue"), ("g2649_bbdx_info", "stat", "intValue"), ("g2649_bbdx_info", "message", "unicodeStringValue")'
            )
        if not self.table_exists('g2649_bbdx_search'):
            cursor.execute(
                'CREATE TABLE g2649_bbdx_search (recordid INTEGER PRIMARY KEY AUTOINCREMENT, song_name TEXT, creator_name TEXT, average_rating REAL, serialid INT, filestore INT, is_lyric INT, num_ratings INT)'
            )
            cursor.execute(
                'INSERT INTO typedata VALUES ("g2649_bbdx_search", "recordid", "intValue"), ("g2649_bbdx_search", "song_name", "asciiStringValue"), ("g2649_bbdx_search", "creator_name", "asciiStringValue"), ("g2649_bbdx_search", "average_rating", "floatValue"), ("g2649_bbdx_search", "serialid", "intValue"), ("g2649_bbdx_search", "filestore", "intValue"), ("g2649_bbdx_search", "is_lyric", "booleanValue"), ("g2649_bbdx_search", "num_ratings", "intValue")'
            )

        # load column info into memory, unfortunately there's no simple way
        # to check for column-existence so get that data in advance
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
        tabledata = cursor.fetchall()
        for t in tabledata:
            cursor.execute(
                "PRAGMA table_info(%s)" % t[0]
            )  # yeah I know but parameters don't work in pragmas, and inserting table names like that should be safe
            columns = cursor.fetchall()
            columndata = []
            for c in columns:
                columndata.append(c[1])
            self.tables[t[0]] = columndata

        self.db.commit()
예제 #11
0
    def __init__(self, address):
        self.setRawMode()
        self.db = gs_database.GamespyDatabase()

        self.address = address
        self.leftover = ""
    def handle_packet(self, socket, recv_data, address):
        db = gs_database.GamespyDatabase()
        time.sleep(0.05)

        # Tetris DS overlay 10 @ 02144184 - Handle responses back to server
        # Tetris DS overlay 10 @ 02144184 - Handle responses back to server
        #
        # After some more packet inspection, it seems the format goes something like this:
        # - All server messages seem to always start with \xfe\xfd.
        # - The first byte from the client (or third byte from the server) is a command.
        # - Bytes 2 - 5 from the client is some kind of ID. This will have to be inspected later. I believe it's a
        # session-like ID because the number changes between connections. Copying the client's ID might be enough.
        #
        # The above was as guessed.
        # The code in Tetris DS (overlay 10) @ 0216E974 handles the network command creation.
        # R1 contains the command to be sent to the server.
        # R2 contains a pointer to some unknown integer that gets written after the command.
        #
        # - Commands
        #   Commands range from 0x00 to 0x09 (for client only at least?) (Tetris DS overlay 10 @ 0216DDCC)
        #
        #   CLIENT:
        #       0x01 - Response (Tetris DS overlay 10 @ 216DCA4)
        #           Sends back base64 of RC4 encrypted string that was gotten from the server's 0x01.
        #
        #       0x03 - Send client state? (Tetris DS overlay 10 @ 216DA30)
        #           Data sent:
        #           1) Loop for each localip available on the system, write as localip%d\x00(local ip)
        #           2) localport\x00(local port)
        #           3) natneg (either 0 or 1)
        #           4) ONLY IF STATE CHANGED: statechanged\x00(state) (Possible values: 0, 1, 2, 3)
        #           5) gamename\x00(game name)
        #           6) ONLY IF PUBLIC IP AND PORT ARE AVAILABLE: publicip\x00(public ip)
        #           7) ONLY IF PUBLIC IP AND PORT ARE AVAILABLE: publicport\x00(public port)
        #
        #           if statechanged != 2:
        #               Write various other data described here: http://docs.poweredbygamespy.com/wiki/Query_and_Reporting_Implementation
        #
        #       0x07 - Unknown, related to server's 0x06 (returns value sent from server)
        #
        #       0x08 - Keep alive? Sent after 0x03
        #
        #       0x09 - Availability check
        #
        #   SERVER:
        #       0x01 - Unknown
        #           Data sent:
        #           8 random ASCII characters (?) followed by the public IP and port of the client as a hex string
        #
        #       0x06 - Unknown
        #           First 4 bytes is some kind of id? I believe it's a unique identifier for the data being sent,
        #           seeing how the server can send the same IP information many times in a row. If the IP information has
        #           already been parsed then it doesn't waste time handling it.
        #
        #           After that is a "SBCM" section which is 0x14 bytes in total.
        #           SBCM information gets parsed at 2141A0C in Tetris DS overlay 10.
        #           Seems to contain IP address information.
        #
        #           The SBCM seems to contain a little information that must be parsed before.
        #           After the SBCM:
        #               \x03\x00\x00\x00 - Always the same?
        #               \x01 - Found player?
        #               \x04 - Unknown
        #               (2 bytes) - Unknown. Port?
        #               (4 bytes) - Player's IP
        #               (4 bytes) - Unknown. Some other IP? Remote server IP?
        #               \x00\x00\x00\x00 - Unknown but seems to get checked
        #
        #           Another SBCM, after a player has been found and attempting to start a game:
        #               \x03\x00\x00\x00 - Always the same?
        #               \x05 - Connecting to player?
        #               \x00 - Unknown
        #               (2 bytes) - Unknown. Port? Same as before.
        #               (4 bytes) - Player's IP
        #               (4 bytes) - Unknown. Some other IP? Remote server IP?
        #
        #       0x0a - Response to 0x01
        #           Gets sent after receiving 0x01 from the client. So, server 0x01 -> client 0x01 -> server 0x0a.
        #           Has no other data besides the client ID.
        #
        #  - \xfd\xfc commands get passed directly between the other player(s)?
        #
        #
        # Open source version of GameSpy found here: https://github.com/sfcspanky/Openspy-Core/tree/master/qr
        # Use as reference.

        if recv_data[0] != '\x09':
            # Don't add a session if the client is trying to check if the game is available or not
            session_id = struct.unpack("<I", recv_data[1:5])[0]
            session_id_raw = recv_data[1:5]
            if session_id not in self.sessions:
                # Found a new session, add to session list
                self.sessions[session_id] = self.Session(address)
                self.sessions[session_id].session = session_id
                self.sessions[session_id].keepalive = int(time.time())

        # Handle commands
        if recv_data[0] == '\x00': # Query
            self.log(logging.DEBUG, address, "NOT IMPLEMENTED! Received query from %s:%s... %s" % (address[0], address[1], recv_data[5:]))

        elif recv_data[0] == '\x01': # Challenge
            self.log(logging.DEBUG, address, "Received challenge from %s:%s... %s" % (address[0], address[1], recv_data[5:]))

            # Prepare the challenge sent from the server to be compared
            challenge = gs_utils.prepare_rc4_base64(self.sessions[session_id].secretkey, self.sessions[session_id].challenge)

            # Compare challenge
            client_challenge = recv_data[5:-1]
            if client_challenge == challenge:
                # Challenge succeeded

                # Send message back to client saying it was accepted
                packet = bytearray([0xfe, 0xfd, 0x0a]) # Send client registered command
                packet.extend(session_id_raw) # Get the session ID
                socket.sendto(packet, address)
                self.log(logging.DEBUG, address, "Sent client registered to %s:%s..." % (address[0], address[1]))
            else:
                # Failed the challenge, request another during the next heartbeat
                self.sessions[session_id].sent_challenge = False

        elif recv_data[0] == '\x02': # Echo
            self.log(logging.DEBUG, address, "NOT IMPLEMENTED! Received echo from %s:%s... %s" % (address[0], address[1], recv_data[5:]))

        elif recv_data[0] == '\x03': # Heartbeat
            data = recv_data[5:]
            self.log(logging.DEBUG, address, "Received heartbeat from %s:%s... %s" % (address[0], address[1], data))

            # Parse information from heartbeat here
            d = data.rstrip('\0').split('\0')

            # It may be safe to ignore "unknown" keys because the proper key names get filled in later...
            k = {}
            for i in range(0, len(d), 2):
                #self.log(logging.DEBUG, address, "%s = %s" % (d[i], d[i+1]))
                k[d[i]] = d[i+1]

            if "gamename" in k:
                self.sessions[session_id].secretkey = self.secret_key_list[k['gamename']]
                #print "Got secret key %s for %s" % (self.sessions[session_id].secretkey, k['gamename'])

            if self.sessions[session_id].playerid == 0 and "dwc_pid" in k:
                # Get the player's id and then query the profile to figure out what console they are on.
                # The endianness of some server data depends on the endianness of the console, so we must be able
                # to account for that.
                self.sessions[session_id].playerid = int(k['dwc_pid'])
                profile = db.get_profile_from_profileid(self.sessions[session_id].playerid)

                if "console" in profile:
                    self.sessions[session_id].console = profile['console']


            if self.sessions[session_id].sent_challenge == False:
                addr_hex =  ''.join(["%02X" % int(x) for x in address[0].split('.')])
                port_hex = "%04X" % int(address[1])
                server_challenge = utils.generate_random_str(8) + addr_hex + port_hex

                self.sessions[session_id].challenge = server_challenge

                packet = bytearray([0xfe, 0xfd, 0x01]) # Send challenge command
                packet.extend(session_id_raw) # Get the session ID
                packet.extend(server_challenge)
                packet.extend('\x00')

                socket.sendto(packet, address)
                self.log(logging.DEBUG, address, "Sent challenge to %s:%s..." % (address[0], address[1]))

                self.sessions[session_id].sent_challenge = True

            if 'publicip' in k and k['publicip'] == "0": #and k['dwc_hoststate'] == "2": # When dwc_hoststate == 2 then it doesn't send an IP, so calculate it ourselves
                if self.sessions[session_id].console != 0:
                    k['publicip'] = str(ctypes.c_int32(utils.get_int_be(bytearray([int(x) for x in address[0].split('.')]), 0)).value) # Wii
                else:
                    k['publicip'] = str(ctypes.c_int32(utils.get_int(bytearray([int(x) for x in address[0].split('.')]), 0)).value) # DS

            if "statechanged" in k:
                if k['statechanged'] == "2": # Close server
                    self.server_manager.delete_server(k['gamename'] , session_id)

                    if session_id in self.sessions:
                        self.sessions.pop(session_id)
                else: #if k['statechanged'] == "1": # Create server
                    #if k['publicport'] != "0" and k['publicip'] != "0":
                        # dwc_mtype controls what kind of server query we're looking for.
                        # dwc_mtype = 0 is used when looking for a matchmaking game.
                        # dwc_mtype = 1 is unknown.
                        # dwc_mtype = 2 is used when hosting a friends only game (possibly other uses too).
                        # dwc_mtype = 3 is used when looking for a friends only game (possibly other uses too).

                        # Some memory could be saved by clearing out any unwanted fields from k before sending.
                    self.server_manager.update_server_list(k['gamename'], session_id, k, self.sessions[session_id].console)._getvalue()

                    if session_id in self.sessions:
                        self.sessions[session_id].gamename = k['gamename']


        elif recv_data[0] == '\x04': # Add Error
            self.log(logging.WARNING, address, "NOT IMPLEMENTED! Received add error from %s:%s... %s" % (address[0], address[1], recv_data[5:]))

        elif recv_data[0] == '\x05': # Echo Response
            self.log(logging.WARNING, address, "NOT IMPLEMENTED! Received echo response from %s:%s... %s" % (address[0], address[1], recv_data[5:]))

        elif recv_data[0] == '\x06': # Client Message
            self.log(logging.WARNING, address, "NOT IMPLEMENTED! Received echo from %s:%s... %s" % (address[0], address[1], recv_data[5:]))

        elif recv_data[0] == '\x07': # Client Message Ack
            #self.log(logging.WARNING, address, "NOT IMPLEMENTED! Received client message ack from %s:%s... %s" % (address[0], address[1], recv_data[5:]))
            self.log(logging.DEBUG, address, "Received client message ack from %s:%s..." % (address[0], address[1]))

        elif recv_data[0] == '\x08': # Keep Alive
            self.log(logging.DEBUG, address, "Received keep alive from %s:%s..." % (address[0], address[1]))
            self.sessions[session_id].keepalive = int(time.time())

        elif recv_data[0] == '\x09': # Available
            # Availability check only sent to *.available.gs.nintendowifi.net
            self.log(logging.DEBUG, address, "Received availability request for '%s' from %s:%s..." % (recv_data[5: -1], address[0], address[1]))
            socket.sendto(bytearray([0xfe, 0xfd, 0x09, 0x00, 0x00, 0x00, 0x00]), address)

        elif recv_data[0] == '\x0a': # Client Registered
            # Only sent to client, never received?
            self.log(logging.WARNING, address, "NOT IMPLEMENTED! Received client registered from %s:%s... %s" % (address[0], address[1], recv_data[5:]))

        else:
            self.log(logging.ERROR, address, "Unknown request from %s:%s:" % (address[0], address[1]))
            self.log(logging.DEBUG, address, utils.pretty_print_hex(recv_data))
    def do_POST(self):
        self.server = lambda: None
        self.server.db = gs_database.GamespyDatabase()

        try:
            length = int(self.headers['content-length'])
            post = self.str_to_dict(self.rfile.read(length))
            if self.client_address[0] == '127.0.0.1':
                client_address = (self.headers.get('x-forwarded-for',
                                                   self.client_address[0]),
                                  self.client_address[1])
            else:
                client_address = self.client_address

            post['ipaddr'] = client_address[0]

            if self.path == "/ac":
                logger.log(logging.DEBUG, "Request to %s from %s", self.path,
                           client_address)
                logger.log(logging.DEBUG, post)
                ret = {"datetime": time.strftime("%Y%m%d%H%M%S"), "retry": "0"}
                action = post["action"]
                self.send_response(200)
                self.send_header("Content-type", "text/plain")
                self.send_header("NODE", "wifiappe1")

                if action == "acctcreate":
                    # TODO: test for duplicate accounts
                    if self.server.db.is_ip_banned(post):
                        logger.log(
                            logging.DEBUG,
                            "acctcreate denied for banned user " + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3913",
                            "locator": "gamespy.com",
                            "retry": "1",
                            "reason": "User banned."
                        }
                    else:
                        ret["returncd"] = "002"
                        ret['userid'] = self.server.db.get_next_available_userid(
                        )

                        logger.log(logging.DEBUG, "acctcreate response to %s",
                                   client_address)
                        logger.log(logging.DEBUG, ret)

                    ret = self.dict_to_str(ret)

                elif action == "login":
                    if self.server.db.is_ip_banned(post):
                        logger.log(logging.DEBUG,
                                   "login denied for banned user " + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3917",
                            "locator": "gamespy.com",
                            "retry": "1",
                            "reason": "User banned."
                        }
                    elif self.server.db.is_console_macadr_banned(post):
                        logger.log(
                            logging.DEBUG,
                            "login denied for banned console" + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3914",
                            "locator": "gamespy.com",
                            "retry": "1",
                            "reason": "User's console is banned."
                        }
                    elif self.server.db.is_console_cfc_banned(post):
                        logger.log(
                            logging.DEBUG,
                            "login denied for banned console" + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3915",
                            "locator": "gamespy.com",
                            "retry": "1",
                            "reason": "User's console is banned."
                        }
                    elif self.server.db.is_console_csnum_banned(post):
                        logger.log(
                            logging.DEBUG,
                            "login denied for banned console" + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3915",
                            "locator": "gamespy.com",
                            "retry": "1",
                            "reason": "User's console is banned."
                        }
                    elif self.server.db.is_ingamesn_banned(post):
                        logger.log(
                            logging.DEBUG,
                            "login denied for banned Mii name" + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3916",
                            "locator": "gamespy.com",
                            "retry": "1",
                            "reason": "Mii name is black listed"
                        }
                    elif self.server.db.is_devname_banned(post):
                        logger.log(
                            logging.DEBUG,
                            "login denied for banned device name" + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3916",
                            "locator": "gamespy.com",
                            "retry": "1",
                            "reason": "device name is black listed"
                        }


#Un0comment the lines below to activate console registration
                    elif not self.server.db.pending(post):
                        logger.log(
                            logging.DEBUG,
                            "Login denied - Unknown console" + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3921",
                            "locator": "gamespy.com",
                            "retry": "1",
                        }
                    elif not self.server.db.registered(post):
                        logger.log(
                            logging.DEBUG,
                            "Login denied - console pending" + str(post))
                        ret = {
                            "datetime": time.strftime("%Y%m%d%H%M%S"),
                            "returncd": "3888",
                            "locator": "gamespy.com",
                            "retry": "1",
                        }
                    else:
                        challenge = utils.generate_random_str(8)
                        post["challenge"] = challenge

                        authtoken = self.server.db.generate_authtoken(
                            post["userid"], post)
                        ret.update({
                            "returncd": "001",
                            "locator": "gamespy.com",
                            "challenge": challenge,
                            "token": authtoken,
                        })

                        logger.log(logging.DEBUG, "login response to %s",
                                   client_address)
                        logger.log(logging.DEBUG, ret)

                    ret = self.dict_to_str(ret)

                elif action == "SVCLOC" or action == "svcloc":  # Get service based on service id number
                    ret["returncd"] = "007"
                    ret["statusdata"] = "Y"
                    authtoken = self.server.db.generate_authtoken(
                        post["userid"], post)

                    if 'svc' in post:
                        if post["svc"] in ("9000", "9001"):  # DLC host = 9000
                            ret["svchost"] = self.headers[
                                'host']  # in case the client's DNS isn't redirecting dls1.nintendowifi.net

                            # Brawl has 2 host headers which Apache chokes on, so only return the first one or else it won't work
                            ret["svchost"] = ret["svchost"].split(',')[0]

                            if post["svc"] == 9000:
                                ret["token"] = authtoken
                            else:
                                ret["servicetoken"] = authtoken
                        elif post[
                                "svc"] == "0000":  # Pokemon requests this for some things
                            ret["servicetoken"] = authtoken
                            ret["svchost"] = "n/a"

                    logger.log(logging.DEBUG, "svcloc response to %s",
                               client_address)
                    logger.log(logging.DEBUG, ret)

                    ret = self.dict_to_str(ret)
                else:
                    logger.log(logging.WARNING,
                               "Unknown action request %s from %s!", self.path,
                               client_address)

            elif self.path == "/pr":
                logger.log(logging.DEBUG, "Request to %s from %s", self.path,
                           client_address)
                logger.log(logging.DEBUG, post)
                words = len(post["words"].split('\t'))
                wordsret = "0" * words
                ret = {
                    "prwords": wordsret,
                    "returncd": "000",
                    "datetime": time.strftime("%Y%m%d%H%M%S")
                }

                for l in "ACEJKP":
                    ret["prwords" + l] = wordsret

                self.send_response(200)
                self.send_header("Content-type", "text/plain")
                self.send_header("NODE", "wifiappe1")

                logger.log(logging.DEBUG, "pr response to %s", client_address)
                logger.log(logging.DEBUG, ret)

                ret = self.dict_to_str(ret)

            elif self.path == "/download":
                logger.log(logging.DEBUG, "Request to %s from %s", self.path,
                           client_address)
                logger.log(logging.DEBUG, post)

                action = post["action"]

                ret = ""
                dlcdir = os.path.abspath('dlc')
                dlcpath = os.path.abspath("dlc/" + post["gamecd"])
                dlc_contenttype = False

                if os.path.commonprefix([dlcdir, dlcpath]) != dlcdir:
                    logging.log(
                        logging.WARNING,
                        'Attempted directory traversal attack "%s", cancelling.',
                        dlcpath)
                    self.send_response(403)
                    return

                def safeloadfi(fn, mode='rb'):
                    '''
                    safeloadfi : string -> string

                    Safely load contents of a file, given a filename, and closing the file afterward
                    '''
                    with open(os.path.join(dlcpath, fn), mode) as fi:
                        return fi.read()

                if action == "count":
                    if post["gamecd"] in gamecodes_return_random_file:
                        ret = "1"
                    else:
                        count = 0

                        if os.path.exists(dlcpath):
                            count = len(os.listdir(dlcpath))

                            if os.path.isfile(dlcpath + "/_list.txt"):
                                attr1 = post.get("attr1", None)
                                attr2 = post.get("attr2", None)
                                attr3 = post.get("attr3", None)

                                dlcfi = safeloadfi("_list.txt")
                                lst = self.filter_list(dlcfi, attr1, attr2,
                                                       attr3)
                                count = self.get_file_count(lst)

                        ret = "%d" % count

                if action == "list":
                    num = post.get("num", None)
                    offset = post.get("offset", None)

                    if num != None:
                        num = int(num)

                    if offset != None:
                        offset = int(offset)

                    attr1 = post.get("attr1", None)
                    attr2 = post.get("attr2", None)
                    attr3 = post.get("attr3", None)

                    if os.path.exists(dlcpath):
                        # Look for a list file first.
                        # If the list file exists, send the entire thing back to the client.
                        if os.path.isfile(os.path.join(dlcpath, "_list.txt")):
                            if post["gamecd"].startswith(
                                    "IRA") and attr1.startswith("MYSTERY"):
                                # Pokemon BW Mystery Gifts, until we have a better solution for that
                                ret = self.filter_list(safeloadfi("_list.txt"),
                                                       attr1, attr2, attr3)
                                ret = self.filter_list_g5_mystery_gift(
                                    ret, post["rhgamecd"])
                                ret = self.filter_list_by_date(
                                    ret, post["token"])
                            elif post[
                                    "gamecd"] in gamecodes_return_random_file:
                                # Pokemon Gen 4 Mystery Gifts, same here
                                ret = self.filter_list(safeloadfi("_list.txt"),
                                                       attr1, attr2, attr3)
                                ret = self.filter_list_by_date(
                                    ret, post["token"])
                            else:
                                # default case for most games
                                ret = self.filter_list(safeloadfi("_list.txt"),
                                                       attr1, attr2, attr3,
                                                       num, offset)

                if action == "contents":
                    # Get only the base filename just in case there is a path involved somewhere in the filename string.
                    dlc_contenttype = True
                    contents = os.path.basename(post["contents"])
                    ret = safeloadfi(contents)

                self.send_response(200)

                if dlc_contenttype == True:
                    self.send_header("Content-type", "application/x-dsdl")
                    self.send_header(
                        "Content-Disposition",
                        "attachment; filename=\"" + post["contents"] + "\"")
                else:
                    self.send_header("Content-type", "text/plain")

                self.send_header("X-DLS-Host", "http://127.0.0.1/")

                logger.log(logging.DEBUG, "download response to %s",
                           client_address)

                #if dlc_contenttype == False:
                #    logger.log(logging.DEBUG, ret)
            else:
                self.send_response(404)
                logger.log(logging.WARNING, "Unknown path request %s from %s!",
                           self.path, client_address)
                return

            self.send_header("Content-Length", str(len(ret)))
            self.end_headers()
            self.wfile.write(ret)
        except:
            logger.log(logging.ERROR,
                       "Unknown exception: %s" % traceback.format_exc())
from gamespy_gamestats_server import GameSpyGamestatsServer
from nas_server import NasServer
from internal_stats_server import InternalStatsServer
from storage_server import StorageServer
from gamestats_server_http import GameStatsServer

import gamespy.gs_database as gs_database

import threading


if __name__ == "__main__":
    # Let database initialize before starting any servers.
    # This fixes any conflicts where two servers find an uninitialized database at the same time and both try to
    # initialize it.
    gs_database.GamespyDatabase().initialize_database()
    
    servers = [
        GameSpyBackendServer,
        GameSpyQRServer,
        GameSpyProfileServer,
        GameSpyPlayerSearchServer,
        GameSpyGamestatsServer,
        #GameSpyServerBrowserServer,
        GameSpyNatNegServer,
        NasServer,
        InternalStatsServer,
        StorageServer,
        GameStatsServer,
    ]
    for server in servers: