Esempio n. 1
0
async def _abnormal_shutdown(exception, server=None):
    # Print complete traceback to console
    etype, evalue, etraceback = (type(exception), exception,
                                 exception.__traceback__)
    info = 'TSUSERVERDR HAS ENCOUNTERED A FATAL PYTHON ERROR.'
    info += "\r\n" + "".join(
        traceback.format_exception(etype, evalue, etraceback))
    logger.log_print(info)
    logger.log_error(info, server=server, errortype='P')

    logger.log_server('Server is shutting down due to an unhandled exception.')
    logger.log_print('Attempting a graceful shutdown.')

    if not server:
        logger.log_pserver('Server has successfully shut down.')
        return

    try:
        await server.normal_shutdown()
    except Exception as exception2:
        logger.log_print('Unable to gracefully shut down: Forcing a shutdown.')
        etype, evalue, etraceback = (type(exception2), exception2,
                                     exception2.__traceback__)
        info = "\r\n" + "".join(
            traceback.format_exception(etype, evalue, etraceback))

        logger.log_print(info)
        logger.log_error(info, server=server, errortype='P')
Esempio n. 2
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()
Esempio n. 3
0
    def start(self):
        try:
            self.loop = asyncio.get_event_loop()
        except RuntimeError:
            self.loop = asyncio.new_event_loop()

        self.tasker = Tasker(self, self.loop)
        bound_ip = '0.0.0.0'
        if self.config['local']:
            bound_ip = '127.0.0.1'
            server_name = 'localhost'
            logger.log_print('Starting a local server...')
        else:
            server_name = self.config['masterserver_name']
            logger.log_print('Starting a nonlocal server...')

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

        logger.log_pserver('Server started successfully!')

        if self.config['local']:
            host_ip = '127.0.0.1'
        else:
            try:
                host_ip = (urllib.request.urlopen('https://api.ipify.org',
                                                  context=ssl.SSLContext())
                           .read().decode('utf8'))
            except urllib.error.URLError as ex:
                host_ip = None
                logger.log_pdebug('Unable to obtain personal IP from https://api.ipify.org\n'
                                  '{}: {}\n'
                                  'Players may be unable to join.'
                                  .format(type(ex).__name__, ex.reason))
        if host_ip is not None:
            logger.log_pdebug('Server should be now accessible from {}:{}:{}'
                              .format(host_ip, self.config['port'], server_name))
        if not self.config['local']:
            logger.log_pdebug('If you want to join your server from this device, you may need to '
                              'join with this IP instead: 127.0.0.1:{}:localhost'
                              .format(self.config['port']))

        if self.config['local']:
            self.local_connection = asyncio.ensure_future(self.tasker.do_nothing(), loop=self.loop)

        if self.config['use_district']:
            self.district_client = DistrictClient(self)
            self.district_connection = asyncio.ensure_future(self.district_client.connect(),
                                                             loop=self.loop)
            print(' ')
            logger.log_print('Attempting to connect to district at {}:{}.'
                             .format(self.config['district_ip'], self.config['district_port']))

        if self.config['use_masterserver']:
            self.ms_client = MasterServerClient(self)
            self.masterserver_connection = asyncio.ensure_future(self.ms_client.connect(),
                                                                 loop=self.loop)
            print(' ')
            logger.log_print('Attempting to connect to the master server at {}:{} with the '
                             'following details:'.format(self.config['masterserver_ip'],
                                                         self.config['masterserver_port']))
            logger.log_print('*Server name: {}'.format(self.config['masterserver_name']))
            logger.log_print('*Server description: {}'
                             .format(self.config['masterserver_description']))

        try:
            self.loop.run_forever()
        except KeyboardInterrupt:
            pass

        print('') # Lame
        logger.log_pdebug('You have initiated a server shut down.')
        self.shutdown()

        ao_server.close()
        self.loop.run_until_complete(ao_server.wait_closed())
        self.loop.close()
        logger.log_pserver('Server has successfully shut down.')
Esempio n. 4
0
async def _normal_shutdown(server=None):
    if not server:
        logger.log_pserver('Server has successfully shut down.')
        return

    await server.normal_shutdown()
Esempio n. 5
0
    async def start(self):
        self.loop = asyncio.get_event_loop()
        self.error_queue = asyncio.Queue()

        self.tasker = Tasker(self)
        bound_ip = '0.0.0.0'
        if self.config['local']:
            bound_ip = '127.0.0.1'
            server_name = 'localhost'
            logger.log_print('Starting a local server...')
        else:
            server_name = self.config['masterserver_name']
            logger.log_print('Starting a nonlocal server...')

        # Check if port is available
        port = self.config['port']
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            try:
                s.bind((bound_ip, port))
            except socket.error as exc:
                if exc.errno == errno.EADDRINUSE:
                    msg = (
                        f'Port {port} is in use by another application. Make sure to close any '
                        f'conflicting applications (even another instance of this server) and '
                        f'try again.')
                    raise ServerError(msg)
                raise exc
            except OverflowError as exc:
                msg = str(exc).replace('bind(): ', '').capitalize()
                msg += ' Make sure to set your port number to an appropriate value and try again.'
                raise ServerError(msg)

        # Yes there is a race condition here (between checking if port is available, and actually
        # using it). The only side effect of a race condition is a slightly less nice error
        # message, so it's not that big of a deal.
        self._server = await self.loop.create_server(
            lambda: self.protocol(self), bound_ip, port, start_serving=False)
        asyncio.create_task(self._server.serve_forever())
        logger.log_pserver('Server started successfully!')

        if self.config['local']:
            host_ip = '127.0.0.1'
        else:
            try:
                host_ip = (urllib.request.urlopen(
                    'https://api.ipify.org',
                    context=ssl.SSLContext()).read().decode('utf8'))
            except urllib.error.URLError as ex:
                host_ip = None
                logger.log_pdebug(
                    'Unable to obtain personal IP from https://api.ipify.org\n'
                    '{}: {}\n'
                    'Players may be unable to join.'.format(
                        type(ex).__name__, ex.reason))
        if host_ip is not None:
            logger.log_pdebug(
                'Server should be now accessible from {}:{}:{}'.format(
                    host_ip, self.config['port'], server_name))
        if not self.config['local']:
            logger.log_pdebug(
                'If you want to join your server from this device, you may need to '
                'join with this IP instead: 127.0.0.1:{}:localhost'.format(
                    self.config['port']))

        if self.config['local']:
            self.local_connection = asyncio.create_task(
                self.tasker.do_nothing())

        if self.config['use_district']:
            self.district_client = DistrictClient(self)
            self.district_connection = asyncio.create_task(
                self.district_client.connect())
            print(' ')
            logger.log_print(
                'Attempting to connect to district at {}:{}.'.format(
                    self.config['district_ip'], self.config['district_port']))

        if self.config['use_masterserver']:
            self.ms_client = MasterServerClient(self)
            self.masterserver_connection = asyncio.create_task(
                self.ms_client.connect())
            print(' ')
            logger.log_print(
                'Attempting to connect to the master server at {}:{} with the '
                'following details:'.format(self.config['masterserver_ip'],
                                            self.config['masterserver_port']))
            logger.log_print('*Server name: {}'.format(
                self.config['masterserver_name']))
            logger.log_print('*Server description: {}'.format(
                self.config['masterserver_description']))

        raise await self.error_queue.get()