diff --git a/rtorrent_proxy.py b/rtorrent_proxy.py new file mode 100644 index 0000000..3fe4d9f --- /dev/null +++ b/rtorrent_proxy.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +""" +This file handles a rather silly use case: +You have two servers, A and B, each with various rtorrent instances running. +You want one instance of Aberrant, running on server A. In your infinite +wisdom, you bound the SCGI ports of the rtorrent instances on server B to +localhost, leaving Aberrant unable to query them. Rtorrent won't allow you +to rebind the SCGI port while it's running, and you don't want to restart +the rtorrent instance(s) because restarting stuff sucks. Therefore, you +need another way for Aberrant to communicate to them. + +This script acts as a proxy to the rtorrent instances on server B. It will +listen on a port (don't forget to open the port in your iptables), and +relay all data it receives to the torrent SCGI port and vice versa. + +To use: +Configure `port_mapping` in the form `listen_port`: `rtorrent_port`. +Run the script on server B. +""" +import socket +import threading + +port_mapping = { + 4000: 5000, + 4001: 5001, + 4002: 5002, +} + +class SocketProxy(threading.Thread): + """Represents a proxy to an rTorrent SCGI port.""" + def __init__(self, listen_port, rtorrent_port): + super().__init__() + self.listen_port = listen_port + self.rtorrent_port = rtorrent_port + self._stop_event = threading.Event() + + def stop(self): + """Sets the internal 'stop running' flag.""" + self._stop_event.set() + + def stopped(self): + """Returns true if the internal stop flag has been set.""" + return self._stop_event.is_set() + + def run(self): + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.settimeout(1) + s.bind(('0.0.0.0', self.listen_port)) + s.listen(1) + while not self.stopped(): + try: + conn, addr = s.accept() # client connected + except socket.timeout: + continue + with conn: + # receive data from client + data = [] + data.append(conn.recv(1024)) # the header really shouldn't be longer than this... + header = data[0].partition(b',')[0] + body_len = int(header.split(b'\x00')[1]) + recv_len = len(data[0]) + while recv_len < body_len + len(header) + 1: + data.append(conn.recv(1024)) + recv_len += len(data[-1]) + data = b''.join(data) + + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as rt_conn: + rt_conn.connect(('127.0.0.1', self.rtorrent_port)) + rt_conn.sendall(data) # send to rtorrent + + # receive data from rtorrent + data = [] + data.append(rt_conn.recv(1024)) + header = data[0].partition(b'