From a2cb1615b3d2bfc06122dd5d2509de9d6ab95ca0 Mon Sep 17 00:00:00 2001 From: Vadim Ushakov Date: Wed, 10 Jul 2019 21:12:25 +0700 Subject: [PATCH] Move the BitTorrent related code from SiteAnnouncer.py (#2078) * Move the BitTorrent related code from SiteAnnouncer.py to a separate plugin * AnnounceBitTorrentPlugin.py: add missing `from Debug import Debug` --- .../AnnounceBitTorrentPlugin.py | 150 ++++++++++++++++++ plugins/AnnounceBitTorrent/__init__.py | 1 + src/Site/SiteAnnouncer.py | 125 +-------------- 3 files changed, 152 insertions(+), 124 deletions(-) create mode 100644 plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py create mode 100644 plugins/AnnounceBitTorrent/__init__.py diff --git a/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py b/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py new file mode 100644 index 00000000..ae674c00 --- /dev/null +++ b/plugins/AnnounceBitTorrent/AnnounceBitTorrentPlugin.py @@ -0,0 +1,150 @@ +import time +import urllib.request +import struct +import socket + +import bencode +from lib.subtl.subtl import UdpTrackerClient +import socks +import sockshandler +import gevent + +from Plugin import PluginManager +from Config import config +from Debug import Debug +from util import helper + + +# We can only import plugin host clases after the plugins are loaded +@PluginManager.afterLoad +def importHostClasses(): + global Peer, AnnounceError + from Peer import Peer + from Site.SiteAnnouncer import AnnounceError + + +@PluginManager.registerTo("SiteAnnouncer") +class SiteAnnouncerPlugin(object): + def getSupportedTrackers(self): + trackers = super(SiteAnnouncerPlugin, self).getSupportedTrackers() + if config.disable_udp or config.trackers_proxy != "disable": + trackers = [tracker for tracker in trackers if not tracker.startswith("udp://")] + + return trackers + + def getTrackerHandler(self, protocol): + if protocol == "udp": + handler = self.announceTrackerUdp + elif protocol == "http": + handler = self.announceTrackerHttp + elif protocol == "https": + handler = self.announceTrackerHttps + else: + handler = super(SiteAnnouncerPlugin, self).getTrackerHandler(protocol) + return handler + + def announceTrackerUdp(self, tracker_address, mode="start", num_want=10): + s = time.time() + if config.disable_udp: + raise AnnounceError("Udp disabled by config") + if config.trackers_proxy != "disable": + raise AnnounceError("Udp trackers not available with proxies") + + ip, port = tracker_address.split("/")[0].split(":") + tracker = UdpTrackerClient(ip, int(port)) + if helper.getIpType(ip) in self.getOpenedServiceTypes(): + tracker.peer_port = self.fileserver_port + else: + tracker.peer_port = 0 + tracker.connect() + if not tracker.poll_once(): + raise AnnounceError("Could not connect") + tracker.announce(info_hash=self.site.address_sha1, num_want=num_want, left=431102370) + back = tracker.poll_once() + if not back: + raise AnnounceError("No response after %.0fs" % (time.time() - s)) + elif type(back) is dict and "response" in back: + peers = back["response"]["peers"] + else: + raise AnnounceError("Invalid response: %r" % back) + + return peers + + def httpRequest(self, url): + headers = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', + 'Accept-Encoding': 'none', + 'Accept-Language': 'en-US,en;q=0.8', + 'Connection': 'keep-alive' + } + + req = urllib.request.Request(url, headers=headers) + + if config.trackers_proxy == "tor": + tor_manager = self.site.connection_server.tor_manager + handler = sockshandler.SocksiPyHandler(socks.SOCKS5, tor_manager.proxy_ip, tor_manager.proxy_port) + opener = urllib.request.build_opener(handler) + return opener.open(req, timeout=50) + elif config.trackers_proxy == "disable": + return urllib.request.urlopen(req, timeout=25) + else: + proxy_ip, proxy_port = config.trackers_proxy.split(":") + handler = sockshandler.SocksiPyHandler(socks.SOCKS5, proxy_ip, int(proxy_port)) + opener = urllib.request.build_opener(handler) + return opener.open(req, timeout=50) + + def announceTrackerHttps(self, *args, **kwargs): + kwargs["protocol"] = "https" + return self.announceTrackerHttp(*args, **kwargs) + + def announceTrackerHttp(self, tracker_address, mode="start", num_want=10, protocol="http"): + tracker_ip, tracker_port = tracker_address.rsplit(":", 1) + if helper.getIpType(tracker_ip) in self.getOpenedServiceTypes(): + port = self.fileserver_port + else: + port = 1 + params = { + 'info_hash': self.site.address_sha1, + 'peer_id': self.peer_id, 'port': port, + 'uploaded': 0, 'downloaded': 0, 'left': 431102370, 'compact': 1, 'numwant': num_want, + 'event': 'started' + } + + url = protocol + "://" + tracker_address + "?" + urllib.parse.urlencode(params) + + s = time.time() + response = None + # Load url + if config.tor == "always" or config.trackers_proxy != "disable": + timeout = 60 + else: + timeout = 30 + + with gevent.Timeout(timeout, False): # Make sure of timeout + req = self.httpRequest(url) + response = req.read() + req.close() + req = None + + if not response: + raise AnnounceError("No response after %.0fs" % (time.time() - s)) + + # Decode peers + try: + peer_data = bencode.decode(response)["peers"] + if type(peer_data) is not bytes: + peer_data = peer_data.encode() + response = None + peer_count = int(len(peer_data) / 6) + peers = [] + for peer_offset in range(peer_count): + off = 6 * peer_offset + peer = peer_data[off:off + 6] + addr, port = struct.unpack('!LH', peer) + peers.append({"addr": socket.inet_ntoa(struct.pack('!L', addr)), "port": port}) + except Exception as err: + raise AnnounceError("Invalid response: %r (%s)" % (response, Debug.formatException(err))) + + return peers diff --git a/plugins/AnnounceBitTorrent/__init__.py b/plugins/AnnounceBitTorrent/__init__.py new file mode 100644 index 00000000..c7422855 --- /dev/null +++ b/plugins/AnnounceBitTorrent/__init__.py @@ -0,0 +1 @@ +from . import AnnounceBitTorrentPlugin \ No newline at end of file diff --git a/src/Site/SiteAnnouncer.py b/src/Site/SiteAnnouncer.py index f066a033..2e2a353f 100644 --- a/src/Site/SiteAnnouncer.py +++ b/src/Site/SiteAnnouncer.py @@ -1,16 +1,9 @@ import random import time import hashlib -import urllib.request -import struct -import socket import re import collections -import bencode -from lib.subtl.subtl import UdpTrackerClient -import socks -import sockshandler import gevent from Plugin import PluginManager @@ -41,8 +34,6 @@ class SiteAnnouncer(object): def getSupportedTrackers(self): trackers = self.getTrackers() - if config.disable_udp or config.trackers_proxy != "disable": - trackers = [tracker for tracker in trackers if not tracker.startswith("udp://")] if not self.site.connection_server.tor_manager.enabled: trackers = [tracker for tracker in trackers if ".onion" not in tracker] @@ -157,15 +148,7 @@ class SiteAnnouncer(object): self.updateWebsocket(pex="announced") def getTrackerHandler(self, protocol): - if protocol == "udp": - handler = self.announceTrackerUdp - elif protocol == "http": - handler = self.announceTrackerHttp - elif protocol == "https": - handler = self.announceTrackerHttps - else: - handler = None - return handler + return None def getAddressParts(self, tracker): if "://" not in tracker or not re.match("^[A-Za-z0-9:/\\.#-]+$", tracker): @@ -266,112 +249,6 @@ class SiteAnnouncer(object): ) return time.time() - s - def announceTrackerUdp(self, tracker_address, mode="start", num_want=10): - s = time.time() - if config.disable_udp: - raise AnnounceError("Udp disabled by config") - if config.trackers_proxy != "disable": - raise AnnounceError("Udp trackers not available with proxies") - - ip, port = tracker_address.split("/")[0].split(":") - tracker = UdpTrackerClient(ip, int(port)) - if helper.getIpType(ip) in self.getOpenedServiceTypes(): - tracker.peer_port = self.fileserver_port - else: - tracker.peer_port = 0 - tracker.connect() - if not tracker.poll_once(): - raise AnnounceError("Could not connect") - tracker.announce(info_hash=self.site.address_sha1, num_want=num_want, left=431102370) - back = tracker.poll_once() - if not back: - raise AnnounceError("No response after %.0fs" % (time.time() - s)) - elif type(back) is dict and "response" in back: - peers = back["response"]["peers"] - else: - raise AnnounceError("Invalid response: %r" % back) - - return peers - - def httpRequest(self, url): - headers = { - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11', - 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', - 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', - 'Accept-Encoding': 'none', - 'Accept-Language': 'en-US,en;q=0.8', - 'Connection': 'keep-alive' - } - - req = urllib.request.Request(url, headers=headers) - - if config.trackers_proxy == "tor": - tor_manager = self.site.connection_server.tor_manager - handler = sockshandler.SocksiPyHandler(socks.SOCKS5, tor_manager.proxy_ip, tor_manager.proxy_port) - opener = urllib.request.build_opener(handler) - return opener.open(req, timeout=50) - elif config.trackers_proxy == "disable": - return urllib.request.urlopen(req, timeout=25) - else: - proxy_ip, proxy_port = config.trackers_proxy.split(":") - handler = sockshandler.SocksiPyHandler(socks.SOCKS5, proxy_ip, int(proxy_port)) - opener = urllib.request.build_opener(handler) - return opener.open(req, timeout=50) - - def announceTrackerHttps(self, *args, **kwargs): - kwargs["protocol"] = "https" - return self.announceTrackerHttp(*args, **kwargs) - - def announceTrackerHttp(self, tracker_address, mode="start", num_want=10, protocol="http"): - tracker_ip, tracker_port = tracker_address.rsplit(":", 1) - if helper.getIpType(tracker_ip) in self.getOpenedServiceTypes(): - port = self.fileserver_port - else: - port = 1 - params = { - 'info_hash': self.site.address_sha1, - 'peer_id': self.peer_id, 'port': port, - 'uploaded': 0, 'downloaded': 0, 'left': 431102370, 'compact': 1, 'numwant': num_want, - 'event': 'started' - } - - url = protocol + "://" + tracker_address + "?" + urllib.parse.urlencode(params) - - s = time.time() - response = None - # Load url - if config.tor == "always" or config.trackers_proxy != "disable": - timeout = 60 - else: - timeout = 30 - - with gevent.Timeout(timeout, False): # Make sure of timeout - req = self.httpRequest(url) - response = req.read() - req.close() - req = None - - if not response: - raise AnnounceError("No response after %.0fs" % (time.time() - s)) - - # Decode peers - try: - peer_data = bencode.decode(response)["peers"] - if type(peer_data) is not bytes: - peer_data = peer_data.encode() - response = None - peer_count = int(len(peer_data) / 6) - peers = [] - for peer_offset in range(peer_count): - off = 6 * peer_offset - peer = peer_data[off:off + 6] - addr, port = struct.unpack('!LH', peer) - peers.append({"addr": socket.inet_ntoa(struct.pack('!L', addr)), "port": port}) - except Exception as err: - raise AnnounceError("Invalid response: %r (%s)" % (response, Debug.formatException(err))) - - return peers - @util.Noparallel(blocking=False) def announcePex(self, query_num=2, need_num=5): peers = self.site.getConnectedPeers()