예제 #1
0
    def data_received(self, data):
        """ Handles any data received from the network.

        Receives data, parses them into a command and passes it
        to the command handler.

        :param data: bytes of data
        """
        # try to decode as utf-8, ignore any erroneous characters
        self.buffer += data.decode('utf-8', 'ignore')
        if len(self.buffer) > 8192:
            self.client.disconnect()
        for msg in self.get_messages():
            if len(msg) < 2:
                self.client.disconnect()
                return
            # general netcode structure is not great
            if msg[0] in ('#', '3', '4'):
                if msg[0] == '#':
                    msg = msg[1:]
                spl = msg.split('#', 1)
                msg = '#'.join([fanta_decrypt(spl[0])] + spl[1:])
                logger.log_debug('[INC][RAW]{}'.format(msg), self.client)
            try:
                cmd, *args = msg.split('#')
                self.net_cmd_dispatcher[cmd](self, args)
            except KeyError:
                return
예제 #2
0
 async def handle_connection(self):
     logger.log_debug('Master server connected.')
     await self.send_server_info()
     fl = False
     lastping = time.time() - 20
     while True:
         self.reader.feed_data(b'END')
         full_data = await self.reader.readuntil(b'END')
         full_data = full_data[:-3]
         if len(full_data) > 0:
             data_list = list(full_data.split(b'#%'))[:-1]
             for data in data_list:
                 raw_msg = data.decode()
                 cmd, *args = raw_msg.split('#')
                 if cmd != 'CHECK' and cmd != 'PONG':
                     logger.log_debug(
                         '[MASTERSERVER][INC][RAW]{}'.format(raw_msg))
                 elif cmd == 'CHECK':
                     await self.send_raw_message('PING#%')
                 elif cmd == 'PONG':
                     fl = False
                 elif cmd == 'NOSERV':
                     await self.send_server_info()
         if time.time() - lastping > 5:
             if fl:
                 self.writer.close()
                 return
             lastping = time.time()
             fl = True
             await self.send_raw_message('PING#%')
         await asyncio.sleep(1)
예제 #3
0
 def load_iniswaps(self):
     try:
         with open('config/iniswaps.yaml', 'r',
                   encoding='utf-8') as iniswaps:
             self.allowed_iniswaps = yaml.load(iniswaps)
     except:
         logger.log_debug('cannot find iniswaps.yaml')
예제 #4
0
    def start(self):
        loop = asyncio.get_event_loop()

        bound_ip = '0.0.0.0'
        if self.config['local']:
            bound_ip = '127.0.0.1'

        ao_server_crt = loop.create_server(lambda: AOProtocol(self), bound_ip,
                                           self.config['port'])
        ao_server = loop.run_until_complete(ao_server_crt)

        if self.config['use_district']:
            self.district_client = DistrictClient(self)
            asyncio.ensure_future(self.district_client.connect(), loop=loop)

        if self.config['use_masterserver']:
            self.ms_client = MasterServerClient(self)
            asyncio.ensure_future(self.ms_client.connect(), loop=loop)

        logger.log_debug('Server started.')

        try:
            loop.run_forever()
        except KeyboardInterrupt:
            pass

        logger.log_debug('Server shutting down.')

        ao_server.close()
        loop.run_until_complete(ao_server.wait_closed())
        loop.close()
예제 #5
0
 async def handle_connection(self):
     logger.log_debug('District connected.')
     self.send_raw_message('AUTH#{}'.format(
         self.server.config['district_password']))
     while True:
         data = await self.reader.readuntil(b'\r\n')
         if not data:
             return
         raw_msg = data.decode()[:-2]
         logger.log_debug('[DISTRICT][INC][RAW]{}'.format(raw_msg))
         cmd, *args = raw_msg.split('#')
         if cmd == 'GLOBAL':
             glob_name = '{}[{}:{}][{}]'.format('<dollar>G', args[1],
                                                args[2], args[3])
             if args[0] == '1':
                 glob_name += '[M]'
             self.server.send_all_cmd_pred(
                 'CT',
                 glob_name,
                 args[4],
                 pred=lambda x: not x.muted_global)
         elif cmd == 'NEED':
             need_msg = '=== Cross Advert ===\r\n{} at {} in {} [{}] needs {}\r\n====================' \
                 .format(args[1], args[0], args[2], args[3], args[4])
             self.server.send_all_cmd_pred(
                 'CT',
                 '{}'.format(self.server.config['hostname']),
                 need_msg,
                 pred=lambda x: not x.muted_adverts)
