def authenticate_as_client(self, session_socket): # authenticates an external server connected via session_socket iv = self.receive_iv(session_socket) master_encrypter = Encrypter(self.master_key, iv) m = Messenger(session_socket, master_encrypter, self.continueHandler) client_challenge = genStr(CHALLENGE_LENGTH) client_challenge_hash = str(create_key(client_challenge)) hash_len = len(client_challenge_hash) secretA = generateAorB() publicA = pow(g, secretA, p) m.send(client_challenge + str(publicA)) response = m.recv() while not response: response = m.recv() if response[:hash_len] != client_challenge_hash: m.close() raise Exception('client could not authenticate') server_challenge_hash = str(create_key(response[hash_len:hash_len + CHALLENGE_LENGTH])) m.send(server_challenge_hash) public_b = int(response[hash_len + CHALLENGE_LENGTH:]) self.log.info('g^b mod p is {}'.format(public_b)) session_key = create_key(str(pow(public_b, secretA, p))) self.log.info('Session key generated by the client is {}'.format(session_key)) session_encrypter = Encrypter(session_key, iv) session_m = Messenger(session_socket, session_encrypter, self.continueHandler) self._messenger = session_m
def _test_chain_execution(session): def job1(outbox, params): print 'did 1' outbox.send('do_2') def job2(outbox, params): print 'did 1' outbox.send('do_3') def job3(outbox, params): print 'did 1' outbox.send('done') disp = Dispatcher() disp.register('do_1', job1) disp.register('do_2', job2) disp.register('do_3', job3) disp.start() try: messenger = Messenger(session) # db.get_session()) assert not messenger.recv() messenger.send('do_1') messenger.subscribe('done') import time time.sleep(1) assert messenger.recv() finally: disp.stop()
def _run(self): messenger = Messenger(db.get_session()) running_jobs = set() for dest in self._mapping: messenger.subscribe(dest) counter = 0 while self._running: print 'counter = ', counter counter += 1 messages = messenger.recv() for msg in messages: running_jobs.add((msg, self._dispatch_job(msg))) gevent.sleep(0.01) done = gevent.joinall( [j[1] for j in running_jobs], timeout=0, ) for j in done: for msg, job in running_jobs: if j == job: running_jobs.remove((msg, job)) messenger.retire(msg.id) break while self._outgoing_messages: to, params = self._outgoing_messages.pop() messenger.send(to, **params)
def test_recv_twice(session): m = Messenger(session) m.send('test_recv_twice', a='a', b=1) m.subscribe('test_recv_twice') assert len(m.recv()) == 1 assert len(m.recv()) == 0
def test_retire(session): m = Messenger(session) sent_id = m.send('test_retire', a='a', b=1) m.subscribe('test_retire') m.retire(sent_id) assert not m.recv()
def handleConnection(self, client_sock): while True: req = Messenger.recv(client_sock) if req is None: break t = threading.Thread(target=self.handleReq, args=(req,)) t.start()
def handleConnection(self, client_sock): while True: req = Messenger.recv(client_sock) if req is None: raise ConnectionError( 'Something went wrong with the connection') t = threading.Thread(target=self.handleReq, args=(req, )) t.start()
def test_recv(session): m = Messenger(session) sent_id = m.send('test_recv', a='a', b=1) m.subscribe('test_recv') incoming = m.recv() assert len(incoming) == 1 mm = incoming.pop() assert mm.id == sent_id assert mm.destination == 'test_recv' assert len(mm.params) == 2 assert mm.params.a == 'a' assert mm.params.b == 1
def handleConnection(self, req_sock): while True: req = Messenger.recv(req_sock) if req == None: break if verbose: print("Recv msg {0}\n".format(req)) body = req['body'] if req['msg_type'] == MSG.DECISION: if self.cntr < self.global_config['client']['num_txns']: self.recvDecision(body['final_decision']) else: self.logResults() break
def authenticate_as_server(self, session_socket): # authenticates an external client connected via session_socket iv = self.generate_and_send_iv(session_socket) # the server should generate a random iv master_encrypter = Encrypter(self.master_key, iv) m_messenger = Messenger(session_socket, master_encrypter, self.continueHandler) secret_b = generateAorB() public_b = str(pow(g, secret_b, p)) server_challenge = genStr(CHALLENGE_LENGTH) server_challenge_hash = str(create_key(server_challenge)) response = m_messenger.recv() while not response: response = m_messenger.recv() client_challenge = response[:CHALLENGE_LENGTH] client_challenge_hash = str(create_key(client_challenge)) public_a = response[CHALLENGE_LENGTH:] self.log.info('publicA is {}'.format(public_a)) m_messenger.send(client_challenge_hash + server_challenge + public_b) session_key = create_key(str(pow(int(public_a), secret_b, p))) self.log.info('session key is {}'.format(session_key)) response = m_messenger.recv() while not response: response = m_messenger.recv() if response != server_challenge_hash: self.log.warn('Client could not be authenticated. Session will be terminated!') m_messenger.close() else: print('Server Authentication Successful!!!') session_encrypter = Encrypter(session_key, iv) self._messenger = Messenger(session_socket, session_encrypter, self.continueHandler)
def thread_client(conn, addr, db_conn, active_clients): """ Checks and verifies password/username, and handles adding new users to the database. Main client loop Accepts messages from client socket then broadcasts message to all clients. If the connection is broken the loop will break, database will be updated(active state). :param conn: socket objected connected with remote client :param addr: tuple of the remote clients address and port :param db_conn: connection to the sqlite3 database containing user-info """ length_struct = struct.Struct("!I") local_messenger = Messenger(conn, length_struct) lock = _thread.allocate_lock() verified = False # used to control looping while not verified: # handle client login/signup credentials try: """ first message received will be a login or sign up attempt message_type will be "LOGIN" or "SIGNUP" """ message = local_messenger.recv() message_type, username, password = message.split("`", 2) except ValueError or ConnectionResetError: print("bad connection at {}".format(addr)) break # retrieve user info from database. username_check() returns two boolean values lock.acquire() username_exists, correct_password = username_check(db_conn, username, password) lock.release() # add new users to database if message_type == "SIGNUP": if username_exists: # username already taken local_messenger.send("UNAVAILABLE") else: # acquire lock and add user to database and active_clients lock.acquire() new_user(db_conn, username, password) active_clients[username] = conn lock.release() local_messenger.send("OK") verified = True # login existing users elif message_type == "LOGIN": if username_exists and correct_password: if username not in active_clients: # username is not already signed in # acquire lock and add username to active_clients lock.acquire() active_clients[username] = conn lock.release() local_messenger.send("OK") verified = True else: local_messenger.send("USER_ACTIVE") # user is already active else: local_messenger.send("BAD") # wrong password or username while verified: """ client will only be verified when an existing username and password have been submitted, or a new username and password has been created. verified loop will handle all incoming messages, errors, socket closures """ try: message = local_messenger.recv() except socket.error or struct.error: print("bad connection at {}".format(addr)) break if message: lock.acquire() local_messenger.broadcast(active_clients, message) lock.release() else: # empty string signaling connection closed lock.acquire() del active_clients[username] lock.release() conn.close() break # clean up after client disconnects or the connection is broken if username in active_clients: lock.acquire() del active_clients[username] lock.release() conn.close() print("{} DISCONNECTED".format(addr))
class App(tkinter.Tk): def __init__(self): super().__init__() self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.protocol("WM_DELETE_WINDOW", lambda: destroy_all(self.connection, self)) self.title("Chat Client") self.length_struct = struct.Struct("!I") self.messenger = Messenger(self.connection, self.length_struct) self.username = "" self.password = "" self.connect = ConnectScreen(self, self.connect_to_server) self.login = LoginScreen(self, self.check_data) self.chat = ChatScreen(self, self.handle_out_going) self.connect.pack() self.connect.pack_children() def connect_to_server(self): """ Callback for self.connect. Retrieves the user submitted address and port. Attempts to make connection. If any errors are caught the connect_message widget will be updated with information about the error. """ host_address = self.connect.ip_entry.get() host_port = self.connect.port_entry.get() try: host_port = int(host_port) self.connection.connect((host_address, host_port)) self.connect.pack_forget() self.login.pack() self.login.pack_children() except ValueError: self.connect.connect_message.config(text="Invalid Entry For Port\nMust Be an Integer", fg="red") except ConnectionRefusedError: self.connect.connect_message.config(text="Server Refused Connection", fg="red") except socket.gaierror: self.connect.connect_message.config(text="Invalid Address", fg="red") def check_data(self, message_type): """ Communicates with chat server to verify login information. If the login or sign up attempt fails a message is displayed on the login screen. :param message_type: tells the server whether it is a login attempt or signup request """ self.username = self.login.name_entry.get() self.password = self.login.pass_entry.get() # restrict user names to alpha numeric values if not self.username.isalnum(): self.login.display_message.config(text="Username can only be numbers and letters", fg="red") return # format message to be sent message = "{}`{}`{}".format(message_type, self.username, self.password) reply = "" # try communicating with server try: self.messenger.send(message) reply = self.messenger.recv() except ConnectionResetError or ValueError: self.login.display_message.config(text="Connection with server lost...restarting", fg="red") self.login.pack_forget() self.connection.detach() self.connect.pack() # check for all possible server responses if reply == "OK": self.login.pack_forget() self.title(self.username) self.chat.pack() self.chat.pack_children() self.connection.settimeout(.10) # prevents blocking calls of handle_incoming() self.handle_incoming() elif reply == "UNAVAILABLE": self.login.display_message.config(text="Username Unavailable", fg="red") elif reply == "BAD": self.login.display_message.config(text="Incorrect user Info", fg="red") elif reply == "USER ACTIVE": self.login.display_message.config(text="Username is currently already logged in", fg="red") else: self.login.display_message.config(text="Unexpected Server Response") def handle_out_going(self, event=None): """ reads from the chat_entry and sends it to the server. :param event: is used as a place holder for the event information sent by self.chat_entry.bind(<RETURN>) it is not used. """ text = self.chat.chat_entry.get() if text: # prevent empty messages from being sent try: message = "{}: {}".format(self.username, text) # This should be handled by server self.messenger.send(message) self.chat.chat_entry.delete(0, "end") except ConnectionResetError: self.chat.pack_forget() self.login.pack() self.login.display_message.config(text="Connection with server lost") def handle_incoming(self): """ called every 500 milliseconds from within the tkinter mainloop. Will check for incoming socket data, but will pass if socket timeout limit is reached. """ try: message = self.messenger.recv() self.chat.add_message(message) except socket.timeout: pass except ConnectionResetError or struct.error: self.chat.pack_forget() self.login.pack() self.login.display_message.config(text="Connection with server lost") return finally: self.after(500, self.handle_incoming)
class SessionManager: def __init__(self, port, ip_address, secret_value, continueHandler): # can be either a server or client. if ip_address=None, be a server on port. Otherwise, try to connect to # ip_address:port self.port = port self.ip_address = ip_address self.master_key = create_key(secret_value) self.log = logging.getLogger(__name__) self.continueHandler = continueHandler self._messenger = None self.reset_messenger() def generate_and_send_iv(self, session_socket): iv = urandom(16) self.continueHandler(iv) # send iv over socket first! sent_len = 0 while sent_len < len(iv): sent = session_socket.send(iv[sent_len:]) if sent == 0: raise RuntimeError("socket send connection issue") sent_len += sent # how much of the message we have sent logging.getLogger(__name__).info("sent iv: " + str(iv)) return iv def receive_iv(self, session_socket): iv = b'' while len(iv) < IV_LENGTH: chunk = session_socket.recv(IV_LENGTH - len(iv)) if chunk == b'': session_socket.close() raise RuntimeError("socket closed") iv += chunk logging.getLogger(__name__).info('received iv: {}'.format(str(iv))) return iv def authenticate_as_server(self, session_socket): # authenticates an external client connected via session_socket iv = self.generate_and_send_iv(session_socket) # the server should generate a random iv master_encrypter = Encrypter(self.master_key, iv) m_messenger = Messenger(session_socket, master_encrypter, self.continueHandler) secret_b = generateAorB() public_b = str(pow(g, secret_b, p)) server_challenge = genStr(CHALLENGE_LENGTH) server_challenge_hash = str(create_key(server_challenge)) response = m_messenger.recv() while not response: response = m_messenger.recv() client_challenge = response[:CHALLENGE_LENGTH] client_challenge_hash = str(create_key(client_challenge)) public_a = response[CHALLENGE_LENGTH:] self.log.info('publicA is {}'.format(public_a)) m_messenger.send(client_challenge_hash + server_challenge + public_b) session_key = create_key(str(pow(int(public_a), secret_b, p))) self.log.info('session key is {}'.format(session_key)) response = m_messenger.recv() while not response: response = m_messenger.recv() if response != server_challenge_hash: self.log.warn('Client could not be authenticated. Session will be terminated!') m_messenger.close() else: print('Server Authentication Successful!!!') session_encrypter = Encrypter(session_key, iv) self._messenger = Messenger(session_socket, session_encrypter, self.continueHandler) def authenticate_as_client(self, session_socket): # authenticates an external server connected via session_socket iv = self.receive_iv(session_socket) master_encrypter = Encrypter(self.master_key, iv) m = Messenger(session_socket, master_encrypter, self.continueHandler) client_challenge = genStr(CHALLENGE_LENGTH) client_challenge_hash = str(create_key(client_challenge)) hash_len = len(client_challenge_hash) secretA = generateAorB() publicA = pow(g, secretA, p) m.send(client_challenge + str(publicA)) response = m.recv() while not response: response = m.recv() if response[:hash_len] != client_challenge_hash: m.close() raise Exception('client could not authenticate') server_challenge_hash = str(create_key(response[hash_len:hash_len + CHALLENGE_LENGTH])) m.send(server_challenge_hash) public_b = int(response[hash_len + CHALLENGE_LENGTH:]) self.log.info('g^b mod p is {}'.format(public_b)) session_key = create_key(str(pow(public_b, secretA, p))) self.log.info('Session key generated by the client is {}'.format(session_key)) session_encrypter = Encrypter(session_key, iv) session_m = Messenger(session_socket, session_encrypter, self.continueHandler) self._messenger = session_m def reset_messenger(self): if self._messenger is not None: self._messenger.close() self._messenger = None # AF_INET = ipv4, SOCK_STREAM = tcp s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # assuming we can use the same "client socket" for both reading and writing if self.ip_address is None: # server init s.bind(('', self.port)) self.log.info("Listening for connection on port {}".format(self.port)) s.listen(1) # listen for only one connection session_socket, addr = s.accept() self.log.info("Accepted connection from {}".format(addr)) self.authenticate_as_server(session_socket) s.close() else: # client init: specify ip address and port to try to ping self.log.info("Trying to connect to {}:{}".format(self.ip_address, self.port)) s.connect((self.ip_address, self.port)) self.authenticate_as_client(s) def checkReceivedMessages(self): try: nextReceivedMessage = self.recv() # Get and Send Messages return nextReceivedMessage except Exception as e: self.log.warning("Session closed: {}".format(e)) self.reset_messenger() # Return 0 as default return 0 def send(self, msg): self._messenger.send(msg) def recv(self): data_in = self._messenger.recv() if len(data_in) > 0: self.log.info('received data: ' + data_in) return data_in def close(self): self._messenger.close()
def test_empty_recv(session): m = Messenger(session) assert not m.recv()
class App(tkinter.Tk): def __init__(self): super().__init__() self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.protocol("WM_DELETE_WINDOW", lambda: destroy_all(self.connection, self)) self.title("Chat Client") self.length_struct = struct.Struct("!I") self.messenger = Messenger(self.connection, self.length_struct) self.username = "" self.password = "" self.connect = ConnectScreen(self, self.connect_to_server) self.login = LoginScreen(self, self.check_data) self.chat = ChatScreen(self, self.handle_out_going) self.connect.pack() self.connect.pack_children() def connect_to_server(self): """ Callback for self.connect. Retrieves the user submitted address and port. Attempts to make connection. If any errors are caught the connect_message widget will be updated with information about the error. """ host_address = self.connect.ip_entry.get() host_port = self.connect.port_entry.get() try: host_port = int(host_port) self.connection.connect((host_address, host_port)) self.connect.pack_forget() self.login.pack() self.login.pack_children() except ValueError: self.connect.connect_message.config( text="Invalid Entry For Port\nMust Be an Integer", fg="red") except ConnectionRefusedError: self.connect.connect_message.config( text="Server Refused Connection", fg="red") except socket.gaierror: self.connect.connect_message.config(text="Invalid Address", fg="red") def check_data(self, message_type): """ Communicates with chat server to verify login information. If the login or sign up attempt fails a message is displayed on the login screen. :param message_type: tells the server whether it is a login attempt or signup request """ self.username = self.login.name_entry.get() self.password = self.login.pass_entry.get() # restrict user names to alpha numeric values if not self.username.isalnum(): self.login.display_message.config( text="Username can only be numbers and letters", fg="red") return # format message to be sent message = "{}`{}`{}".format(message_type, self.username, self.password) reply = "" # try communicating with server try: self.messenger.send(message) reply = self.messenger.recv() except ConnectionResetError or ValueError: self.login.display_message.config( text="Connection with server lost...restarting", fg="red") self.login.pack_forget() self.connection.detach() self.connect.pack() # check for all possible server responses if reply == "OK": self.login.pack_forget() self.title(self.username) self.chat.pack() self.chat.pack_children() self.connection.settimeout( .10) # prevents blocking calls of handle_incoming() self.handle_incoming() elif reply == "UNAVAILABLE": self.login.display_message.config(text="Username Unavailable", fg="red") elif reply == "BAD": self.login.display_message.config(text="Incorrect user Info", fg="red") elif reply == "USER ACTIVE": self.login.display_message.config( text="Username is currently already logged in", fg="red") else: self.login.display_message.config( text="Unexpected Server Response") def handle_out_going(self, event=None): """ reads from the chat_entry and sends it to the server. :param event: is used as a place holder for the event information sent by self.chat_entry.bind(<RETURN>) it is not used. """ text = self.chat.chat_entry.get() if text: # prevent empty messages from being sent try: message = "{}: {}".format( self.username, text) # This should be handled by server self.messenger.send(message) self.chat.chat_entry.delete(0, "end") except ConnectionResetError: self.chat.pack_forget() self.login.pack() self.login.display_message.config( text="Connection with server lost") def handle_incoming(self): """ called every 500 milliseconds from within the tkinter mainloop. Will check for incoming socket data, but will pass if socket timeout limit is reached. """ try: message = self.messenger.recv() self.chat.add_message(message) except socket.timeout: pass except ConnectionResetError or struct.error: self.chat.pack_forget() self.login.pack() self.login.display_message.config( text="Connection with server lost") return finally: self.after(500, self.handle_incoming)