Files
proxynet/quad4_proxynet/server.py
2025-12-26 15:09:05 -06:00

162 lines
5.6 KiB
Python

import socket
import threading
import RNS
import RNS.vendor.umsgpack as umsgpack
from .common import APP_NAME, load_or_create_identity
class ProxynetServer:
def __init__(self, identity_name="proxynet_server"):
self.identity = load_or_create_identity(identity_name)
self.destination = RNS.Destination(
self.identity,
RNS.Destination.IN,
RNS.Destination.SINGLE,
APP_NAME,
"rproxy"
)
self.destination.set_link_established_callback(self.link_established)
self.active_links = {}
def announce(self):
self.destination.announce()
RNS.log(f"Server announced: {RNS.prettyhexrep(self.destination.hash)}", RNS.LOG_NOTICE)
def link_established(self, link):
RNS.log(f"Link established with {RNS.prettyhexrep(link.get_remote_identity().hash if link.get_remote_identity() else b'unknown')}", RNS.LOG_NOTICE)
link.set_packet_callback(self.packet_received)
link.set_link_closed_callback(self.link_closed)
def link_closed(self, link):
RNS.log("Link closed", RNS.LOG_NOTICE)
# Clean up any associated sockets
if link in self.active_links:
target_sock = self.active_links[link].get('target_sock')
if target_sock:
try:
target_sock.close()
except:
pass
del self.active_links[link]
def packet_received(self, message, packet):
try:
data = umsgpack.unpackb(message)
msg_type = data.get('type')
if msg_type == 'connect':
self.handle_connect(data, packet.link)
elif msg_type == 'data':
self.handle_data(data, packet.link)
elif msg_type == 'udp_packet':
self.handle_udp_packet(data, packet.link)
elif msg_type == 'close':
self.handle_close(packet.link)
except Exception as e:
RNS.log(f"Error processing packet: {e}", RNS.LOG_ERROR)
def handle_udp_packet(self, data, link):
host = data.get('host')
port = data.get('port')
payload = data.get('payload')
try:
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_sock.settimeout(2.0)
udp_sock.sendto(payload, (host, port))
# Try to receive a response
try:
response, addr = udp_sock.recvfrom(65535)
# Send back to client
resp_msg = umsgpack.packb({
'type': 'udp_response',
'host': addr[0],
'port': addr[1],
'payload': response
})
RNS.Packet(link, resp_msg).send()
except socket.timeout:
pass
finally:
udp_sock.close()
except Exception as e:
RNS.log(f"UDP handling error: {e}", RNS.LOG_DEBUG)
def handle_connect(self, data, link):
host = data.get('host')
port = data.get('port')
RNS.log(f"Connection request to {host}:{port}", RNS.LOG_INFO)
try:
target_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
target_sock.settimeout(10)
target_sock.connect((host, port))
target_sock.settimeout(None)
self.active_links[link] = {
'target_sock': target_sock,
'active': True
}
# Start a thread to read from the target socket and send back over Reticulum
threading.Thread(target=self.target_reader, args=(link, target_sock), daemon=True).start()
# Send success response
response = umsgpack.packb({'type': 'response', 'success': True})
RNS.Packet(link, response).send()
except Exception as e:
RNS.log(f"Failed to connect to {host}:{port}: {e}", RNS.LOG_ERROR)
response = umsgpack.packb({'type': 'response', 'success': False, 'error': str(e)})
RNS.Packet(link, response).send()
link.teardown()
def handle_data(self, data, link):
if link in self.active_links:
target_sock = self.active_links[link]['target_sock']
try:
target_sock.sendall(data.get('payload'))
except Exception as e:
RNS.log(f"Error sending to target: {e}", RNS.LOG_ERROR)
self.handle_close(link)
def handle_close(self, link):
if link in self.active_links:
target_sock = self.active_links[link]['target_sock']
try:
target_sock.close()
except:
pass
self.active_links[link]['active'] = False
del self.active_links[link]
link.teardown()
def target_reader(self, link, target_sock):
try:
while True:
data = target_sock.recv(16384)
if not data:
break
# Split data into chunks if necessary, though Reticulum Link handles some of this
# For now, just send it. RNS.Link handles mtu.
msg = umsgpack.packb({'type': 'data', 'payload': data})
RNS.Packet(link, msg).send()
except Exception as e:
RNS.log(f"Target reader error: {e}", RNS.LOG_DEBUG)
finally:
self.handle_close(link)
def run_server(identity_name="proxynet_server"):
server = ProxynetServer(identity_name)
server.announce()
import time
while True:
time.sleep(600)
server.announce()