예제 #6
0
    def handshake(self, data):
        try:
            message = data[0:1024].decode().strip()
        except UnicodeDecodeError:
            return False

        upgrade = re.search('\nupgrade[\s]*:[\s]*websocket', message.lower())
        if not upgrade:
            self.keep_alive = False
            return False

        key = re.search('\n[sS]ec-[wW]eb[sS]ocket-[kK]ey[\s]*:[\s]*(.*)\r\n',
                        message)
        if key:
            key = key.group(1)
        else:
            logger.log_debug("Client tried to connect but was missing a key",
                             self.client)
            self.keep_alive = False
            return False

        response = self.make_handshake_response(key)
        print(response.encode())
        self.transport.write(response.encode())
        self.handshake_done = True
        self.valid = True
        return True
예제 #7
0
    def data_received(self, data):
        """ Handles any data received from the network.

        Receives data, parses them into a command and passes it
        to the command handler.

        :param data: bytes of data
        """
        buf = data
        if buf is None:
            buf = b''
        # try to decode as utf-8, ignore any erroneous characters
        self.buffer += buf.decode('utf-8', 'ignore')
        self.buffer = self.buffer.translate({ord(c): None for c in '\0'})

        if len(self.buffer) > 8192:
            msg = self.buffer if len(
                self.buffer) < 512 else self.buffer[:512] + '...'
            logger.log_server(
                'Terminated {} (packet too long): sent {} ({} bytes)'.format(
                    self.client.get_ipreal(), msg, len(self.buffer)))
            self.client.disconnect()
            return

        found_message = False
        for msg in self.get_messages():
            found_message = True
            if len(msg) < 2:
                # This immediatelly kills any client that does not even try to follow the proper
                # client protocol
                msg = self.buffer if len(
                    self.buffer) < 512 else self.buffer[:512] + '...'
                logger.log_server(
                    'Terminated {} (packet too short): sent {} ({} bytes)'.
                    format(self.client.get_ipreal(), msg, len(self.buffer)))
                self.client.disconnect()
                return
            # general netcode structure is not great
            if msg[0] in ('#', '3', '4'):
                if msg[0] == '#':
                    msg = msg[1:]
                spl = msg.split('#', 1)
                msg = '#'.join([fanta_decrypt(spl[0])] + spl[1:])
                logger.log_debug('[INC][RAW]{}'.format(msg), self.client)
            try:
                # print(f'> {self.client.id}: {msg}')
                cmd, *args = msg.split('#')
                self.net_cmd_dispatcher[cmd](self, args)
            except Exception as ex:
                self.server.send_error_report(self.client, cmd, args, ex)
        if not found_message:
            # This immediatelly kills any client that does not even try to follow the proper
            # client protocol
            msg = self.buffer if len(
                self.buffer) < 512 else self.buffer[:512] + '...'
            logger.log_server(
                'Terminated {} (packet syntax unrecognized): sent {} ({} bytes)'
                .format(self.client.get_ipreal(), msg, len(self.buffer)))
            self.client.disconnect()
예제 #8
0
 def load_ids(self):
     self.hdid_list = {}
     # load hdids
     try:
         with open('storage/hd_ids.json', 'r',
                   encoding='utf-8') as whole_list:
             self.hdid_list = json.loads(whole_list.read())
     except:
         logger.log_debug(
             'Failed to load hd_ids.json from ./storage. If hd_ids.json is exist then remove it.'
         )
예제 #9
0
 async def handle_connection(self):
     logger.log_debug('Master server connected.')
     await self.send_server_info()
     while True:
         data = await self.reader.readuntil(b'#%')
         if not data:
             return
         raw_msg = data.decode()[:-2]
         cmd, *args = raw_msg.split('#')
         if cmd != 'CHECK' and cmd != 'PONG':
             logger.log_debug('[MASTERSERVER][INC][RAW]{}'.format(raw_msg))
         if cmd == 'CHECK':
             await self.send_raw_message('PING#%')
         elif cmd == 'NOSERV':
             await self.send_server_info()
예제 #10
0
 async def connect(self):
     loop = asyncio.get_event_loop()
     while True:
         try:
             self.reader, self.writer = await asyncio.open_connection(
                 self.server.config['district_ip'],
                 self.server.config['district_port'])
             await self.handle_connection()
         except (ConnectionRefusedError, TimeoutError):
             pass
         except (ConnectionResetError, asyncio.IncompleteReadError):
             self.writer = None
             self.reader = None
         finally:
             logger.log_debug(
                 "Couldn't connect to the district, retrying in 30 seconds."
             )
             await asyncio.sleep(30)
