Пример #1
0
 def run(self):
     """
     The main loop of the client's app.
     While the running flag is True, it locks the socket and tries to receive
     messages from the server. Handles various exceptions and emits the
     lost connection signal.
     """
     SOCKET_LOGGER.debug('Запущен процесс приема сообщений с сервера.')
     while self.running:
         sleep(1)
         with socket_lock:
             try:
                 self.client_socket.settimeout(0.5)
                 server_response = receive_message(self.client_socket)
             except OSError as e:
                 if e.errno:
                     SOCKET_LOGGER.critical(
                         'Потеряно соединение с сервером.')
                     self.running = False
                     self.connection_lost.emit()
             except (ConnectionError, ConnectionAbortedError,
                     ConnectionRefusedError, JSONDecodeError, TypeError):
                 SOCKET_LOGGER.critical('Ошибка при соединении с сервером.')
                 self.running = False
                 self.connection_lost.emit()
             else:
                 SOCKET_LOGGER.debug('Принято сообщение с сервера: %s' %
                                     server_response)
                 self.process_answer(server_response)
             finally:
                 self.client_socket.settimeout(5)
Пример #2
0
    def run(self):
        """
        The main loop of the server.
        Creates a listening socket, and then accepts connections while the working flag is True.
        If there are clients with messages, sends them.
        """
        self.server_socket = socket(AF_INET, SOCK_STREAM)
        self.server_socket.bind((self.listening_address, self.listening_port))
        self.server_socket.settimeout(0.5)
        self.server_socket.listen(MAX_NUMBER_OF_CONNECTIONS)

        try:
            while self.working:
                try:
                    client_socket, client = self.server_socket.accept()
                    addr, port = client
                except OSError:
                    pass
                else:
                    SERVER_LOGGER.info(
                        'Установлено соединение с пользователем: '
                        'адрес: %s, порт: %s.' % (
                            addr,
                            port,
                        ))
                    client_socket.settimeout(5)
                    self.clients_list.append(client_socket)
                ready_to_receive = []
                ready_to_send = []
                exceptions_list = []
                try:
                    if self.clients_list:
                        ready_to_send, self.sockets_list, self.exceptions_list = select(
                            self.clients_list, self.clients_list, [], 0)
                except OSError as err:
                    SERVER_LOGGER.error('Ошибка работы с сокетами: %s.' % err)
                if ready_to_send:
                    for client in ready_to_send:
                        try:
                            self.process_client_message(
                                receive_message(client), client)
                        except (OSError, JSONDecodeError, TypeError) as e:
                            SERVER_LOGGER.error(
                                'Ошибка при запросе '
                                'информации от клиента.',
                                exc_info=e)
                            self.delete_client(client)
        except KeyboardInterrupt:
            SERVER_LOGGER.info('Серер остановлен пользователем.')
            self.server_socket.close()
Пример #3
0
    def remove_contact(self, contact: str):
        """
        Handles the adding of a new contact on the socket's side.
        Generates the relevant message and sends it to the server.

        :param contact: contact to be deleted
        """
        SOCKET_LOGGER.debug('Удаление контакта %s для пользователя %s' % (
            contact,
            self.client_nickname,
        ))
        request = {
            ACTION: REMOVE_CONTACT,
            TIME: time(),
            USER: self.client_nickname,
            ACCOUNT_NAME: contact
        }
        with socket_lock:
            send_message(self.client_socket, request)
            self.process_answer(receive_message(self.client_socket))
Пример #4
0
    def create_message(self, recipient: str, message: str):
        """
        Creates and sends the dictionary with the message from one client to another.

        :param recipient: message's recipient
        :param message: message text
        """
        message_to_send_dict = {
            ACTION: MESSAGE,
            SENDER: self.client_nickname,
            DESTINATION: recipient,
            TIME: time(),
            MESSAGE_TEXT: message
        }
        SOCKET_LOGGER.debug('Сформирован словарь сообщения: %s' %
                            message_to_send_dict)
        with socket_lock:
            send_message(self.client_socket, message_to_send_dict)
            self.process_answer(receive_message(self.client_socket))
            SOCKET_LOGGER.info('Отправлено сообщение пользователю %s' %
                               recipient)
