def __init__(self, port, ip_address, db, username, passwd, keys): """Initializes client-server network interface, connects and authenticates with the server. :type db: :class:`messenger.client.client_database.ClientBase` """ threading.Thread.__init__(self) QObject.__init__(self) self._db = db self.username = username self.password = passwd self._socket = None self.keys = keys self.pubkey = None self.connection_init(port, ip_address) try: self.user_list_update() self.contact_list_update() except OSError as err: if err.errno: CLIENT_LOG.critical('Connection lost.') raise ServerError('Server connection lost!') CLIENT_LOG.error('Timed out while updating user lists!') except json.JSONDecodeError: CLIENT_LOG.critical('Connection lost.') raise ServerError('Server connection lost!') self.running = True
def run(self): """Main client transport cycle thread method. Receives incoming messages, passes them to the processing method and watches over the server connection.""" CLIENT_LOG.debug('Server message transport launched.') while self.running: time.sleep(1) with socket_lock: try: self._socket.settimeout(0.5) message = receive_message(self._socket, CLIENT_LOG) except OSError as err: if err.errno: CLIENT_LOG.critical('Lost connection to the server!') self.running = False self.connection_lost.emit() except (ConnectionError, ConnectionResetError, ConnectionAbortedError, json.JSONDecodeError, TypeError): CLIENT_LOG.critical('Lost connection to the server!') self.running = False self.connection_lost.emit() else: CLIENT_LOG.debug(f'Got a message: {message}') self.process_server_answer(message) finally: self._socket.settimeout(5)
def arg_parser(): """ CLI parameters parser. Returns address, port (validated), username, password for user """ parser = argparse.ArgumentParser() parser.add_argument('addr', default=DEFAULT_ADDRESS, nargs='?') parser.add_argument('port', default=DEFAULT_PORT, type=int, nargs='?') parser.add_argument('-n', '--name', default=None, nargs='?') parser.add_argument('-p', '--password', default='', nargs='?') namespace = parser.parse_args(sys.argv[1:]) server_address = namespace.addr server_port = namespace.port client_name = namespace.name client_passwd = namespace.password if not 1023 < server_port < 65536: CLIENT_LOG.critical( f'Unusable port number provided: {server_port}. Use ports between {MIN_PORT_NUMBER} and {MAX_PORT_NUMBER}.' ) exit(1) return server_address, server_port, client_name, client_passwd
def connection_init(self, port, ip): """Method for starting a server connection. Opens socket, connects to the server over network, performs handshake and authorization.""" self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self._socket.settimeout(5) connected = False for i in range(5): CLIENT_LOG.info(f'Connecting - try #{i + 1}') try: self._socket.connect((ip, port)) except (OSError, ConnectionRefusedError): pass else: connected = True break time.sleep(1) if not connected: CLIENT_LOG.critical( f"Couldn't connect to the server ({ip}:{port}).") raise ServerError("Couldn't connect to the server!") CLIENT_LOG.debug( f'Connection with server {ip}:{port} established, starting AUTH process.' ) passwd_bytes = self.password.encode('utf-8') salt = self.username.lower().encode('utf-8') passwd_hash = hashlib.pbkdf2_hmac('sha512', passwd_bytes, salt, 10000) passwd_hash_string = binascii.hexlify(passwd_hash) CLIENT_LOG.debug(f'Passwd hash ready: {passwd_hash_string}') self.pubkey = self.keys.publickey().export_key().decode('ascii') try: with socket_lock: send_message(self.form_presence_message(), self._socket, CLIENT_LOG) ans = receive_message(self._socket, CLIENT_LOG) if JIM.RESPONSE in ans: if ans[JIM.RESPONSE] == 400: raise ServerError(ans[JIM.ERROR]) elif ans[JIM.RESPONSE] == ResCodes.AUTH_PROCESS: ans_data = ans[JIM.DATA] hash = hmac.new(passwd_hash_string, ans_data.encode('utf-8'), 'MD5') digest = hash.digest() to_send = form_response(ResCodes.AUTH_PROCESS) to_send[JIM.DATA] = binascii.b2a_base64(digest).decode( 'ascii') send_message(to_send, self._socket, CLIENT_LOG) self.process_server_answer( receive_message(self._socket, CLIENT_LOG)) # self.process_server_answer( # receive_message(self._socket, CLIENT_LOG)) except (OSError, json.JSONDecodeError) as err: CLIENT_LOG.critical('Connection lost.', exc_info=err) raise ServerError('Server connection lost!') CLIENT_LOG.info('Server received our presence message.')