예제 #11
0
    def data_received(self, data):
        """ Handles any data received from the network.

        Receives data, parses them into a command and passes it
        to the command handler.

        :param data: bytes of data
        """
        buf = data

        if not self.client.is_checked and self.server.ban_manager.is_banned(
                self.client.ipid):
            self.client.transport.close()
        else:
            self.client.is_checked = True

        if buf is None:
            buf = b''

        if not isinstance(buf, str):
            # try to decode as utf-8, ignore any erroneous characters
            self.buffer += buf.decode('utf-8', 'ignore')
        else:
            self.buffer = buf

        if len(self.buffer) > 8192:
            self.client.disconnect()
        for msg in self.get_messages():
            if len(msg) < 2:
                continue
            # general netcode structure is not great
            if msg[0] in ('#', '3', '4'):
                if msg[0] == '#':
                    msg = msg[1:]
                spl = msg.split('#', 1)
                msg = '#'.join([fanta_decrypt(spl[0])] + spl[1:])
                logger.log_debug('[INC][RAW]{}'.format(msg), self.client)
            try:
                cmd, *args = msg.split('#')
                self.net_cmd_dispatcher[cmd](self, args)
            except KeyError:
                logger.log_debug('[INC][UNK]{}'.format(msg), self.client)
예제 #12
0
    def data_received(self, data):
        """ Handles any data received from the network.

        Receives data, parses them into a command and passes it
        to the command handler.

        :param data: bytes of data
        """
        buf = data
        if buf is None:
            buf = b''
        # try to decode as utf-8, ignore any erroneous characters
        self.buffer += buf.decode('utf-8', 'ignore')
        self.buffer = self.buffer.translate({ord(c): None for c in '\0'})

        if len(self.buffer) > 8192:
            self.client.disconnect()
        found_message = False
        for msg in self.get_messages():
            found_message = True
            if len(msg) < 2:
                self.client.disconnect()
                return
            # general netcode structure is not great
            if msg[0] in ('#', '3', '4'):
                if msg[0] == '#':
                    msg = msg[1:]
                spl = msg.split('#', 1)
                msg = '#'.join([fanta_decrypt(spl[0])] + spl[1:])
                logger.log_debug('[INC][RAW]{}'.format(msg), self.client)
            try:
                cmd, *args = msg.split('#')
                self.net_cmd_dispatcher[cmd](self, args)
            except Exception as ex:
                self.server.send_error_report(self.client, cmd, args, ex)
        if not found_message:
            # This immediatelly kills webAO or any client that does not even try to follow the
            # standalone client protocol
            self.client.disconnect()
예제 #13
0
    def data_received(self, data):
        """ Handles any data received from the network.

        Receives data, parses them into a command and passes it
        to the command handler.

        :param data: bytes of data
        """
        buf = data
        if buf is None:
            buf = b''

        # try to decode as utf-8, ignore any erroneous characters
        self.buffer += buf.decode('utf-8', 'ignore')
        self.buffer = self.buffer.translate({ord(c): None for c in '\0'})

        if len(self.buffer) > 8192:
            msg = self.buffer if len(self.buffer) < 512 else self.buffer[:512] + '...'
            logger.log_server('Terminated {} (packet too long): sent {} ({} bytes)'
                              .format(self.client.get_ipreal(), msg, len(self.buffer)))
            self.client.disconnect()
            return

        found_message = False
        for msg in self.get_messages():
            found_message = True
            if len(msg) < 2:
                # This immediatelly kills any client that does not even try to follow the proper
                # client protocol
                msg = self.buffer if len(self.buffer) < 512 else self.buffer[:512] + '...'
                logger.log_server('Terminated {} (packet too short): sent {} ({} bytes)'
                                  .format(self.client.get_ipreal(), msg, len(self.buffer)))
                self.client.disconnect()
                return
            # general netcode structure is not great
            if msg[0] in ('#', '3', '4'):
                if msg[0] == '#':
                    msg = msg[1:]
                raw_parameters = msg.split('#')
                raw_parameters[0] = fanta_decrypt(raw_parameters[0])
                msg = '#'.join(raw_parameters)

            logger.log_debug('[INC][RAW]{}'.format(msg), self.client)
            try:
                if self.server.print_packets:
                    print(f'> {self.client.id}: {msg}')
                self.server.log_packet(self.client, msg, True)
                # Decode AO clients' encoding
                cmd, *args = Constants.decode_ao_packet(msg.split('#'))
                try:
                    dispatched = self.net_cmd_dispatcher[cmd]
                except KeyError:
                    logger.log_pserver(f'Client {self.client.id} sent abnormal packet {msg} '
                                       f'(client version: {self.client.version}).')
                else:
                    dispatched(self, args)
            except AOProtocolError.InvalidInboundPacketArguments:
                pass
            except Exception as ex:
                self.server.send_error_report(self.client, cmd, args, ex)
        if not found_message:
            # This immediatelly kills any client that does not even try to follow the proper
            # client protocol
            msg = self.buffer if len(self.buffer) < 512 else self.buffer[:512] + '...'
            logger.log_server('Terminated {} (packet syntax unrecognized): sent {} ({} bytes)'
                              .format(self.client.get_ipreal(), msg, len(self.buffer)))
            self.client.disconnect()
