class Server(threading.Thread): """ Основной класс сервера. Принимает содинения, словари - пакеты от клиентов, обрабатывает поступающие сообщения. Работает в качестве отдельного потока. """ # Дескриптор порта и ip адреса port = Port() address = Address() def __init__(self, listen_address, listen_port, database): # Параментры подключения self.listen_address = listen_address self.listen_port = listen_port self.database = database # Сокет, через который будет осуществляться работа self.main_socket = None # Устанавливаем список клиентов и очередь сообщений self.clients = [] # Сокеты self.listen_sockets = None self.error_sockets = None # Флаг продолжения работы self.running = True # Словарь имен пользователей и соответствующие им сокеты self.names = dict() # Конструктор предка super().__init__() def run(self): """Метод основной цикл потока""" self.start_socket() # Основной цикл программы сервера while self.running: try: client, client_address = self.main_socket.accept() except OSError: pass else: LOGGER.info(f'Клиент {client_address} подключился.') client.settimeout(5) self.clients.append(client) recv_lst = [] send_lst = [] err_lst = [] # Проверка на наличие ждущих клиентов try: if self.clients: recv_lst, self.listen_sockets, self.error_sockets = select.select( self.clients, self.clients, [], 0) except OSError as e: LOGGER.error(f'Ошибка сокетов: {e}') # Принимаем сообщения и если они есть, то заполняем словарь, если # ошибка - отключаем клиента if recv_lst: for client_with_msg in recv_lst: try: self.process_client_message(get_message(client_with_msg), client_with_msg) except (OSError, json.JSONDecodeError, TypeError) as e: LOGGER.debug(f'Получение данных из исключения клиента.', exc_info=e) self.clients.remove(client_with_msg) def start_socket(self): """Метод инициализатор сокета.""" LOGGER.info( f'Сервер запущен. Порт для подключений: {self.listen_port}.' f'Адрес с котрого принимаются сообщения: {self.listen_address}. ' ) # Готовим сокет transport = socket.socket(socket.AF_INET, socket.SOCK_STREAM) transport.bind((self.listen_address, self.listen_port)) transport.settimeout(0.5) # Слушаем порт self.main_socket = transport self.main_socket.listen(MAX_CONNECTIONS) def remove_client(self, client): """ Функция обработчик клиента с которым прервана связь. Ищет клиента и удаляет его из списков и базы: :param client: :return: """ LOGGER.info(f'Клиент {client.getpeername()} отключился от сервера.') for name in self.names: if self.names[name] == client: self.database.user_logout(name) del self.names[name] break self.clients.remove(client) client.close() def process_message(self, message): """ Функция адресной отправки сообщения определённому клиенту. Принимает словарь сообщение, список зарегистрированых пользователей и слушающие сокеты. Ничего не возвращает. """ if message[DESTINATION] in self.names and self.names[message[DESTINATION]] in self.listen_sockets: try: send_message(self.names[message[DESTINATION]], message) LOGGER.info( f'Отправлено сообщение пользователю {message[DESTINATION]} ' f'от пользователя {message[SENDER]}.') except OSError: self.remove_client(self.names[message[DESTINATION]]) elif message[DESTINATION] in self.names and self.names[message[DESTINATION]] not in self.listen_sockets: LOGGER.error( f'Связь с пользователем {message[DESTINATION]} потеряна. ' f'Соединение закрыто, отправка сообщения невозможна.') self.remove_client(self.names[message[DESTINATION]]) else: LOGGER.error( f'Пользователь {message[DESTINATION]} не зарегистрирован на сервере, ' f'отправка сообщения невозможна.') @login_required def process_client_message(self, message, client): """ Обработчик сообщений от клиентов """ LOGGER.debug(f'Разбор сообщения от клиента : {message}') # Если это сообщение о присутствии, принимаем и отвечаем if ACTION in message and message[ACTION] == PRESENCE and TIME in message and USER in message: # Если сообщение о присутствии то вызываем функцию авторизации. self.autorized_user(message, client) # Если это сообщение, то добавляем его в очередь elif ACTION in message and message[ACTION] == MESSAGE and TIME in message and DESTINATION in message \ and SENDER in message and MESSAGE_TEXT in message and self.names[message[SENDER]] == client: if message[DESTINATION] in self.names: self.database.process_message(message[SENDER], message[DESTINATION]) self.process_message(message) try: send_message(client, RESPONSE_200) except OSError: self.remove_client(client) else: response = RESPONSE_400 response[ERROR] = 'Пользователь не зарегистрирован.' try: send_message(client, response) except OSError: pass return # Если клиент выходит elif ACTION in message and message[ACTION] == EXIT and ACCOUNT_NAME in message and \ self.names[message[ACCOUNT_NAME]] == client: self.remove_client(client) LOGGER.info(f'Клиент {message[ACCOUNT_NAME]} коректно вышел') # Если запрос контактов elif ACTION in message and message[ACTION] == GET_CONTACTS and USER in message and \ self.names[message[USER]] == client: response = RESPONSE_202 response[LIST_INFO] = self.database.get_contacts(message[USER]) try: send_message(client, response) except OSError: self.remove_client(client) # Если запрос на добавление контакта elif ACTION in message and message[ACTION] == ADD_CONTACT and ACCOUNT_NAME in message and USER in message \ and self.names[message[USER]] == client: self.database.add_contact(message[USER], message[ACCOUNT_NAME]) try: send_message(client, RESPONSE_200) except OSError: self.remove_client(client) # Если запрос на удаление контакта elif ACTION in message and message[ACTION] == REMOVE_CONTACT and ACCOUNT_NAME in message and USER in message \ and self.names[message[USER]] == client: self.database.remove_contact(message[USER], message[ACCOUNT_NAME]) try: send_message(client, RESPONSE_200) except OSError: self.remove_client(client) # Если запрос известных пользователей elif ACTION in message and message[ACTION] == USERS_REQUEST and ACCOUNT_NAME in message \ and self.names[message[ACCOUNT_NAME]] == client: response = RESPONSE_202 response[LIST_INFO] = [user[0] for user in self.database.users_list()] try: send_message(client, response) except OSError: self.remove_client(client) # Если запрос публичного ключа пользователя elif ACTION in message and message[ACTION] == PUBLIC_KEY_REQUEST and ACCOUNT_NAME in message: response = RESPONSE_511 response[DATA] = self.database.get_public_key(message[ACCOUNT_NAME]) if response[DATA]: try: send_message(client, response) except OSError: self.remove_client(client) else: response = RESPONSE_400 response[ERROR] = 'Публичный ключ не найден для данного пользователя.' try: send_message(client, response) except OSError: self.remove_client(client) # Иначе отдаём Bad request else: response = RESPONSE_400 response[ERROR] = 'Запрос некорректен.' try: send_message(client, response) except OSError: self.remove_client(client) def autorized_user(self, message, client): """ Функция реализующая авторизацию пользователей. :param message: :param client: :return: """ # Если имя пользователя уже занято то возвращаем 400 LOGGER.debug(f'Авторизация для пользователя: {message[USER]}') if message[USER][ACCOUNT_NAME] not in self.names.keys(): response = RESPONSE_400 response[ERROR] = 'Имя пользователя уже занято.' try: LOGGER.debug(f'Имя занято {response}') send_message(client, response) except OSError: LOGGER.debug(f'Ошибка системы') pass self.clients.remove(client) client.close() # Проверяем что пользователь зарегистрирован на сервере. elif not self.database.check_user(message[USER][ACCOUNT_NAME]): response = RESPONSE_400 response[ERROR] = 'Пользователь не зарегистрирован.' try: LOGGER.debug(f'Неизвестное имя пользователя {response}') send_message(client, response) except OSError: pass self.clients.remove(client) client.close() else: LOGGER.debug('Имя пользователя корректно. Проверяем пароль.') # Словарь - заготовка 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) digest = hash.digest() LOGGER.debug(f'Auth message = {message_auth}') try: # Обмен с клиентом send_message(client, message_auth) ans = get_message(client) except OSError as err: LOGGER.debug('Ошибка авторизации:', exc_info=err) client.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]] = client client_ip, client_port = client.getpeername() try: send_message(client, 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(client, response) except OSError: pass self.clients.remove(client) client.close() def service_update_lists(self): """ Функция реализующая отправку сервисного сообщения "205" клиентам. :return: """ for client in self.names: try: send_message(self.names[client], RESPONSE_205) except OSError: self.remove_client(self.names[client])
class MessageProcessor(threading.Thread): ''' Основной класс сервера. Принимает содинения, словари - пакеты от клиентов, обрабатывает поступающие сообщения. Работает в качестве отдельного потока. ''' port = Port() def __init__(self, listen_address, listen_port, database): # Параментры подключения self.listen_address = listen_address self.listen_port = listen_port # БД сервера self.database = database self.sock = None # Список подключённых клиентов. self.clients = [] # Сокеты self.listen_sockets = None self.error_sockets = None # Флаг продолжения работы self.running = True # Словарь содержащий сопоставленные имена и соответствующие им сокеты. self.names = dict() super().__init__() def run(self): '''основной цикл потока.''' self.init_socket() while self.running: try: client_sock, address = self.sock.accept() except OSError: pass else: server_logger.info(f'Установлено соедение {address}') client_sock.settimeout(5) self.clients.append(client_sock) receive_lst = [] send_lst = [] err_lst = [] try: if self.clients: receive_lst, self.listen_sockets, self.error_sockets = \ select.select(self.clients, self.clients, [], 0) except OSError: server_logger.error("Ошибка работы с сокетами") # принимаем сообщения и если ошибка, исключаем клиента. if receive_lst: for client_with_message in receive_lst: try: self.client_requests_handler( get_message(client_with_message), client_with_message) except (OSError, json.JSONDecodeError, TypeError) as err: server_logger.debug( f'Getting data from client exception.', exc_info=err) self.remove_client(client_with_message) def remove_client(self, client): ''' обработчик клиента с которым прервана связь. Ищет клиента и удаляет его из списков и базы: ''' server_logger.info( f'Клиент {client.getpeername()} отключился от сервера.') for name in self.names: if self.names[name] == client: self.database.logout(name) del self.names[name] break self.clients.remove(client) client.close() def init_socket(self): '''Инициализация сокета''' server_logger.info( f'Сервер запущен.' f'Адрес и порт : {self.listen_port}, {self.listen_address} ' f'Если адрес не указан, принимаются соединения с любых адресов.') # создаем сокет server_sock = socket(AF_INET, SOCK_STREAM) server_sock.bind((self.listen_address, self.listen_port)) server_sock.settimeout(3) self.sock = server_sock self.sock.listen(5) def process_message(self, message): '''Отправка сообщения клиенту.''' if message['to'] in self.names and self.names[ message['to']] in self.listen_sockets: try: send_message(self.names[message['to']], message) server_logger.info( f'Отправлено сообщение пользователю {message["to"]} от пользователя {message["from"]}.' ) except OSError: self.remove_client(message["to"]) elif message["to"] in self.names and self.names[ message["to"]] not in self.listen_sockets: server_logger.error( f'Связь с клиентом {message["to"]} была потеряна. Соединение закрыто, доставка невозможна.' ) self.remove_client(self.names[message["to"]]) else: server_logger.error( f'Пользователь {message["to"]} не зарегистрирован на сервере, отправка сообщения невозможна.' ) # @login_required def client_requests_handler(self, message, client): """ Валидация сообщения от клиента и отправка ответа сервера """ server_logger.debug(f'Сообщение : {message} клиента {client}') # обрабатываем сообщение присутствие и регистрация пользователя if 'action' in message and message['action'] == 'presence' and 'time' in message \ and 'user' in message: server_logger.debug(f"Принято и обработано presence сообщение") self.autorize_user(message, client) # если есть сообщение - то отправляем получателю elif 'action' in message and message['action'] == 'message' and 'time' in message \ and 'message_text' in message and 'to' in message and 'from' in message \ and self.names[message['from']] == client: server_logger.debug( f"Принято сообщение --- > проверка пользователя -- > отправка получателю" ) if message['to'] in self.names: self.database.count_messages(message['from'], message['to']) self.process_message(message) try: send_message(client, {'response': 200}) except OSError: self.remove_client(client) else: response = {'response': 400, 'error': None} response[ 'error'] = 'Пользователь не зарегистрирован на сервере.' try: send_message(client, response) except OSError: pass return # если клиент выходит elif 'action' in message and message['action'] == 'exit' and 'account_name' in message and \ self.names[message['account_name']] == client: self.remove_client(client) # запрос списка контактов elif 'action' in message and message['action'] == 'get_contacts' and 'user' in message and \ self.names[message['user']] == client: response = {'response': 202, 'data_list': None} response['data_list'] = self.database.get_contacts(message['user']) try: send_message(client, response) except OSError: self.remove_client(client) # добавление контакта elif 'action' in message and message['action'] == 'add' and 'account_name' in message and 'user' in message \ and self.names[message['user']] == client: self.database.add_contact(message['user'], message['account_name']) try: send_message(client, {"response": 200}) except OSError: self.remove_client(client) # удаление контакта elif 'action' in message and message['action'] == 'remove' and 'account_name' in message and 'user' in message \ and self.names[message['user']] == client: self.database.remove_contact(message['user'], message['account_name']) try: send_message(client, {"response": 200}) except OSError: self.remove_client(client) # запрос списка всех пользователей elif 'action' in message and message['action'] == 'get_users' and 'account_name' in message \ and self.names[message['account_name']] == client: response = {'response': 202, 'data_list': None} response['data_list'] = [ user[0] for user in self.database.users_list() ] try: send_message(client, response) except OSError: self.remove_client(client) # запрос публичного ключа пользователя elif 'action' in message and message[ 'action'] == 'pubkey_need' and 'account_name' in message: response = {'response': 511, 'bin': None} response['bin'] = self.database.get_pubkey(message['account_name']) # может быть, что ключа ещё нет (пользователь никогда не логинился, # тогда шлём 400) if response['bin']: try: send_message(client, response) except OSError: self.remove_client(client) else: response = {"response": 400, "error": None} response[ "error"] = 'Нет публичного ключа для данного пользователя' try: send_message(client, response) except OSError: self.remove_client(client) else: response = {"response": 400, "error": None} response["error"] = 'Bad Request' try: send_message(client, response) except OSError: self.remove_client(client) def autorize_user(self, message, sock): '''Авторизация пользователя''' server_logger.debug(f'Start auth process for {message["user"]}') if message["user"]['account_name'] in self.names.keys(): response = {"response": 400, "error": None} response["error"] = 'Имя пользователя уже занято.' server_logger.debug(f"Имя пользователя уже занято.") try: server_logger.debug(f'Username busy, sending {response}') send_message(sock, response) except OSError: server_logger.debug('OS Error') pass self.clients.remove(sock) sock.close() # Если пользователь не зарегистрирован - отправляем 400 ошибку elif not self.database.check_user(message["user"]['account_name']): server_logger.debug( f"Пользователь : {message['user']['account_name']}не зарегистрирован." ) response = {"response": 400, "error": None} response["error"] = 'Пользователь не зарегистрирован.' try: send_message(sock, response) server_logger.debug(f'Unknown username, sending {response}') except OSError: pass self.clients.remove(sock) server_logger.debug( f"Пользователь удален из списка потенциальных клиентов") sock.close() else: server_logger.debug('Correct username, starting password check.') message_auth = {'response': 511, 'bin': None} random_str = binascii.hexlify(os.urandom(64)) message_auth['bin'] = random_str.decode('ascii') hash = hmac.new( self.database.get_hash(message["user"]['account_name']), random_str, 'MD5') digest = hash.digest() server_logger.debug(f'Auth message = {message_auth}') try: send_message(sock, message_auth) ans = get_message(sock) except OSError as err: server_logger.debug('Error in auth, data:', exc_info=err) sock.close() return client_digest = binascii.a2b_base64(ans['bin']) # Если ответ клиента корректный, то сохраняем его в список # пользователей. 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() server_logger.debug( f"Ответ клиента корректный - клиент сохранен в список пользователей" ) try: send_message(sock, {"response": 200}) except OSError: self.remove_client(message["user"]['account_name']) # добавляем пользователя в список активных и если у него изменился открытый ключ # сохраняем новый self.database.login(message["user"]['account_name'], client_ip, client_port, message['user']['pubkey']) else: response = {"response": 400, "error": None} response["error"] = 'Неверный пароль.' try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() def service_update_lists(self): '''Отправка 205 сообщения''' for client in self.names: try: send_message(self.names[client], {"response": 205}) except OSError: self.remove_client(self.names[client])
class Server(threading.Thread, metaclass=ServerVerifier): port = Port() address = Host() def __init__(self, server_address, server_port, connections, database): self.address = server_address self.port = server_port self.connections = connections self.database = database super().__init__() self.clients = [] self.messages = [] self.names = dict() def autorize_user(self, message, sock): if message[USER][ACCOUNT_NAME] in self.names.keys(): response = {RESPONSE: 400, ERROR: 'Имя пользователя уже занято'} try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() # Проверяем что пользователь зарегистрирован на сервере. elif not self.database.check_user(message[USER][ACCOUNT_NAME]): response = { RESPONSE: 400, ERROR: 'Пользователь не зарегистрирован.' } try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() else: # Иначе отвечаем 501 и проводим процедуру авторизации # Словарь - заготовка message_auth = {RESPONSE: 511, DATA: None} # Набор байтов в 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) digest = hash.digest() try: # Обмен с клиентом send_message(sock, message_auth) ans = get_message(sock) except OSError: 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, ERROR: 'Неверный пароль.'} try: send_message(sock, response) except OSError: pass self.clients.remove(sock) sock.close() def init_socket(self): SERV = socket(AF_INET, SOCK_STREAM) try: SERV.bind((self.address, self.port)) except OSError as err: print(err) SERV_LOG.debug('Запуск сервера') SERV.settimeout(0.5) self.sock = SERV self.sock.listen(self.connections) print(f'Сервер запущен {self.address}:{self.port}') def run(self): self.init_socket() while True: try: client, client_addr = self.sock.accept() except OSError: pass else: SERV_LOG.info(f'Подключился клиент: {client_addr}') self.clients.append(client) read_clients_lst = [] send_clients_lst = [] err_lst = [] try: if self.clients: read_clients_lst, send_clients_lst, err_lst = select( self.clients, self.clients, [], 0) except OSError: pass if read_clients_lst: for client_with_message in read_clients_lst: try: self.do_answer(get_message(client_with_message), self.messages, client_with_message, self.clients, self.names) except Exception: SERV_LOG.info( f'Клиент {client_with_message.getpeername()}' f' отключился от сервера') for name in self.names: if self.names[name] == client_with_message: self.database.user_logout(name) del self.names[name] break self.clients.remove(client_with_message) with conflag_lock: new_connection = True for mess in self.messages: try: self.do_message(mess, self.names, send_clients_lst) except Exception: SERV_LOG.info(f'Связь с клиентом {mess[TO]} потеряна') self.clients.remove(self.names[mess[TO]]) self.database.user_logout(mess[TO]) del self.names[mess[TO]] self.messages.clear() # @log def do_answer(self, message, message_list, client, clients, names): """Обрабатывает сообщение от клиента и готовит ответ""" global new_connection SERV_LOG.debug(f'Принято сообщение от клиента: {message}') if ACTION in message and message[ACTION] == PRESENCE \ and TIME in message and USER in message: if message[USER][ACCOUNT_NAME] not in names.keys(): names[message[USER][ACCOUNT_NAME]] = client client_ip, client_port = client.getpeername() self.database.user_login(message[USER][ACCOUNT_NAME], client_ip, client_port) send_message(client, {RESPONSE: 200}) with conflag_lock: new_connection = True SERV_LOG.debug('Ответ подготовлен: {RESPONSE: 200}') else: SERV_LOG.debug("Ответ подготовлен: {RESPONSE: 400\nERROR: \ 'Имя пользователя уже занято'}") send_message(client, { RESPONSE: 400, ERROR: 'Имя пользователя уже занято' }) clients.remove(client) client.close() return elif ACTION in message and message[ACTION] == MSG and TIME in message \ and MESSAGE in message and TO in message: message_list.append(message) self.database.process_user_message(message[SENDER], message[TO]) return elif ACTION in message and message[ACTION] == QUIT and \ ACCOUNT_NAME in message: self.database.user_logout(message[ACCOUNT_NAME]) SERV_LOG.debug('Удален клиент из таблицы активных клиентов') clients.remove(names[message[ACCOUNT_NAME]]) names[message[ACCOUNT_NAME]].close() del names[message[ACCOUNT_NAME]] with conflag_lock: new_connection = True return elif ACTION in message and message[ACTION] == GET_CONTACTS and \ USER in message and self.names[message[USER]] == client: response = {RESPONSE: 202} response[LIST_INFO] = self.database.get_contacts(message[USER]) send_message(client, response) SERV_LOG.info(f'Отправлен ответ на запрос GET_CONTACTS {response}') elif ACTION in message and message[ACTION] == ADD_CONTACT and \ ACCOUNT_NAME in message and USER in message \ and self.names[message[USER]] == client: self.database.add_contact(message[USER], message[ACCOUNT_NAME]) send_message(client, {RESPONSE: 200}) elif ACTION in message and message[ACTION] == REMOVE_CONTACT and \ ACCOUNT_NAME in message and USER in message and \ self.names[message[USER]] == client: self.database.remove_contact(message[USER], message[ACCOUNT_NAME]) send_message(client, {RESPONSE: 200}) elif ACTION in message and message[ACTION] == USERS_REQUEST and \ ACCOUNT_NAME in message and self.names[message[ACCOUNT_NAME]] ==\ client: response[LIST_INFO] = [ user[0] for user in self.database.users_list() ] send_message(client, response) SERV_LOG.info( f'Отправлен ответ на запрос USERS_REQUEST {response}') else: SERV_LOG.debug("Ответ подготовлен: {RESPONSE: 400\nERROR: \ 'Bad request'}") send_message(client, {RESPONSE: 400, ERROR: 'Bad request'}) return @log def do_message(self, message, names, socks): """Отправляет сообщение определённому клиенту""" if message[TO] in names and names[message[TO]] in socks: send_message(names[message[TO]], message) SERV_LOG.info(f'Отправлено сообщение пользователю {message[TO]} ' f' от пользователя {message[SENDER]}') elif message[TO] in names and names[message[TO]] not in socks: raise ConnectionError else: SERV_LOG.error(f'Пользователь {message[TO]} не зарегистрирован на \ на сервере. Отправка невозможна')
class Client: __slots__ = ('_addr', '_port', 'user', 'logger', 'socket', 'connected', 'listener', 'sender', 'encryptors', 'priv_key', 'storage', 'subs', 'answers') TCP = (AF_INET, SOCK_STREAM) addr = Addr('_addr') port = Port('_port') def __init__(self, addr, port): self.logger = logging.getLogger(log_config.LOGGER_NAME) self.addr = addr self.port = port self.connected = False self.subs = {201: [], 202: [], 203: [], 204: [], 205: []} self.answers = queue.Queue() self.encryptors = {} @property def username(self): return self.user.username def get_encryptor(self, contact): return self.encryptors[contact] if contact in self.encryptors else None def set_encryptor(self, contact, value): self.encryptors[contact] = value def set_user(self, username, password): self.user = User(username, password) self.storage = ClientStorage(username) def start(self): self.socket = socket(*self.TCP) start_txt = f'Connect to {self.addr}:{self.port} as {self.user.username}...' self.logger.debug(start_txt) print(start_txt) self.__connect() @try_except_wrapper def __connect(self): self.socket.connect((self.addr, self.port)) self.connected = True print('Done') response = self.authorization() if response.code != OK: show_error(str(response.message)) self.logger.warning(response) return self.listener = ClientThread(self.__listen_server, self.logger) self.listener.start() @try_except_wrapper def __send_request(self, request): if not self.connected: return self.logger.debug(request) send_data(self.socket, request) @try_except_wrapper def __get_response(self): if not self.connected: return response = get_data(self.socket) self.logger.debug(response) return response @try_except_wrapper def authorization(self): pr_req = Request(RequestAction.PRESENCE, self.user.username) self.__send_request(pr_req) resp = self.__get_response() if resp is None: return Response(SERVER_ERROR) if resp.code != AUTH: return resp enc_pass = encrypt_rsa(import_pub_key(resp.message.encode()), self.user.password) auth_req = Request(RequestAction.AUTH, enc_pass.decode()) self.__send_request(auth_req) return self.__get_response() def get_chat_req(self, contact): req = Request(RequestAction.COMMAND, f'get_chat {self.user.username} {contact}') self.__send_request(req) def get_users_req(self): self.__send_request(Request(RequestAction.COMMAND, 'get_users')) def get_contacts_req(self): self.__send_request(Request(RequestAction.COMMAND, 'get_contacts')) @try_except_wrapper def start_chat(self, contact): key = self.storage.get_key(contact) if key is not None: self.set_encryptor(contact, ClientCrypt(key)) # self.encryptor = ClientCrypt(key) prv, pub = gen_keys() self.priv_key = prv msg = Msg(pub.export_key().decode(), self.username, contact) start_req = Request(RequestAction.START_CHAT, msg) self.__send_request(start_req) @try_except_wrapper def accepting_chat(self, resp_mes): r_msg = Msg.from_formated(resp_mes) pub = import_pub_key(r_msg.text.encode()) key = self.storage.get_key(r_msg.sender) if key is not None: encryptor = ClientCrypt(key) # self.encryptor = ClientCrypt(key) else: # self.encryptor = ClientCrypt.gen_secret(self.username, r_msg.sender) encryptor = ClientCrypt.gen_secret(self.username, r_msg.sender) self.storage.add_chat_key(r_msg.sender, encryptor.secret) self.set_encryptor(r_msg.sender, encryptor) enc_key = encrypt_rsa(pub, encryptor.secret) msg = Msg(enc_key.decode(), self.username, r_msg.sender) accept_req = Request(RequestAction.ACCEPT_CHAT, msg) self.__send_request(accept_req) @try_except_wrapper def accepted_chat(self, resp_mes): msg = Msg.from_formated(resp_mes) encryptor = self.get_encryptor(msg.sender) if encryptor is not None: return secret = decrypt_rsa(self.priv_key, msg.text.encode()) self.set_encryptor(msg.sender, ClientCrypt(secret)) self.storage.add_chat_key(msg.sender, secret) def add_contact(self, contact): if self.storage.get_contact(contact): return False self.storage.add_contact(contact) req = Request(RequestAction.COMMAND, f'add_contact {contact}') self.__send_request(req) def rem_contact(self, contact): self.storage.remove_contact(contact) req = Request(RequestAction.COMMAND, f'rem_contact {contact}') self.__send_request(req) def sync_contacts(self, contacts): for c in contacts: self.storage.append_contact(c) @try_except_wrapper def send_msg(self, text, to): encryptor = self.get_encryptor(to) text = encryptor.encript_msg(text.encode()).decode() msg = Msg(text, self.user, to) self.storage.add_message(msg.to, msg.text) request = Request(RequestAction.MESSAGE, msg) self.__send_request(request) def __listen_server(self): while self.connected: resp = get_data(self.socket) self.logger.debug(resp) if resp.type != RESPONSE: self.logger.warning(f'Received not RESPONSE:\n {resp}') continue if resp.code == ANSWER: self.answers.put(resp.message) print(f'server: {resp.message}') elif resp.code in self.subs.keys(): for s in self.subs[resp.code]: s(resp.message) else: print(resp.message) def subscribe(self, code, func): if code in self.subs.keys(): self.subs[code].append(func) else: self[code] = [func] def parse_recv_message(self, msg): msg = Msg.from_formated(msg) encryptor = self.get_encryptor(msg.sender) msg.text = encryptor.decrypt_msg(msg.text.encode()).decode() return msg.sender, msg.text
class Server(threading.Thread, metaclass=ServerVerifier): server_port = Port() def __init__(self, SETTINGS, address, port): self.address = address self.server_port = port self.socket = socket(AF_INET, SOCK_STREAM) self.socket.bind((self.address, self.server_port)) self.socket.listen(SETTINGS['MAX_CONNECTION']) self.socket.settimeout(0.1) self.db = ServerDB(SETTINGS['DATABASE']) self.new_connection = False self.clients_info = {} self.clients_hash = {} super().__init__() def presence(self, message, sock): """Метод реализующий идентификацию пользователей. """ # Если имя пользователя уже занято то возвращаем 400 if message["user"]["account_name"] in self.db.get_active_username(): print('Имя пользователя уже занято.') response = RESPONSE_400 response['error'] = 'Имя пользователя уже занято.' try: logger.debug(f'Username busy, sending {response}') send_message(sock, response) except OSError: logger.debug('OS Error') pass if self.clients_info.get(sock): self.clients_info.pop(sock) # Проверяем что пользователь зарегистрирован на сервере. elif not self.db.check_user(message["user"]["account_name"]): print('Пользователь не зарегистрирован.') response = RESPONSE_400 response['error'] = 'Пользователь не зарегистрирован.' try: logger.debug(f'Unknown username, sending {response}') send_message(sock, response) except OSError: pass if self.clients_info.get(sock): self.clients_info.pop(sock) else: logger.debug('Correct username, starting passwd check.') print('Correct username, starting passwd check.') # Иначе отвечаем 511 и высылаем запрос на авторизацию # Набор байтов в hex представлении random_str = binascii.hexlify(os.urandom(64)) message_auth = {'response': 511, 'data': random_str.decode('ascii')} # Создаём хэш пароля и связки с рандомной строкой hash = hmac.new(self.db.get_hash(message["user"]["account_name"]), random_str, 'MD5') digest = hash.digest() logger.debug(f'Auth message = {message_auth}') print(f'Auth message = {message_auth}') try: # Обмен с клиентом send_message(sock, message_auth) self.clients_hash[message["user"]["account_name"]] = digest except OSError as err: logger.debug('Error in auth, data:', exc_info=err) sock.close() return def autorize_user(self, message, sock): """Метод реализующий аутентификацию и авторизацию пользователей.""" client_digest = binascii.a2b_base64(message['data']) # Если ответ клиента корректный, то сохраняем его в список # пользователей. digest = self.clients_hash[message["username"]] if 'response' in message and message['response'] == 511 and hmac.compare_digest(digest, client_digest): self.clients_info[sock] = message["username"] client_ip, client_port = sock.getpeername() try: send_success_code(sock) except OSError: self.db.user_logout(message["username"]) # добавляем пользователя в список активных и если у него изменился открытый ключ # сохраняем новый self.db.user_login(message["username"], client_ip, client_port, message["keys"]) print('Корректный пароль') else: response = RESPONSE_400 response['error'] = 'Неверный пароль.' try: send_message(sock, response) except OSError: pass print('Неверный пароль') self.db.user_logout(message["username"]) if self.clients_info.get(sock): self.clients_info.pop(sock) def read_requests(self, r_clients, w_clients, all_clients_dict): """Читаем накопишившиеся запросы на чтение""" responses = {} # Словарь ответов сервера вида {сокет: запрос} for sock in r_clients: try: data = sock.recv(1024).decode('utf-8') responses[sock] = json.loads(data) if responses[sock]['action'] == 'presence': print(f"Клиент {sock.getpeername()[0]} {responses[sock]['user']['account_name']} вошел") all_clients_dict[sock] = responses[sock]['user']['account_name'] if responses[sock]['action'] == 'add_contact': self.db.add_contact(responses[sock]['from'], responses[sock]['to']) if responses[sock]['action'] == 'del_contact': self.db.remove_contact(responses[sock]['from'], responses[sock]['to']) except: print(f' r Клиент {all_clients_dict[sock]} {sock.getpeername()} отключился') self.db.user_logout(all_clients_dict[sock]) client_quit_req = { sock: {'action': 'quit', 'username': all_clients_dict[sock], "time": int(time.time())}} # оправляем всем сообщение что пользователь отключился self.write_responses(client_quit_req, w_clients, all_clients_dict) if all_clients_dict.get(sock): all_clients_dict.pop(sock) return responses def write_responses(self, requests, w_clients, all_clients_dict): """Отправляем накопишившиеся запросы на отправку""" for sock in w_clients: for _, request in requests.items(): try: print(request, sock) # Подготовить и отправить всем слушающм клиентам ответ в чат или личным сообщением if request['action'] == 'get_contacts': if request['user_login'] == all_clients_dict[sock]: # print(self.db.contact_list(request['user_login'])) send_contact_list(sock, self.db.contact_list(request['user_login'])) else: continue elif request['action'] == 'public_key_request': key = self.db.get_pubkey(request['username']) print(key) send_user_keys(sock, key) elif request['action'] == 'get_users': if request['user_login'] == all_clients_dict[sock]: send_users_list(sock, [x[0] for x in self.db.users_list()]) else: continue elif request['action'] == 'quit': send_message(sock, request) continue elif request['action'] == 'msg' and request['to'] == all_clients_dict[sock]: print(f"Сообщение от пользователя {request['from']} пользователю {request['to']}") self.db.user_send_message(request['from'], request['to']) send_message(sock, request) elif request['action'] == 'presence' and request['user']['account_name'] == all_clients_dict[sock]: self.new_connection = True self.presence(request, sock) elif request['action'] == 'join' and request['username'] == all_clients_dict[sock]: self.new_connection = True self.autorize_user(request, sock) else: continue except: # Сокет недоступен, клиент отключился print(f' w Клиент {sock.fileno()} {sock.getpeername()} отключился') self.db.user_logout(sock.fileno()) sock.close() if all_clients_dict.get(sock): all_clients_dict.pop(sock) def run(self): """ Основной цикл потока. Оюработка пришедших запросов. """ print('Сервер запущен') while True: try: conn, addr = self.socket.accept() # Проверка подключений except OSError as e: pass # timeout вышел else: print("Получен запрос на соединение от %s" % str(addr)) self.clients_info[conn] = '' finally: # Проверить наличие событий ввода-вывода wait = 10 r = [] w = [] try: r, w, e = select.select(list(self.clients_info.keys()), list(self.clients_info.keys()), [], wait) except: pass # Ничего не делать, если какой-то клиент отключился requests = self.read_requests(r, w, self.clients_info) # Сохраним запросы клиентов if requests: self.write_responses(requests, w, self.clients_info)
class Server: __slots__ = ('bind_addr', '_port', 'logger', 'socket', 'clients', 'users', 'storage', 'commands', 'listener') TCP = (AF_INET, SOCK_STREAM) TIMEOUT = 5 port = Port('_port') def __init__(self, bind_addr, port): self.logger = logging.getLogger(log_config.LOGGER_NAME) self.bind_addr = bind_addr self.port = port self.clients = [] self.users = {} self.storage = ServerStorage() self.__init_commands() def __init_commands(self): self.commands = { 'get_users': self.storage.get_users_online, 'add_contact': self.storage.add_contact, 'rem_contact': self.storage.remove_contact, 'get_contacts': self.storage.get_contacts, 'get_chat': self.storage.get_chat_str } def start(self, request_count=5): self.socket = socket(*self.TCP) self.socket.settimeout(0.5) self.socket.bind((self.bind_addr, self.port)) self.logger.info( f'Config server port - {self.port}| Bind address - {self.bind_addr}' ) self.socket.listen(request_count) # self.__listen() self.listener = ServerThread(self.__listen, self.logger) self.listener.start() # self.__console() def __console(self): while True: msg = input('Enter command:\n') if msg.upper() == 'Q': break if msg[0] == '#': msg = msg[1:] command, *args = msg.split(' ') if command in self.commands: res = self.commands[command](*args) print(res) def __listen(self): self.logger.info('Start listen') while True: try: client, addr = self.socket.accept() except OSError: pass except Exception as ex: self.logger.error(ex) else: self.logger.info(f'Connection from {addr}') self.clients.append(client) i_clients, o_clients = [], [] try: i_clients, o_clients, ex = select(self.clients, self.clients, [], self.TIMEOUT) except OSError: pass except Exception as ex: self.logger.error(ex) requests = self.__get_requests(i_clients) if requests: self.__send_responses(requests, o_clients) @try_except_wrapper def __get_requests(self, i_clients): requests = {} for client in i_clients: try: request = get_data(client) requests[client] = request if request.action == RequestAction.PRESENCE: if request.body in self.users: requests.pop(client) send_data(client, Response(CONFLICT)) self.clients.remove(client) else: self.users[request.body] = client self.storage.login_user(request.body, client.getpeername()[0]) elif request.action == RequestAction.QUIT: self.__client_disconnect(client) except (ConnectionError, ValueError): self.__client_disconnect(client) except Exception as e: raise e return requests @try_except_wrapper def __send_responses(self, requests, o_clients): for client, i_req in requests.items(): other_clients = [c for c in o_clients if c != client] self.logger.info(client) self.logger.info(i_req) if i_req.action == RequestAction.PRESENCE: self.__send_to_client(client, Response(OK)) self.__send_to_all(other_clients, Response(CONNECTED, i_req.body)) elif i_req.action == RequestAction.QUIT: self.__client_disconnect(client) elif i_req.action == RequestAction.MESSAGE: msg = Msg.from_dict(i_req.body) self.storage.user_stat_update(msg.sender, ch_sent=1) if msg.to.upper() != 'ALL' and msg.to in self.users: self.storage.user_stat_update(msg.to, ch_recv=1) self.storage.add_message(msg.sender, msg.to, msg.text) self.__send_to_client(self.users[msg.to], Response(LETTER, str(msg))) else: self.__send_to_all(other_clients, Response(LETTER, str(msg))) for u in self.storage.get_users_online(): if str(u) == msg.sender: continue self.storage.user_stat_update(str(u), ch_recv=1) self.storage.add_message(msg.sender, str(u), msg.text) elif i_req.action == RequestAction.COMMAND: command, *args = i_req.body.split() user = [u for u, c in self.users.items() if c == client].pop() if len(args) < 1 or args[0] != user: args.insert(0, user) o_resp = self.__execute_command(command, *args) self.__send_to_client(client, o_resp) else: self.__send_to_client(client, Response(INCORRECT_REQUEST)) self.logger.error(f'Incorrect request:\n {i_req}') @try_except_wrapper def __send_to_client(self, client, resp): try: self.logger.debug(resp) send_data(client, resp) except ConnectionError: self.__client_disconnect(client) except Exception as e: raise e def __send_to_all(self, clients, resp): for cl in clients: self.__send_to_client(cl, resp) @try_except_wrapper def __client_disconnect(self, client): self.clients.remove(client) disconnected_user = [u for u, c in self.users.items() if c == client].pop() self.users.pop(disconnected_user) self.storage.logout_user(disconnected_user) disconnection_response = Response(DISCONNECTED, disconnected_user) self.logger.debug(disconnection_response) for cl in self.clients: send_data(cl, disconnection_response) def __execute_command(self, command, *args): if command in self.commands: answer = self.commands[command](*args) if answer is False: return Response(SERVER_ERROR, 'Command error') elif isinstance(answer, list): answer = [str(a) for a in answer] return Response(ANSWER, answer) elif answer is None: return Response(ANSWER, 'Done') return Response(ANSWER, answer) else: return Response(INCORRECT_REQUEST, 'Command not found')
class Client: __slots__ = ('addr', '_port', 'logger', 'socket', 'connected', 'listener', 'sender', 'storage', 'subs', 'answers') TCP = (AF_INET, SOCK_STREAM) USER = User(f'Test{random.randint(0, 1000)}') port = Port('_port') def __init__(self, addr, port): self.logger = logging.getLogger(log_config.LOGGER_NAME) self.addr = addr self.port = port self.connected = False self.subs = {201: [], 202: [], 203: []} self.answers = queue.Queue() self.__reg_resp_console() # name = input('Set name (enter generate name):\n') # if len(name) > 0: # self.USER.username = name # self.storage = ClientStorage(self.USER.username) @property def username(self): return self.USER.username @username.setter def username(self, value): self.USER.username = value self.storage = ClientStorage(self.USER.username) def start(self): self.socket = socket(*self.TCP) start_txt = f'Connect to {self.addr}:{self.port} as {self.USER}...' self.logger.debug(start_txt) print(start_txt) self.__connect() def __reg_resp_console(self): self.subscribe(201, lambda m: print(f'{m} connected')) self.subscribe(202, lambda m: print(f'{m} disconnected')) self.subscribe(203, lambda m: print(m)) @try_except_wrapper def __connect(self): self.socket.connect((self.addr, self.port)) self.connected = True print('Done') print_help() response = self.presence() if response.code != OK: self.logger.warning(response) return self.listener = ClientThread(self.__listen_server, self.logger) self.listener.start() # self.__console() # self.sender = ClientThread(self.send_msg, self.logger) # self.sender.start() # self.sender.join() @try_except_wrapper def __send_request(self, request): if not self.connected: return self.logger.debug(request) send_data(self.socket, request) @try_except_wrapper def __get_response(self): if not self.connected: return response = get_data(self.socket) self.logger.debug(response) return response def presence(self): request = Request(RequestAction.PRESENCE, self.USER) self.__send_request(request) return self.__get_response() def __console(self): while self.connected: msg = input('Enter message:\n') if msg.upper() == 'Q': break if msg[0] == '!': self.__execute_local_command(msg[1:]) continue if msg[0] == '#': request = Request(RequestAction.COMMAND, msg[1:]) self.parse_command(request.body) else: msg = Msg(msg, self.USER) msg.parse_msg() self.storage.add_message(msg.to, msg.text) request = Request(RequestAction.MESSAGE, msg) self.__send_request(request) def get_chat_req(self, contact): req = Request(RequestAction.COMMAND, f'get_chat {self.USER.username} {contact}') self.__send_request(req) def get_users_req(self): self.__send_request(Request(RequestAction.COMMAND, 'get_users')) def get_contacts_req(self): self.__send_request(Request(RequestAction.COMMAND, 'get_contacts')) def add_contact(self, contact): if self.storage.get_contact(contact): return False self.storage.add_contact(contact) req = Request(RequestAction.COMMAND, f'add_contact {contact}') self.__send_request(req) def rem_contact(self, contact): self.storage.remove_contact(contact) req = Request(RequestAction.COMMAND, f'rem_contact {contact}') self.__send_request(req) def sync_contacts(self, contacts): for c in contacts: self.storage.append_contact(c) def send_msg(self, text, to): msg = Msg(text, self.USER, to) self.storage.add_message(msg.to, msg.text) request = Request(RequestAction.MESSAGE, msg) self.__send_request(request) def parse_command(self, command): command, *args = command.split(' ') if command == 'add_contact': self.storage.add_contact(args[0]) elif command == 'rem_contact': self.storage.remove_contact(args[0]) def __execute_local_command(self, command): if command == 'help': print_help() elif command == 'set_name': name = input('Set new name') self.USER.username = name self.__send_request(Request(RequestAction.PRESENCE, self.USER)) elif command == 'reconnect': self.start() else: print('Command not found') def __listen_server(self): while self.connected: resp = get_data(self.socket) self.logger.debug(resp) if resp.type != RESPONSE: self.logger.warning(f'Received not RESPONSE:\n {resp}') continue if resp.code == 101: self.answers.put(resp.message) print(f'server: {resp.message}') elif resp.code in self.subs.keys(): for s in self.subs[resp.code]: s(resp.message) else: print(resp.message) def subscribe(self, code, func): if code in self.subs.keys(): self.subs[code].append(func) else: self[code] = [func]