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
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)
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')
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()
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)
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
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()
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.' )
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()
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)
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)
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()
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()
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)
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)