瀏覽代碼

initial commit

Billy Barrow 1 年之前
當前提交
fbf2668675
共有 5 個文件被更改,包括 183 次插入0 次删除
  1. 1 0
      .gitignore
  2. 6 0
      README.md
  3. 105 0
      RiddleBtt/Riddle.py
  4. 0 0
      RiddleBtt/__init__.py
  5. 71 0
      RiddleBtt/__main__.py

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+__pycache__

+ 6 - 0
README.md

@@ -0,0 +1,6 @@
+# Riddle BTT
+
+This is a small simple local service that provides a bittorrent tracker at http://[::1]:877/announce when run.
+
+The tracker uses [Riddle](https://git.sr.ht/~tilo15/Riddle) to find peers using a riddle based on the infohash of the torrent and report that back to the bittorrent client.
+

+ 105 - 0
RiddleBtt/Riddle.py

@@ -0,0 +1,105 @@
+
+from gi.repository import Riddle
+from gi.repository import Gio
+from gi.repository import GLib
+import threading
+import hashlib
+
+class RiddleClient:
+
+    GROUP_NAME = "riddle-btt"
+
+    def __init__(self):
+        self.loop = GLib.MainLoop()
+        self.__service = Riddle.construct_best_available_service()
+        self.infohashes = set()
+        self.addresses = set()
+
+        self.mirror_riddles_infohash_map = {}
+        self.peer_riddles_infohash_map = {}
+
+        self.mirror_peers = {}
+        self.peer_peers = {}
+
+        self.riddle_sets = {}
+
+        self.__service.connect("discovered_peer", self.__discovery_made)
+        self.__service.join_group(self.GROUP_NAME)
+
+        threading.Thread(target=self.__mainloop).start()
+
+    def __mainloop(self):
+        self.loop.run()
+
+    def add_riddles(self, infohash, port, is_mirror):
+        public_ip = self.__service.get_public_ip()
+        socket_address = Gio.InetSocketAddress.new_from_string(public_ip.to_string(), port)
+
+        mirror_riddle = Riddle.SharedKeyRiddle.new(self.__get_key(infohash, True))
+        mirror_riddle.set_group(self.GROUP_NAME)
+        
+        peer_riddle = Riddle.SharedKeyRiddle.new(self.__get_key(infohash, False))
+        peer_riddle.set_group(self.GROUP_NAME)
+        peer_riddle.set_app_socket_address(socket_address)
+        print("Created")
+        self.infohashes.add(infohash)
+
+        self.mirror_riddles_infohash_map[mirror_riddle] = infohash
+        if(infohash not in self.mirror_peers):
+            self.mirror_peers[infohash] = []
+
+        self.peer_riddles_infohash_map[peer_riddle] = infohash
+        if(infohash not in self.peer_peers):
+            self.peer_peers[infohash] = []
+
+        self.__service.register_riddle(peer_riddle)
+        if(is_mirror):
+            mirror_riddle.set_app_socket_address(socket_address)
+            self.__service.register_riddle(mirror_riddle)
+
+        print("Registered")            
+
+        self.__service.publish_riddle(peer_riddle)
+        self.__service.publish_riddle(mirror_riddle)
+
+        print("Registered")     
+
+        self.riddle_sets[infohash] = [peer_riddle, mirror_riddle]
+
+    def republish_riddle(self, infohash):
+        for riddle in self.riddle_sets[infohash]:
+            self.__service.publish_riddle(riddle)
+
+    def __discovery_made(self, service, address, riddle):
+        print("Discovery made: {}".format(address))
+
+        if(address.to_string() in self.addresses):
+            print("Already disovered")
+            return
+        else:
+            self.addresses.add(address.to_string())
+
+        if(riddle in self.mirror_riddles_infohash_map):
+            infohash = self.mirror_riddles_infohash_map[riddle]
+            self.mirror_peers[infohash].append(address)
+
+        elif(riddle in self.peer_riddles_infohash_map):
+            infohash = self.peer_riddles_infohash_map[riddle]
+            self.peer_peers[infohash].append(address)
+
+
+        else:
+            print("Discovery made but no matching infohash found.")
+
+    def __get_key(self, infohash, mirror):
+        hasher = hashlib.sha256()
+        hasher.update(infohash.encode())
+        res = hasher.digest()
+
+        if(mirror):
+            hasher = hashlib.sha256()
+            hasher.update(res)
+            res = hasher.digest()
+        
+        return res
+

+ 0 - 0
RiddleBtt/__init__.py


+ 71 - 0
RiddleBtt/__main__.py

@@ -0,0 +1,71 @@
+
+from http.server import HTTPServer
+from http.server import BaseHTTPRequestHandler
+
+from RiddleBtt import Riddle
+import urllib.parse
+import fastbencode
+import sys
+import socket
+
+client = Riddle.RiddleClient()
+
+class RiddleBttHttpHandler(BaseHTTPRequestHandler):
+
+    def do_GET(self):
+        print(self.path)
+
+        if(self.client_address[0] == "::1" and self.path.startswith("/announce")):
+            return self.do_ANNOUNCE()
+
+        self.send_response(404)
+        self.end_headers()
+        self.wfile.write(b"Not Found")
+        self.wfile.flush()
+
+
+    def do_ANNOUNCE(self):
+        param_str = self.path.split("?", 2)[1]
+        params = urllib.parse.parse_qs(param_str)
+        print(params)
+
+        infohash = params["info_hash"][0]
+        port = int(params["port"][0])
+        is_mirror = params["left"][0] == "0"
+        
+        if(infohash not in client.infohashes):
+            client.add_riddles(infohash, port, is_mirror)
+        else:
+            client.republish_riddle(infohash)
+
+        peers = []
+        for peer in client.peer_peers[infohash]:
+            peers.append({
+                b"ip": peer.get_address().to_string().encode("ASCII"),
+                b"port": peer.get_port()
+            })
+
+        for peer in client.mirror_peers[infohash]:
+            peers.append({
+                b"ip": peer.get_address().to_string().encode("ASCII"),
+                b"port": peer.get_port()
+            })
+
+        response = {
+            b"interval": 10,
+            b"complete": len(client.mirror_peers[infohash]),
+            b"incomplete": len(client.peer_peers[infohash]),
+            b"peers": peers
+        }
+
+        self.send_response(200)
+        self.end_headers()
+        self.wfile.write(fastbencode.bencode(response))
+        self.wfile.flush()
+
+
+class RiddleBttHttpServer(HTTPServer):
+    address_family = socket.AF_INET6
+
+httpd = RiddleBttHttpServer(('::1', 877), RiddleBttHttpHandler)
+httpd.serve_forever()