Пример #5
0
    def request_pub_key(self, user: str) -> str:
        """
        Requests a public RSA key for a client in user's contact list.
        If the status code in the server's response is 511, returns the key.

        :param user: contact
        """
        SOCKET_LOGGER.debug('Запрос публичного ключа для пользователя %s' %
                            user)
        request = {
            ACTION: PUBLIC_KEY_REQUEST,
            TIME: time(),
            ACCOUNT_NAME: user
        }
        with socket_lock:
            send_message(self.client_socket, request)
            response = receive_message(self.client_socket)
            if RESPONSE in response and response[RESPONSE] == 511:
                return response[DATA]
            else:
                SOCKET_LOGGER.error('Не удалось получить публичный ключ '
                                    'пользователя %s' % user)
Пример #6
0
 def request_user_list(self):
     """
     Requests a list of existing users from the server.
     If the status code of the server's response is 202, updates
     the list in the client's DB.
     """
     SOCKET_LOGGER.info(
         'Пользователь %s запрашивает список всех пользователей.' %
         self.client_nickname)
     request = {
         ACTION: USER_REQUEST,
         TIME: time(),
         ACCOUNT_NAME: self.client_nickname
     }
     with socket_lock:
         send_message(self.client_socket, request)
         response = receive_message(self.client_socket)
     if RESPONSE in response and response[RESPONSE] == 202:
         self.database.add_existing_users(response[LIST_INFO])
     else:
         SOCKET_LOGGER.error(
             'Не удалось обновить список известных пользователей.')
Пример #7
0
 def request_contacts(self):
     """
     Requests a contact list from the server.
     If the status code of the server's response is 202, updates
     the contact list in the client's DB.
     """
     SOCKET_LOGGER.debug('Запрос списка контактов пользователя: %s' %
                         self.client_nickname)
     self.database.cleat_contact_list()
     request = {
         ACTION: GET_CONTACTS,
         TIME: time(),
         USER: self.client_nickname
     }
     SOCKET_LOGGER.debug('Сформирован запрос к серверу: %s' % request)
     with socket_lock:
         send_message(self.client_socket, request)
         response = receive_message(self.client_socket)
     SOCKET_LOGGER.debug('Получен ответ от сервера: %s' % response)
     if RESPONSE in response and response[RESPONSE] == 202:
         for contact in response[LIST_INFO]:
             self.database.add_user_to_contacts(contact)
     else:
         SOCKET_LOGGER.error('Не удалось обновить список контактов.')
Пример #8
0
    def establish_connection(self, ip_address: str, port: int):
        """
        Establishes connection to the server.
        Makes 5 attempts to do so, if unsuccessful, ends the cycle.
        If successful, sends the presence message to the server,
        and then, sends the encrypted password to the server to
        compare to the one stored in the server's DB.

        :param ip_address: server's IP address
        :param port: server's listening port
        """
        self.client_socket = socket(AF_INET, SOCK_STREAM)
        self.client_socket.settimeout(5)
        connected = False

        for i in range(5):
            SOCKET_LOGGER.info('Попытка соединения №%d' % (i + 1, ))
            try:
                self.client_socket.connect((ip_address, port))
            except (OSError, ConnectionRefusedError):
                pass
            else:
                connected = True
                break
            sleep(1)

        if not connected:
            SOCKET_LOGGER.critical(
                'Не удалось установить соединение с сервером.')
            raise ServerError('Не удалось установить соединение с сервером.')

        SOCKET_LOGGER.debug('Установлено соединение с сервером. '
                            'Начинаю процесс авторизации.')

        password_bytes = self.password.encode('utf-8')
        salt = self.client_nickname.lower().encode('utf-8')
        password_hash = pbkdf2_hmac('sha512', password_bytes, salt, 10000)
        password_hash_str = hexlify(password_hash)

        SOCKET_LOGGER.debug('Подготовлен хэш пароля: %s' % password_hash_str)

        self.pubkey = self.keys.publickey().export_key().decode('ascii')
        with socket_lock:
            msg = self.establish_presence()
            try:
                send_message(self.client_socket, msg)
                server_response = receive_message(self.client_socket)
                SOCKET_LOGGER.debug('Ответ сервера - %s' % server_response)
                if RESPONSE in server_response:
                    if server_response[RESPONSE] == 400:
                        raise ServerError(server_response[ERROR])
                    elif server_response[RESPONSE] == 511:
                        resp_data = server_response[DATA]
                        resp_hash = new(password_hash_str,
                                        resp_data.encode('utf-8'), 'MD5')
                        digest = resp_hash.digest()
                        client_response = {
                            RESPONSE: 511,
                            DATA: b2a_base64(digest).decode('ascii')
                        }
                        send_message(self.client_socket, client_response)
                        self.process_answer(receive_message(
                            self.client_socket))
            except (OSError, JSONDecodeError):
                SOCKET_LOGGER.critical('В процессе авторизации потеряно '
                                       'соединение с сервером')
                raise ServerError('В процессе авторизации потеряно '
                                  'соединение с сервером')
            else:
                SOCKET_LOGGER.info('Соединение успешно установлено.')
