def __init__(self, port=False): """ Initialisierung und Binden des Ports. Der Port kann via Shell-Argument uebergeben werden, sonst wird versucht, Port 32323 zu binden. @return: None """ self.database = DatabaseHandler("gu.db") self.crypt = EncryptionHandler() self.sid_Pool = Pool(0) self.file_storage = "./files/" self.max_rcv = 2048 self.users = [] # Nutzer, die zum Index Session-ID gehoeren self.ivs = [] # Initialiserungsvektoren self.ctr = [] # Counter self.sesskey = [] # Sessionkeys self.uidstrings = [] # User-ID-Strings #serv_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #file_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serv_soc = socket.socket() if port != False: serv_soc.bind(("", port)) elif len(sys.argv) < 2: serv_soc.bind(("", 32323)) else: serv_soc.bind(("", int(sys.argv[1]))) serv_soc.listen(1) try: while True: komm, addr = serv_soc.accept() data = "" data = komm.recv(self.max_rcv) # Default-Antwort resp = "error - invalid-client-request" # Leere Verbindung if not data: komm.close() continue # Datenpaket ist verschluesslt (= Kein DHEX-Paket) if self.crypt.is_encrypted(data): #body = self.decrypt(data) tmp = split(";", data, 1) body = tmp[1] #print "getting messages :" + body # Wenn nicht entschluesselbar -> Fehler if body == None: komm.send(resp) komm.close() continue body = body.decode("utf-8", "ignore") try: # Kopfdaten und Nutzdaten trennen data = split(";", data, 1) self.header = self.parse_header(data[0]) data = data[1] except IndexError: komm.send(resp) komm.close() continue # Datenpaket encoden #if self.header[0] != "dhex": try: if self.header[0] == "dhex": resp = self.init_dh(data) elif self.header[0] == "auth": resp = self.auth_user(body) elif self.header[0] == "msg": resp = self.recv_msg(body) elif self.header[0] == "getmsg": resp = self.get_msg(body) elif self.header[0] == "gmsg": resp = self.recv_gmsg(body) elif self.header[0] == "getgmsg": resp = self.get_gmsg(body) elif self.header[0] == "reqfile": resp = self.request_file() elif self.header[0] == "regfile": resp = self.register_file(body) elif self.header[0] == "getfile": resp = self.get_globalname(body) elif self.header[0] == "getpic": resp = self.get_profile_pic(body) elif self.header[0] == "adduser": resp = self.add_user(body) elif self.header[0] == "adressbook": resp = self.get_address_book(body) # Antwortpaket senden if self.header[0] == "dhex": komm.send(resp) elif isinstance(resp, list): #for item in resp: #item = (str(item[0]).encode("utf-8", "ignore"), item[1].encode("utf-8", "ignore")) komm.send(self.build_pack(json.dumps(resp))) else: if self.is_error(resp): print "error: " + resp komm.send(resp) else: resp = self.crypt.encode_string(resp) komm.send(self.build_pack(resp)) komm.close() except IndexError: komm.send(resp) komm.close() finally: #for client in clients: # client.close() serv_soc.close()
class ConnectionHandler: """ Infolib.ConnectionHandler ist eine Klasse, die die Grundfunktionalitaet des Infoservers bereitstellt. Sie bindet einen Port, an dem alle Verbindungen eigehen und verteilt die Pakete nach den entsprechenden Kopfinformationen. Sie baut auf allen anderen Klassen in der Infolib auf. Eine Verwendung ausserhalb des Infobook-Projektes wird nicht empfohlen. Eine Dokumentation des Ablaufes finden Sie im GitHub-Wiki unter https://github.org/hansau22/infobook/ """ def __init__(self, port=False): """ Initialisierung und Binden des Ports. Der Port kann via Shell-Argument uebergeben werden, sonst wird versucht, Port 32323 zu binden. @return: None """ self.database = DatabaseHandler("gu.db") self.crypt = EncryptionHandler() self.sid_Pool = Pool(0) self.file_storage = "./files/" self.max_rcv = 2048 self.users = [] # Nutzer, die zum Index Session-ID gehoeren self.ivs = [] # Initialiserungsvektoren self.ctr = [] # Counter self.sesskey = [] # Sessionkeys self.uidstrings = [] # User-ID-Strings #serv_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #file_soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serv_soc = socket.socket() if port != False: serv_soc.bind(("", port)) elif len(sys.argv) < 2: serv_soc.bind(("", 32323)) else: serv_soc.bind(("", int(sys.argv[1]))) serv_soc.listen(1) try: while True: komm, addr = serv_soc.accept() data = "" data = komm.recv(self.max_rcv) # Default-Antwort resp = "error - invalid-client-request" # Leere Verbindung if not data: komm.close() continue # Datenpaket ist verschluesslt (= Kein DHEX-Paket) if self.crypt.is_encrypted(data): #body = self.decrypt(data) tmp = split(";", data, 1) body = tmp[1] #print "getting messages :" + body # Wenn nicht entschluesselbar -> Fehler if body == None: komm.send(resp) komm.close() continue body = body.decode("utf-8", "ignore") try: # Kopfdaten und Nutzdaten trennen data = split(";", data, 1) self.header = self.parse_header(data[0]) data = data[1] except IndexError: komm.send(resp) komm.close() continue # Datenpaket encoden #if self.header[0] != "dhex": try: if self.header[0] == "dhex": resp = self.init_dh(data) elif self.header[0] == "auth": resp = self.auth_user(body) elif self.header[0] == "msg": resp = self.recv_msg(body) elif self.header[0] == "getmsg": resp = self.get_msg(body) elif self.header[0] == "gmsg": resp = self.recv_gmsg(body) elif self.header[0] == "getgmsg": resp = self.get_gmsg(body) elif self.header[0] == "reqfile": resp = self.request_file() elif self.header[0] == "regfile": resp = self.register_file(body) elif self.header[0] == "getfile": resp = self.get_globalname(body) elif self.header[0] == "getpic": resp = self.get_profile_pic(body) elif self.header[0] == "adduser": resp = self.add_user(body) elif self.header[0] == "adressbook": resp = self.get_address_book(body) # Antwortpaket senden if self.header[0] == "dhex": komm.send(resp) elif isinstance(resp, list): #for item in resp: #item = (str(item[0]).encode("utf-8", "ignore"), item[1].encode("utf-8", "ignore")) komm.send(self.build_pack(json.dumps(resp))) else: if self.is_error(resp): print "error: " + resp komm.send(resp) else: resp = self.crypt.encode_string(resp) komm.send(self.build_pack(resp)) komm.close() except IndexError: komm.send(resp) komm.close() finally: #for client in clients: # client.close() serv_soc.close() def is_error(self, data): """ Prueft ob eine Antwort eine Fehlermeldung ist @param data: Antwort @type data: str @return: Boolean Ergebnis """ if string.find(data, "error", 0, 4) == -1 : return False else: return True def init_dh(self, data): """ Initialisiert den DH-Schluesselaustausch anhand der Informationen aus der Anfrage des Clients. @param data: Enthaelt den Oeffentlichen Teil vom Partner @type data: str @return: str - Sessionkey """ # DH-Antwort (B) auf die Anfrage (A) ret = self.crypt.init_dh_b(self.sid_Pool.give_next(),data) if ret == False: return "error - DH-initiation-error - DHEX" try: # Alle Felder fuer die neu Initialiserte Session reservieren (befuellen) self.users.append("") self.uidstrings.append("") self.ivs.append(ret[0]) self.ctr.append(ret[1]) self.sesskey.append(ret[2]) #print "sesskey : " + ret[2] return ret[3] except IndexError: return "error - DH-initiation-server-error - DHEX" def decrypt(self, data): """ Entschluesselt ein Datenpaket mit dem Sessionkey, der zur Session-ID gehoert. @param data: Verschluesseltes Paket mit unverschluesselten Kopfinformationen @type data: str @return: str - Unverschluesseltes Paket ohne Kopfinformationen """ try: tmp = split(";", data, 1) # ";" Seperiert Nutz- und Kopfdaten sid = split(":", tmp[0], 2) # Extrahiere Session-ID sid = int(sid[2]) data = self.crypt.decrypt(self.sesskey[sid], self.ctr[sid], tmp[1]) return data except IndexError: return None def encrypt(self, data): """ Verschluesselt die Daten fuer ein Paket. @param data: Daten-String ohne Kopfinformationen @type data: str @return: Verschluesselt Datenpaket ohne Kopfinformationen """ try: sid = self.header[2] #return self.crypt.encrypt(self.sesskey[sid], self.ctr[sid], data) return data except IndexError: return None def parse_header(self, data): """ Verarbeitet Kopfinformationen und wandelt die Informationen in benoetigte Typen @param data: Kopfinformationen @type data: str @return: Array - Kopfinformationen """ try: header = split(":", data, 2) if not header[0] == "dhex": header[2] = int(header[2]) return header except IndexError: pass def build_pack(self, msg): """ Erstellt die Kopfinformationen fuer ein Datenpaket und fuegt die Nachricht an. @param msg: Nachricht ohne Kopfinformationen @type msg: str @return: str - Nachrichtenpaket mit Kopfinformationen """ try: package = "sresp" + ":" + str(self.header[2]) + ";" #enc_msg = self.encrypt(msg) #if enc_msg == None: # return msg package += msg return package except IndexError: return msg def auth_user(self, data): """ Prueft ob eine Nutzer-Passwort Kombination valid ist. @param data: Paket des Clients ohne Kopfinformationen @type data: str @return: str - Digest, der den Nutzer-ID-String enthaelt """ cred = split(":", data, 1) if len(cred) < 2: return "error - not-enough-arguments - AUTH" try: if self.database.auth_user(cred[0], cred[1]) == True: # User-ID String erzeugen dig = self.crypt.get_hash(self.sesskey[self.header[2]] + str(cred[0])) self.uidstrings[self.header[2]] = dig self.users[self.header[2]] = self.database.get_user_id(cred[0]) return dig else: return "error - wrong-credentials - AUTH" except IndexError: return "error - invalid-header - AUTH" def check_uidstring(self, index, string): """ Vergleicht einen Nutzer-ID-String mit dem, der zu dem Nutzer mit der Session-ID gehoert. @param index: Session-ID @type index: int @param string: Nutzer-ID-String @type string: str @return: Boolean - Ergebnis """ try: if self.uidstrings[index] == string: return True return False except IndexError: return False def recv_msg(self, data): """ Traegt eine Nachricht in die Datenbank ein. @param data: Datenpaket des Clients ohne Kopfinformationen @type data: str @return: str - Erfolgs-/Fehlermeldung """ try: sid = self.header[2] tmp = split(":", data, 2) # Nicht alle Felder gegeben if len(tmp) != 3: return "error - not-long-enough - MSG" if self.check_uidstring(sid, tmp[0]): rcv_uid = self.database.get_user_id(tmp[1]) snd_uid = self.users[self.header[2]] print "writing message:" + tmp[2] if not self.database.rcv_message(snd_uid, rcv_uid, tmp[2]): return "error - server-database-error - MSG" return "success - MSG" else: return "error - wrong-uidstring - MSG" except IndexError: return "error - invalid-header - MSG" def get_msg(self, data): """ Gibt dem Client die Nachrichten zurueck. @param data: Letzte MID, die der Client an den Server gibt. @type data: str @return: Array - Nachrichten """ data = split(":", data, 2) try: if not self.check_uidstring(self.header[2], data[0]): # print "wrong uid string :" + data[0] return "error - wrong-credentials - GetMessage" messages = self.database.get_messages_by_last_mid(self.header[2], data[1]) ret_msg = [] for item in messages: username = self.database.get_user_by_id(item[0]) if (username == None) or (item[0] == "False"): username = "******" ret_msg.append((username, item[1])) return ret_msg except IndexError: return "error - internal-database-request-error - MSG" def get_gmsg(self, data): """ Gibt dem Client die Gruppennachrichten zurueck. @param data: Letzte GID, die der Client an den Server gibt. @type data: str @return: Array - Nachrichten """ data = split(":", data, 2) try: if not self.check_uidstring(self.header[2], data[0]): print "wrong uid string" return "error - wrong-credentials - GetGroupMessage" messages = self.database.get_messages_by_last_gid(self.header[2], data[1]) ret_msg = [] for item in messages: groupname = self.database.get_group_by_id(item[0]) if groupname == None: groupname = "Gruppenname unbekannt" ret_msg.append(item[1] + ":" + groupname + ":" + item[2]) return ret_msg except IndexError: return "error - internal-database-request-error - GroupMessage" def recv_gmsg(self, data): """ Traegt eine Broadcast-Nachricht in die Datenbank ein. @param data: Datenpaket des Clients ohne Kopfinformationen @type data: str @return: str - Erfolgs-/Fehlermeldung """ try: sid = self.header[2] tmp = split(":", data, 2) # Nicht alle Felder gegeben if len(tmp) != 3: return "error - not-enough-arguments - GroupMessage" if self.check_uidstring(sid, tmp[0]): rcv_gid = self.database.get_group_id(tmp[1]) snd_uid = self.users[self.header[2]] print "writing group message:" + tmp[2] if not self.database.rcv_brdc_message(snd_uid, rcv_gid, tmp[2]): return "error - server-database-error - GroupMessage" return "success - GroupMessage" else: print "error in uidstring" return "error - wrong-uidstring - GroupMessage" except IndexError: return "error - invalid-header - GroupMessage" def request_file(self): """ Gibt einen Dateistring fuer eine neue Datei zurueck, die ueber FTP hochgeladen werden kann @return: str - Dateistring """ ret_value = self.generate_file_string() if ret_value == None: return "error - storage-full - REQFILE" else: self.database.add_file(ret_value) return ret_value def register_file(self, data): """ Registriert eine Datei (Setzt den Besitzer zu einem Upload und gibt Dateinamen an) @param data: Datenpaket des Clients ohne Kopfinformationen @type data: str @return: str - Erfolgs-/Fehlermeldung """ values = split(":", data, 2) print values try: filestring = values[0] global_name = values[1] del values if not self.database.check_filestring(filestring): return "error - wrong-filestring" if not self.database.register_file(self.users[self.header[2]], global_name, filestring): return "error - server-storage-error" else: return "success - FILE" except IndexError: return "error - invalid-header - FILE" def get_globalname(self, data): """ Sucht eine Datei nach dem Dateinamen und gibt den filestring zuruekc @param data: Datenpaket des Clients ohne Kopfinformationen @type data: str @return: str - filestring / str - error """ filestring = self.database.get_name_by_filestring(data) if not filestring: return "error - file-not-found - FILE" return filestring def generate_file_string(self): """ Generiert einen Datei-String fuer eine neu empfangene Datei Prueft ausserdem, ob eine Datei mit diesem Namen in ./files vorhanden ist @return: str - Dateistring """ for i in range(0, 14): filestring = "" for i in range(0, 10): filestring = filestring + choice(string.ascii_letters) if os.path.exists(self.file_storage + filestring): break else: return filestring return None def get_profile_pic(self, data): """ Gibt dem Nutzer den String zum Profilfoto zurueck @param data: Datenpaket des Clients ohne Kopfinformationen @type data: str @return: str - Dateistring zum Profilbild """ string = self.database.get_profile_pic(data) if not string: return "-" return string def add_user(self, data): """ Fuegt einen neuen Benutzer hinzu @param data: Daten vom Client @type data: str @return: str - Nachricht an den Client """ tmp = split(":", data, 2) if not len(tmp) == 2: return "error - not-enough-arguments - ADDUSER" username = tmp[0] pwhash = tmp[1] if not self.database.check_user(username): return "error - already-present-username - ADDUSER" self.database.add_user(username, pwhash) return "success - ADDUSER" def get_address_book(self, data): """ Gibt das Nutzer-Addressbuch zurueck @param data: uidstring @type data: str @return: Array - [int UID, str Name] """ if not self.check_uidstring(int(self.header[2]), data): return "error - wrong-uidstring - GETADRBOOK" return self.database.get_address_book(self.users[int(self.header[2])])