예제 #1
0
    def run(self):
        '''Метод содержащий основной цикл работы транспортного потока.'''
        logger.debug('Запущен процесс - приёмник собщений с сервера.')
        while self.running:
            # Отдыхаем секунду и снова пробуем захватить сокет.
            # если не сделать тут задержку, то отправка может достаточно долго
            # ждать освобождения сокета.
            time.sleep(1)
            message = None
            with socket_lock:
                try:
                    self.transport.settimeout(0.5)
                    message = get_message(self.transport)
                except OSError as err:
                    if err.errno:
                        logger.critical(f'Потеряно соединение с сервером.')
                        self.running = False
                        self.connection_lost.emit()
                # Проблемы с соединением
                except (ConnectionError, ConnectionAbortedError,
                        ConnectionResetError, json.JSONDecodeError, TypeError):
                    logger.debug(f'Потеряно соединение с сервером.')
                    self.running = False
                    self.connection_lost.emit()
                finally:
                    self.transport.settimeout(5)

            # Если сообщение получено, то вызываем функцию обработчик:
            if message:
                logger.debug(f'Принято сообщение с сервера: {message}')
                self.process_server_ans(message)
예제 #2
0
 def remove_contact(self, contact):
     '''Метод отправляющий на сервер сведения о удалении контакта.'''
     logger.debug(f'Deleting contact {contact}')
     req = {
         ACTION: REMOVE_CONTACT,
         TIME: time.time(),
         USER: self.username,
         ACCOUNT_NAME: contact
     }
     with socket_lock:
         send_message(self.transport, req)
         self.process_server_ans(get_message(self.transport))
예제 #3
0
 def send_message(self, to, message):
     '''Метод отправляющий на сервер сообщения для пользователя.'''
     message_dict = {
         ACTION: MESSAGE,
         SENDER: self.username,
         DESTINATION: to,
         TIME: time.time(),
         MESSAGE_TEXT: message
     }
     logger.debug(f'Сформирован словарь сообщения: {message_dict}')
     # Необходимо дождаться освобождения сокета для отправки сообщения
     with socket_lock:
         send_message(self.transport, message_dict)
         self.process_server_ans(get_message(self.transport))
         logger.info(f'Отправлено сообщение для пользователя {to}')
예제 #4
0
 def key_request(self, user):
     '''Метод запрашивающий с сервера публичный ключ пользователя.'''
     logger.debug(f'Public key request for {user}')
     req = {
         ACTION: PUBLIC_KEY_REQUEST,
         TIME: time.time(),
         ACCOUNT_NAME: user
     }
     with socket_lock:
         send_message(self.transport, req)
         ans = get_message(self.transport)
     if RESPONSE in ans and ans[RESPONSE] == 511:
         return ans[DATA]
     else:
         logger.error(f'Unable to get key for {user}.')
예제 #5
0
 def user_list_update(self):
     '''Метод обновляющий с сервера список пользователей.'''
     logger.debug(f'Запрос списка известных пользователей {self.username}')
     req = {
         ACTION: USERS_REQUEST,
         TIME: time.time(),
         ACCOUNT_NAME: self.username
     }
     with socket_lock:
         send_message(self.transport, req)
         ans = get_message(self.transport)
     if RESPONSE in ans and ans[RESPONSE] == 202:
         self.database.add_users(ans[LIST_INFO])
     else:
         logger.error('Unable to update known users list.')
예제 #6
0
 def contacts_list_update(self):
     '''Метод обновляющий с сервера список контактов.'''
     self.database.contacts_clear()
     logger.debug(f'Запрос контакт листа для пользователся {self.name}')
     req = {ACTION: GET_CONTACTS, TIME: time.time(), USER: self.username}
     logger.debug(f'Сформирован запрос {req}')
     with socket_lock:
         send_message(self.transport, req)
         ans = get_message(self.transport)
     logger.debug(f'Получен ответ {ans}')
     if RESPONSE in ans and ans[RESPONSE] == 202:
         for contact in ans[LIST_INFO]:
             self.database.add_contact(contact)
     else:
         logger.error('Не удалось обновить список контактов.')
예제 #7
0
    def run(self):
        '''Метод основной цикл потока.'''
        # Инициализация Сокета
        self.init_socket()

        # Основной цикл программы сервера
        while self.running:
            # Ждём подключения, если таймаут вышел, ловим исключение.
            try:
                client, client_address = self.sock.accept()
            except OSError:
                pass
            else:
                logger.info(f'Установлено соедение с ПК {client_address}')
                client.settimeout(5)
                self.clients.append(client)

            recv_data_lst = []
            send_data_lst = []
            err_lst = []
            # Проверяем на наличие ждущих клиентов
            try:
                if self.clients:
                    recv_data_lst, self.listen_sockets, self.error_sockets = select.select(
                        self.clients, self.clients, [], 0)
            except OSError as err:
                logger.error(f'Ошибка работы с сокетами: {err.errno}')

            # принимаем сообщения и если ошибка, исключаем клиента.
            if recv_data_lst:
                for client_with_message in recv_data_lst:
                    try:
                        self.process_client_message(
                            get_message(client_with_message),
                            client_with_message)
                    except (OSError, json.JSONDecodeError, TypeError) as err:
                        logger.debug(f'Getting data from client exception.',
                                     exc_info=err)
                        self.remove_client(client_with_message)