Пример #9
0
    def authorize_client(self, message: dict, client: socket):
        """
        Handles the client's authorization. Checks if the username isn't already taken,
        then checks if the user is registered on the server. If those two checks pass,
        the method then initiates the exchange of encrypted passwords with the client.
        If the password is correct, the client is logged onto the server, otherwise the
        client is removed from the server and his socket gets closed.

        :param message: presence message
        :param client: client's socket
        """
        SERVER_LOGGER.debug('Старт процесса авторизации пользователя %s' %
                            message[USER])
        if message[USER][ACCOUNT_NAME] in self.nicknames.keys():
            response = {RESPONSE: 400, ERROR: 'Имя пользователя уже занято'}
            try:
                send_message(client, response)
            except OSError as e:
                SERVER_LOGGER.debug('Произошла ошибка: %s' % e)
                pass
            self.clients_list.remove(client)
            client.close()
        elif not self.server_db.check_existing_user(
                message[USER][ACCOUNT_NAME]):
            response = {
                RESPONSE: 400,
                ERROR: 'Пользователь не зарегистрирован'
            }
            try:
                SERVER_LOGGER.debug('Пользователь %s не зарегистрирован' %
                                    message[USER][ACCOUNT_NAME])
                send_message(client, response)
            except OSError as e:
                SERVER_LOGGER.debug('Произошла ошибка: %s' % e)
                pass
        else:
            SERVER_LOGGER.debug('Начало проверки пароля')
            random_string = hexlify(urandom(64))
            auth_response = {
                RESPONSE: 511,
                DATA: random_string.decode('ascii')
            }
            pwd_hash = new(
                self.server_db.get_user_pwd_hash(message[USER][ACCOUNT_NAME]),
                random_string, 'MD5')
            pwd_digest = pwd_hash.digest()
            SERVER_LOGGER.debug('Подготовлено сообщение для авторизации: %s' %
                                auth_response)
            try:
                send_message(client, auth_response)
                client_response = receive_message(client)
            except OSError as e:
                SERVER_LOGGER.debug('Ошибка при авторизации: ', exc_info=e)
                client.close()
                return
            client_digest = a2b_base64(client_response[DATA])
            if RESPONSE in client_response and \
                    client_response[RESPONSE] == 511 and \
                    compare_digest(pwd_digest, client_digest):
                self.nicknames[message[USER][ACCOUNT_NAME]] = client
                client_addr, client_port = client.getpeername()
                try:
                    send_message(client, {RESPONSE: 200})
                except OSError:
                    self.delete_client(client)
                self.server_db.login_user(message[USER][ACCOUNT_NAME],
                                          client_addr, client_port,
                                          message[USER][PUBLIC_KEY])
            else:
                response = {RESPONSE: 400, ERROR: 'Неверный пароль'}
                try:
                    send_message(client, response)
                except OSError:
                    pass
                self.clients_list.remove(client)
                client.close()