# compatibile Windows 11 # compatibile Ubuntu 24.10 # compatibile python 3.12.7 # server.py (versione con scambio di chiavi) import socket import struct import os import threading import time from cryptography.hazmat.primitives.asymmetric import rsa, padding from cryptography.hazmat.primitives import hashes, serialization from cryptography.hazmat.backends import default_backend # --- Configurazione --- MULTICAST_GROUP = '224.1.1.1' MULTICAST_PORT = 5007 MAX_PACKET_SIZE = 65507 FRAGMENT_SIZE = 256 # --- Funzioni di utilità (utils.py) --- def create_tlv(type, value): """Crea un messaggio TLV.""" type_bytes = struct.pack('!H', type) length_bytes = struct.pack('!H', len(value)) return type_bytes + length_bytes + value def parse_tlv(data): """Analizza un messaggio TLV.""" if len(data) < 4: raise ValueError("Dati TLV insufficienti.") type, length = struct.unpack('!HH', data[:4]) if len(data) < 4 + length: raise ValueError("Lunghezza TLV non corrisponde ai dati.") value = data[4:4 + length] remaining_data = data[4 + length:] return type, value, remaining_data def encrypt_data(data, public_key): """Crittografa.""" ciphertext = public_key.encrypt( data, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return ciphertext def decrypt_data(ciphertext, private_key): """Decrittografa.""" try: plaintext = private_key.decrypt( ciphertext, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None ) ) return plaintext except Exception as e: print(f"Errore durante la decrittazione: {e}") return None def fragment_data(data, fragment_size): """Frammenta.""" return [data[i:i+fragment_size] for i in range(0, len(data), fragment_size)] def defragment_data(fragments): """Deframmenta.""" return b''.join(fragments) # --- Classe ServerThread --- class ServerThread(threading.Thread): def __init__(self, sock, private_key, public_key): super().__init__() self.sock = sock self.private_key = private_key self.public_key = public_key self.running = True self.received_fragments = {} def run(self): while self.running: try: data, address = self.sock.recvfrom(MAX_PACKET_SIZE) print(f"Ricevuto da {address}") self.process_data(data, address) except (socket.timeout, ValueError, struct.error, OSError) as e: if self.running: pass # Ignora timeout e altri errori se il server è in esecuzione except Exception as ex: if self.running: print("Errore non gestito", ex) def process_data(self, data, address): try: offset = 0 while offset < len(data): type, value, remaining_data = parse_tlv(data[offset:]) offset += 4 + len(value) if type == 1: # Frammento UDP self.handle_fragment(value) elif type == 2: # Messaggio diretto self.handle_direct_message(value) elif type == 4: # Richiesta chiave pubblica self.send_public_key(address) else: print(f"Tipo TLV sconosciuto: {type}") except Exception as e: print("Errore elaborazione", e) def handle_fragment(self, value): """Gestisce i frammenti di tipo 1.""" msg_id = struct.unpack('!I', value[:4])[0] seq_num, total_fragments = struct.unpack('!HH', value[4:8]) fragment_data = value[8:] if msg_id not in self.received_fragments: self.received_fragments[msg_id] = {} self.received_fragments[msg_id][seq_num] = fragment_data if len(self.received_fragments[msg_id]) == total_fragments: self.reassemble_and_decrypt(msg_id) def reassemble_and_decrypt(self, msg_id): """Riassembla i frammenti, decrittografa e stampa.""" sorted_fragments = [self.received_fragments[msg_id][i] for i in range(len(self.received_fragments[msg_id]))] defragmented_udp = defragment_data(sorted_fragments) decrypted_blocks = [] inner_offset = 0 while inner_offset < len(defragmented_udp): inner_type, inner_value, inner_remaining = parse_tlv(defragmented_udp[inner_offset:]) inner_offset += 4 + len(inner_value) if inner_type == 3: decrypted_block = decrypt_data(inner_value, self.private_key) if decrypted_block: decrypted_blocks.append(decrypted_block) if decrypted_blocks: final_message = b"".join(decrypted_blocks) print("Messaggio completo ricostruito:") print(final_message.decode('utf-8', errors='replace')) else: print("Nessun blocco decifrato correttamente") del self.received_fragments[msg_id] def handle_direct_message(self, value): """Gestisce i messaggi diretti di tipo 2.""" decrypted_data = decrypt_data(value, self.private_key) if decrypted_data: print("Messaggio diretto (non frammentato):") print(decrypted_data.decode('utf-8', errors='replace')) def send_public_key(self, address): """Invia la chiave pubblica al client che l'ha richiesta.""" public_pem = self.public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo ) tlv_response = create_tlv(5, public_pem) # Tipo 5: Risposta chiave pubblica self.sock.sendto(tlv_response, address) print(f"Chiave pubblica inviata a {address}") def stop(self): self.running = False self.sock.close() # --- Funzione principale del server --- def run_server(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(1.0) if os.name == 'nt': sock.bind(('', MULTICAST_PORT)) else: sock.bind((MULTICAST_GROUP, MULTICAST_PORT)) group = socket.inet_aton(MULTICAST_GROUP) mreq = group + socket.inet_aton('0.0.0.0') if os.name == 'nt' else struct.pack('4sL', group, socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) print(f"Server in ascolto su {MULTICAST_GROUP}:{MULTICAST_PORT}") # Genera le chiavi all'avvio private_key = rsa.generate_private_key( public_exponent=65537, key_size=2048, backend=default_backend() ) public_key = private_key.public_key() server_thread = ServerThread(sock, private_key, public_key) server_thread.start() try: while server_thread.is_alive(): time.sleep(1) except KeyboardInterrupt: print("Server terminato (Ctrl+C).") server_thread.stop() server_thread.join() finally: if server_thread.is_alive(): server_thread.stop() server_thread.join() print("Server Terminato Correttamente") if __name__ == "__main__": run_server()