apply threading and ax.25 to client

This commit is contained in:
John Burwell 2025-04-27 12:39:30 -05:00
parent bef2415ee7
commit 523c4ac958

View File

@ -1,82 +1,114 @@
import socket #!/usr/bin/env python3
import time import time
import threading
import os
import logging
import logging.handlers
import socket
import ax25 import ax25
import ax25.ports
import ax25.socket
# Settings
CLIENT_CALLSIGN = "N0CALL-7" CLIENT_CALLSIGN = "N0CALL-7"
BEACON_CALLSIGN = "KI5QKX-10" # We expect the server to be beaconing from here
# Setup logging
os.makedirs("logs", exist_ok=True)
logger = logging.getLogger("craprniac_client")
logger.setLevel(logging.INFO)
log_handler = logging.handlers.TimedRotatingFileHandler(
"logs/craprniac_client.log",
when="midnight",
interval=1,
backupCount=7
)
log_handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)s] %(message)s"
))
logger.addHandler(log_handler)
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(
"%(asctime)s [%(levelname)s] %(message)s"
))
logger.addHandler(console_handler)
# Globals for network state
configured = False configured = False
lease_expiration = None lease_expiration = None
def parse_message(data): # Packet builders
parts = data.split('|')
return parts
def build_request(network_name): def build_request(network_name):
return f"0.1|CRAP_REQUEST|{CLIENT_CALLSIGN}|{network_name}" return f"0.1|CRAP_REQUEST|{CLIENT_CALLSIGN}|{network_name}".encode('utf-8')
# Apply network configuration (mock)
def apply_network_config(assigned_ip, gateway, dns, lease_time): def apply_network_config(assigned_ip, gateway, dns, lease_time):
global configured, lease_expiration global configured, lease_expiration
print("\n[Network Configuration]") logger.info(f"Applying network config:")
print(f" Bringing up interface: ax0") logger.info(f" Assigned IP: {assigned_ip}")
print(f" Assigned IP address: {assigned_ip}") logger.info(f" Gateway: {gateway}")
print(f" Default Gateway: {gateway}") logger.info(f" DNS Server: {dns}")
print(f" DNS Server: {dns}") logger.info(f" Lease Time: {lease_time} seconds")
print(f" Lease Time: {lease_time} seconds")
print(" Interface ax0 configured successfully! 🚀")
print("----------------------------------------")
configured = True configured = True
lease_expiration = time.time() + int(lease_time) lease_expiration = time.time() + int(lease_time)
def main(): # Reset network configuration (mock)
def reset_network_config():
global configured, lease_expiration global configured, lease_expiration
logger.info("Resetting network configuration (lease expired)")
configured = False
lease_expiration = None
sock = socket.socket(socket.AF_AX25, socket.SOCK_DGRAM) # Main Client Logic
sock.bind((CLIENT_CALLSIGN,)) # Bind to our own callsign def main():
# Setup DGRAM socket to listen for beacons
beacon_sock = ax25.socket.Socket(socket.SOCK_DGRAM)
beacon_sock.bind(CLIENT_CALLSIGN)
print(f"Client {CLIENT_CALLSIGN} listening for CRAPRNIAC beacons...") logger.info(f"Client {CLIENT_CALLSIGN} started and waiting for beacons...")
while True: while True:
# Check lease expiration try:
if configured and lease_expiration and time.time() >= lease_expiration: if configured and lease_expiration and time.time() >= lease_expiration:
print("\n[Lease Expired]") reset_network_config()
print(" Tearing down interface ax0...")
print(" Ready to rejoin network.")
print("----------------------------------------")
configured = False
lease_expiration = None
data, addr = sock.recvfrom(1024) data, src_callsign = beacon_sock.recvfrom(1024) # recvfrom, not recv
decoded = data.decode('utf-8') parts = data.decode('utf-8').split('|')
parts = parse_message(decoded)
if len(parts) < 2: if len(parts) < 2:
continue continue
if parts[1] == "CRAP_BEACON": if parts[1] == "CRAP_BEACON" and not configured:
if not configured:
base_callsign = parts[2] base_callsign = parts[2]
network_name = parts[3] network_name = parts[3]
print(f"\nHeard beacon from {base_callsign} ({addr}):") logger.info(f"Received beacon from {base_callsign} (network {network_name})")
print(f" Network: {network_name}")
print("Sending join request...")
request = build_request(network_name).encode('utf-8')
sock.sendto(request, (base_callsign,))
elif parts[1] == "CRAP_ACCEPT": # Now create a connection socket for CRAP_REQUEST
client_callsign = parts[2] session_sock = ax25.socket.Socket() # SOCK_STREAM by default
network_name = parts[3] session_sock.bind(CLIENT_CALLSIGN)
assigned_ip = parts[4] session_sock.connect(base_callsign)
gateway = parts[5] session_sock.send(build_request(network_name))
dns = parts[6] logger.info(f"Sent CRAP_REQUEST to {base_callsign}")
lease_time = parts[7]
print(f"\nReceived CRAP_ACCEPT:")
print(f" Assigned IP: {assigned_ip}")
print(f" Gateway: {gateway}")
print(f" DNS Server: {dns}")
print(f" Lease Time: {lease_time} seconds")
apply_network_config(assigned_ip, gateway, dns, lease_time) response = session_sock.recv(1024)
parts = response.decode('utf-8').split('|')
if parts[1] == "CRAP_ACCEPT":
assigned_ip = parts[4]
gateway = parts[5]
dns = parts[6]
lease_time = parts[7]
logger.info(f"Received CRAP_ACCEPT from {base_callsign}")
apply_network_config(assigned_ip, gateway, dns, lease_time)
session_sock.close()
except Exception as e:
logger.error(f"Client error: {e}")
time.sleep(5)
if __name__ == "__main__": if __name__ == "__main__":
main() main()