diff --git a/protocol_prototype/DryBox/Dockerfile b/protocol_prototype/DryBox/Dockerfile index 1535ade..ff26191 100644 --- a/protocol_prototype/DryBox/Dockerfile +++ b/protocol_prototype/DryBox/Dockerfile @@ -1,4 +1,14 @@ +# Use official Python image FROM python:3.9-slim + +# Set working directory WORKDIR /app -COPY gsm_simulator.py /app -CMD ["python", "gsm_simulator.py"] + +# Copy the simulator script +COPY gsm_simulator.py . + +# Expose the port +EXPOSE 12345 + +# Run the simulator +CMD ["python", "gsm_simulator.py"] \ No newline at end of file diff --git a/protocol_prototype/DryBox/gsm_simulator.py b/protocol_prototype/DryBox/gsm_simulator.py index 71dcf36..3605c0a 100644 --- a/protocol_prototype/DryBox/gsm_simulator.py +++ b/protocol_prototype/DryBox/gsm_simulator.py @@ -1,94 +1,58 @@ import socket import threading -import random import time -class GSMSimulator: - def __init__(self, host='0.0.0.0', port=5555): - self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.server_socket.bind((host, port)) - self.server_socket.listen(2) - self.clients = {} - print("GSM Simulator started. Waiting for connections...") +HOST = "0.0.0.0" +PORT = 12345 +FRAME_SIZE = 1000 +FRAME_DELAY = 0.02 - def simulate_signal_strength(self): - return random.uniform(0, 1) +clients = [] - def handle_client(self, client_socket, address): +def handle_client(client_sock, client_id): + print(f"Starting handle_client for Client {client_id}") + while True: try: - client_socket.settimeout(5) - role = client_socket.recv(1024).decode().strip() - if role not in ["CALLER", "RECEIVER"]: - print(f"Invalid role received from {address}: {role}") - client_socket.close() - return - self.clients[role] = client_socket - print(f"{role} connected from {address}") - - if role == "CALLER": - self.handle_caller(client_socket) - elif role == "RECEIVER": - self.handle_receiver(client_socket) - except Exception as e: - print(f"Error handling client {address}: {e}") - finally: - if role in self.clients: - del self.clients[role] - client_socket.close() - - def handle_caller(self, caller_socket): - print("Caller connected, waiting for receiver...") - while "RECEIVER" not in self.clients: - time.sleep(1) - receiver_socket = self.clients["RECEIVER"] - print("Receiver found, sending RINGING...") - receiver_socket.send("RINGING".encode()) - - while True: - try: - data = caller_socket.recv(1024).decode().strip() - if not data: - print("Caller disconnected unexpectedly.") - receiver_socket.send("CALL_END".encode()) - break - if data == "CALL_END": - print("Call terminated by caller.") - receiver_socket.send("CALL_END".encode()) - break - - signal = self.simulate_signal_strength() - if signal < 0.2: - print("Call dropped due to low signal strength.") - caller_socket.send("CALL_DROPPED".encode()) - receiver_socket.send("CALL_DROPPED".encode()) - break - - print(f"Relaying to receiver: {data} (Signal: {signal:.2f})") - receiver_socket.send(data.encode()) - time.sleep(0.1) # Small delay to ensure data is sent properly - except Exception as e: - print(f"Error in caller loop: {e}") - receiver_socket.send("CALL_END".encode()) + other_client = clients[1 - client_id] if len(clients) == 2 else None + print(f"Client {client_id} waiting for data, other_client exists: {other_client is not None}") + data = client_sock.recv(1024) + if not data: + print(f"Client {client_id} disconnected or no data received") break - - def handle_receiver(self, receiver_socket): - print("Receiver connected, waiting for data...") - try: - while True: - data = receiver_socket.recv(1024) - if not data: - print("Receiver disconnected.") - break + if other_client: + for i in range(0, len(data), FRAME_SIZE): + frame = data[i:i + FRAME_SIZE] + other_client.send(frame) + time.sleep(FRAME_DELAY) + print(f"Forwarded {len(data)} bytes from Client {client_id} to Client {1 - client_id}") except Exception as e: - print(f"Error in receiver loop: {e}") + print(f"Error with Client {client_id}: {e}") + break + print(f"Closing connection for Client {client_id}") + client_sock.close() - def run(self): +def start_simulator(): + server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server.bind((HOST, PORT)) + server.listen(2) + print(f"GSM Simulator listening on {HOST}:{PORT}...") + + while len(clients) < 2: + client_sock, addr = server.accept() + client_sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # Keep connection alive + clients.append(client_sock) + client_id = len(clients) - 1 + print(f"Client {client_id} connected from {addr}") + threading.Thread(target=handle_client, args=(client_sock, client_id), daemon=True).start() + + try: while True: - client_socket, address = self.server_socket.accept() - print(f"New connection from {address}") - thread = threading.Thread(target=self.handle_client, args=(client_socket, address)) - thread.start() + time.sleep(1) + except KeyboardInterrupt: + print("Shutting down simulator...") + for client in clients: + client.close() + server.close() if __name__ == "__main__": - gsm = GSMSimulator() - gsm.run() \ No newline at end of file + start_simulator() \ No newline at end of file diff --git a/protocol_prototype/DryBox/input.wav b/protocol_prototype/DryBox/input.wav new file mode 100644 index 0000000..576bfd8 Binary files /dev/null and b/protocol_prototype/DryBox/input.wav differ diff --git a/protocol_prototype/DryBox/input_8k_mono.wav b/protocol_prototype/DryBox/input_8k_mono.wav new file mode 100644 index 0000000..f6dffb1 Binary files /dev/null and b/protocol_prototype/DryBox/input_8k_mono.wav differ diff --git a/protocol_prototype/DryBox/input_8k_mono.wav.gsm b/protocol_prototype/DryBox/input_8k_mono.wav.gsm new file mode 100644 index 0000000..4ffd55e Binary files /dev/null and b/protocol_prototype/DryBox/input_8k_mono.wav.gsm differ diff --git a/protocol_prototype/DryBox/protocol.py b/protocol_prototype/DryBox/protocol.py new file mode 100644 index 0000000..ee4d82e --- /dev/null +++ b/protocol_prototype/DryBox/protocol.py @@ -0,0 +1,86 @@ +import socket +import os +import time +import subprocess + +# Configuration +HOST = "localhost" +PORT = 12345 +INPUT_FILE = "input.wav" +OUTPUT_FILE = "received.wav" + + +def encrypt_data(data): + return data # Replace with your encryption protocol + + +def decrypt_data(data): + return data # Replace with your decryption protocol + + +def run_protocol(send_mode=True): + """Connect to the simulator and send/receive data.""" + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.connect((HOST, PORT)) + print(f"Connected to simulator at {HOST}:{PORT}") + + if send_mode: + # Sender mode: Encode audio with toast + os.system(f"toast -p -l {INPUT_FILE}") # Creates input.wav.gsm + input_gsm_file = f"{INPUT_FILE}.gsm" + if not os.path.exists(input_gsm_file): + print(f"Error: {input_gsm_file} not created") + sock.close() + return + with open(input_gsm_file, "rb") as f: + voice_data = f.read() + + encrypted_data = encrypt_data(voice_data) + sock.send(encrypted_data) + print(f"Sent {len(encrypted_data)} bytes") + os.remove(input_gsm_file) # Clean up + else: + # Receiver mode: Wait for and receive data + print("Waiting for data from sender...") + received_data = b"" + sock.settimeout(5.0) + try: + while True: + print("Calling recv()...") + data = sock.recv(1024) + print(f"Received {len(data)} bytes") + if not data: + print("Connection closed by sender or simulator") + break + received_data += data + except socket.timeout: + print("Timed out waiting for data") + + if received_data: + with open("received.gsm", "wb") as f: + f.write(decrypt_data(received_data)) + print(f"Wrote {len(received_data)} bytes to received.gsm") + # Decode with untoast, then convert to WAV with sox + result = subprocess.run(["untoast", "received.gsm"], capture_output=True, text=True) + print(f"untoast return code: {result.returncode}") + print(f"untoast stderr: {result.stderr}") + if result.returncode == 0: + if os.path.exists("received"): + # Convert raw PCM to WAV (8 kHz, mono, 16-bit) + subprocess.run(["sox", "-t", "raw", "-r", "8000", "-e", "signed", "-b", "16", "-c", "1", "received", + OUTPUT_FILE]) + os.remove("received") + print(f"Received and saved {len(received_data)} bytes to {OUTPUT_FILE}") + else: + print("Error: 'received' file not created by untoast") + else: + print(f"untoast failed: {result.stderr}") + else: + print("No data received from simulator") + + sock.close() + + +if __name__ == "__main__": + mode = input("Enter 'send' to send data or 'receive' to receive data: ").strip().lower() + run_protocol(send_mode=(mode == "send")) \ No newline at end of file diff --git a/protocol_prototype/DryBox/received.wav b/protocol_prototype/DryBox/received.wav new file mode 100644 index 0000000..2bd6b18 Binary files /dev/null and b/protocol_prototype/DryBox/received.wav differ