예제 #14
0
    def data_received(self, data):
        """ Handles any data received from the network.

        Receives data, parses them into a command and passes it
        to the command handler.

        :param data: bytes of data
        """
        # try to decode as utf-8, ignore any erroneous characters
        self.buffer += data.decode('utf-8', 'ignore')
        if len(self.buffer) > 8192:
            self.client.disconnect()
        for msg in self.get_messages():
            if len(msg) < 2:
                self.client.disconnect()
                return
            # general netcode structure is not great
            if msg[0] in ('#', '3', '4'):
                if msg[0] == '#':
                    msg = msg[1:]
                spl = msg.split('#', 1)
                msg = '#'.join([fanta_decrypt(spl[0])] + spl[1:])
                logger.log_debug('[INC][RAW]{}'.format(msg), self.client)
            try:
                cmd, *args = msg.split('#')
                self.net_cmd_dispatcher[cmd](self, args)
            except Exception as ex:
                # Send basic logging information to user
                info = '=========\nThe server ran into a Python issue. Please contact the server owner and send them the following logging information:'
                etype, evalue, etraceback = sys.exc_info()
                tb = traceback.extract_tb(tb=etraceback)
                current_time = asctime(localtime(time()))
                file, line_num, module, func = tb[-1]
                file = file[file.rfind('\\') +
                            1:]  # Remove unnecessary directories
                info += '\r\n*Server time: {}'.format(current_time)
                info += '\r\n*Packet details: {} {}'.format(cmd, args)
                info += '\r\n*Client status: {}, {}, {}'.format(
                    self.client.id, self.client.get_char_name(),
                    self.client.is_staff())
                info += '\r\n*Area status: {}, {}'.format(
                    self.client.area.id, len(self.client.area.clients))
                info += '\r\n*File: {}'.format(file)
                info += '\r\n*Line number: {}'.format(line_num)
                info += '\r\n*Module: {}'.format(module)
                info += '\r\n*Function: {}'.format(func)
                info += '\r\n*Error: {}: {}'.format(type(ex).__name__, ex)
                info += '\r\nYour help would be much appreciated.'
                info += '\r\n========='
                self.client.send_host_message(info)

                # Print complete traceback to console
                info = 'TSUSERVER HAS ENCOUNTERED AN ERROR HANDLING A CLIENT PACKET'
                info += '\r\n*Server time: {}'.format(current_time)
                info += '\r\n*Packet details: {} {}'.format(cmd, args)
                info += '\r\n*Client status: {}, {}, {}'.format(
                    self.client.id, self.client.get_char_name(),
                    self.client.is_staff())
                info += '\r\n*Area status: {}, {}'.format(
                    self.client.area.id, len(self.client.area.clients))
                logger.log_print(info)
                traceback.print_exception(etype, evalue, etraceback)
예제 #15
0
    def parse(self, data):
        b1, b2 = 0, 0
        if len(data) >= 2:
            b1, b2 = data[0], data[1]

        fin = b1 & Bitmasks.FIN
        opcode = b1 & Bitmasks.OPCODE
        masked = b2 & Bitmasks.MASKED
        payload_length = b2 & Bitmasks.PAYLOAD_LEN

        if not b1:
            # Connection closed
            self.keep_alive = 0
            return
        if opcode == Opcode.CLOSE_CONN:
            # Connection close requested
            self.keep_alive = 0
            return
        if not masked:
            # Client was not masked (spec violation)
            logger.log_debug("ws: client was not masked.", self.client)
            self.keep_alive = 0
            print(data)
            return
        if opcode == Opcode.CONTINUATION:
            # No continuation frames supported
            logger.log_debug("ws: client tried to send continuation frame.",
                             self.client)
            return
        elif opcode == Opcode.BINARY:
            # No binary frames supported
            logger.log_debug("ws: client tried to send binary frame.",
                             self.client)
            return
        elif opcode == Opcode.TEXT:

            def opcode_handler(s, msg):
                return msg
        elif opcode == Opcode.PING:
            opcode_handler = self.send_pong
        elif opcode == Opcode.PONG:
            opcode_handler = lambda s, msg: None
        else:
            # Unknown opcode
            logger.log_debug("ws: unknown opcode!", self.client)
            self.keep_alive = 0
            return

        mask_offset = 2
        if payload_length == 126:
            payload_length = struct.unpack(">H", data[2:4])[0]
            mask_offset = 4
        elif payload_length == 127:
            payload_length = struct.unpack(">Q", data[2:10])[0]
            mask_offset = 10

        masks = data[mask_offset:mask_offset + 4]
        decoded = ""
        for char in data[mask_offset + 4:payload_length + mask_offset + 4]:
            char ^= masks[len(decoded) % 4]
            decoded += chr(char)

        return opcode_handler(self, decoded)