예제 #8
0
 def autorize_user(self, message, sock):
     '''Метод реализующий авторизцию пользователей.'''
     # Если имя пользователя уже занято то возвращаем 400
     logger.debug(f'Start auth process for {message[USER]}')
     if message[USER][ACCOUNT_NAME] in self.names.keys():
         response = RESPONSE_400
         response[ERROR] = 'Имя пользователя уже занято.'
         try:
             logger.debug(f'Username busy, sending {response}')
             send_message(sock, response)
         except OSError:
             logger.debug('OS Error')
             pass
         self.clients.remove(sock)
         sock.close()
     # Проверяем что пользователь зарегистрирован на сервере.
     elif not self.database.check_user(message[USER][ACCOUNT_NAME]):
         response = RESPONSE_400
         response[ERROR] = 'Пользователь не зарегистрирован.'
         try:
             logger.debug(f'Unknown username, sending {response}')
             send_message(sock, response)
         except OSError:
             pass
         self.clients.remove(sock)
         sock.close()
     else:
         logger.debug('Correct username, starting passwd check.')
         # Иначе отвечаем 511 и проводим процедуру авторизации
         # Словарь - заготовка
         message_auth = RESPONSE_511
         # Набор байтов в hex представлении
         random_str = binascii.hexlify(os.urandom(64))
         # В словарь байты нельзя, декодируем (json.dumps -> TypeError)
         message_auth[DATA] = random_str.decode('ascii')
         # Создаём хэш пароля и связки с рандомной строкой, сохраняем
         # серверную версию ключа
         hash = hmac.new(
             self.database.get_hash(message[USER][ACCOUNT_NAME]),
             random_str, 'MD5')
         digest = hash.digest()
         logger.debug(f'Auth message = {message_auth}')
         try:
             # Обмен с клиентом
             send_message(sock, message_auth)
             ans = get_message(sock)
         except OSError as err:
             logger.debug('Error in auth, data:', exc_info=err)
             sock.close()
             return
         client_digest = binascii.a2b_base64(ans[DATA])
         # Если ответ клиента корректный, то сохраняем его в список
         # пользователей.
         if RESPONSE in ans and ans[RESPONSE] == 511 and hmac.compare_digest(
                 digest, client_digest):
             self.names[message[USER][ACCOUNT_NAME]] = sock
             client_ip, client_port = sock.getpeername()
             try:
                 send_message(sock, RESPONSE_200)
             except OSError:
                 self.remove_client(message[USER][ACCOUNT_NAME])
             # добавляем пользователя в список активных и если у него изменился открытый ключ
             # сохраняем новый
             self.database.user_login(message[USER][ACCOUNT_NAME],
                                      client_ip, client_port,
                                      message[USER][PUBLIC_KEY])
         else:
             response = RESPONSE_400
             response[ERROR] = 'Неверный пароль.'
             try:
                 send_message(sock, response)
             except OSError:
                 pass
             self.clients.remove(sock)
             sock.close()
예제 #9
0
    def connection_init(self, port, ip):
        '''Метод отвечающий за устанновку соединения с сервером.'''
        # Инициализация сокета и сообщение серверу о нашем появлении
        self.transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Таймаут необходим для освобождения сокета.
        self.transport.settimeout(5)

        # Соединяемся, 5 попыток соединения, флаг успеха ставим в True если
        # удалось
        connected = False
        for i in range(5):
            logger.info(f'Попытка подключения №{i + 1}')
            try:
                self.transport.connect((ip, port))
            except (OSError, ConnectionRefusedError):
                pass
            else:
                connected = True
                logger.debug("Connection established.")
                break
            time.sleep(1)

        # Если соединится не удалось - исключение
        if not connected:
            logger.critical('Не удалось установить соединение с сервером')
            raise ServerError('Не удалось установить соединение с сервером')

        logger.debug('Starting auth dialog.')

        # Запускаем процедуру авторизации
        # Получаем хэш пароля
        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)

        logger.debug(f'Passwd hash ready: {passwd_hash_string}')

        # Получаем публичный ключ и декодируем его из байтов
        pubkey = self.keys.publickey().export_key().decode('ascii')

        # Авторизируемся на сервере
        with socket_lock:
            presense = {
                ACTION: PRESENCE,
                TIME: time.time(),
                USER: {
                    ACCOUNT_NAME: self.username,
                    PUBLIC_KEY: pubkey
                }
            }
            logger.debug(f"Presense message = {presense}")
            # Отправляем серверу приветственное сообщение.
            try:
                send_message(self.transport, presense)
                ans = get_message(self.transport)
                logger.debug(f'Server response = {ans}.')
                # Если сервер вернул ошибку, бросаем исключение.
                if RESPONSE in ans:
                    if ans[RESPONSE] == 400:
                        raise ServerError(ans[ERROR])
                    elif ans[RESPONSE] == 511:
                        # Если всё нормально, то продолжаем процедуру
                        # авторизации.
                        ans_data = ans[DATA]
                        hash = hmac.new(passwd_hash_string,
                                        ans_data.encode('utf-8'), 'MD5')
                        digest = hash.digest()
                        my_ans = RESPONSE_511
                        my_ans[DATA] = binascii.b2a_base64(digest).decode(
                            'ascii')
                        send_message(self.transport, my_ans)
                        self.process_server_ans(get_message(self.transport))
            except (OSError, json.JSONDecodeError) as err:
                logger.debug(f'Connection error.', exc_info=err)
                raise ServerError('Сбой соединения в процессе авторизации.')