AnnounceBitTorrentPlugin.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. import time
  2. import urllib.request
  3. import struct
  4. import socket
  5. import lib.bencode_open as bencode_open
  6. from lib.subtl.subtl import UdpTrackerClient
  7. import socks
  8. import sockshandler
  9. import gevent
  10. from Plugin import PluginManager
  11. from Config import config
  12. from Debug import Debug
  13. from util import helper
  14. # We can only import plugin host clases after the plugins are loaded
  15. @PluginManager.afterLoad
  16. def importHostClasses():
  17. global Peer, AnnounceError
  18. from Peer import Peer
  19. from Site.SiteAnnouncer import AnnounceError
  20. @PluginManager.registerTo("SiteAnnouncer")
  21. class SiteAnnouncerPlugin(object):
  22. def getSupportedTrackers(self):
  23. trackers = super(SiteAnnouncerPlugin, self).getSupportedTrackers()
  24. if config.disable_udp or config.trackers_proxy != "disable":
  25. trackers = [tracker for tracker in trackers if not tracker.startswith("udp://")]
  26. return trackers
  27. def getTrackerHandler(self, protocol):
  28. if protocol == "udp":
  29. handler = self.announceTrackerUdp
  30. elif protocol == "http":
  31. handler = self.announceTrackerHttp
  32. elif protocol == "https":
  33. handler = self.announceTrackerHttps
  34. else:
  35. handler = super(SiteAnnouncerPlugin, self).getTrackerHandler(protocol)
  36. return handler
  37. def announceTrackerUdp(self, tracker_address, mode="start", num_want=10):
  38. s = time.time()
  39. if config.disable_udp:
  40. raise AnnounceError("Udp disabled by config")
  41. if config.trackers_proxy != "disable":
  42. raise AnnounceError("Udp trackers not available with proxies")
  43. ip, port = tracker_address.split("/")[0].split(":")
  44. tracker = UdpTrackerClient(ip, int(port))
  45. if helper.getIpType(ip) in self.getOpenedServiceTypes():
  46. tracker.peer_port = self.fileserver_port
  47. else:
  48. tracker.peer_port = 0
  49. tracker.connect()
  50. if not tracker.poll_once():
  51. raise AnnounceError("Could not connect")
  52. tracker.announce(info_hash=self.site.address_sha1, num_want=num_want, left=431102370)
  53. back = tracker.poll_once()
  54. if not back:
  55. raise AnnounceError("No response after %.0fs" % (time.time() - s))
  56. elif type(back) is dict and "response" in back:
  57. peers = back["response"]["peers"]
  58. else:
  59. raise AnnounceError("Invalid response: %r" % back)
  60. return peers
  61. def httpRequest(self, url):
  62. headers = {
  63. 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
  64. 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  65. 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3',
  66. 'Accept-Encoding': 'none',
  67. 'Accept-Language': 'en-US,en;q=0.8',
  68. 'Connection': 'keep-alive'
  69. }
  70. req = urllib.request.Request(url, headers=headers)
  71. if config.trackers_proxy == "tor":
  72. tor_manager = self.site.connection_server.tor_manager
  73. handler = sockshandler.SocksiPyHandler(socks.SOCKS5, tor_manager.proxy_ip, tor_manager.proxy_port)
  74. opener = urllib.request.build_opener(handler)
  75. return opener.open(req, timeout=50)
  76. elif config.trackers_proxy == "disable":
  77. return urllib.request.urlopen(req, timeout=25)
  78. else:
  79. proxy_ip, proxy_port = config.trackers_proxy.split(":")
  80. handler = sockshandler.SocksiPyHandler(socks.SOCKS5, proxy_ip, int(proxy_port))
  81. opener = urllib.request.build_opener(handler)
  82. return opener.open(req, timeout=50)
  83. def announceTrackerHttps(self, *args, **kwargs):
  84. kwargs["protocol"] = "https"
  85. return self.announceTrackerHttp(*args, **kwargs)
  86. def announceTrackerHttp(self, tracker_address, mode="start", num_want=10, protocol="http"):
  87. tracker_ip, tracker_port = tracker_address.rsplit(":", 1)
  88. if helper.getIpType(tracker_ip) in self.getOpenedServiceTypes():
  89. port = self.fileserver_port
  90. else:
  91. port = 1
  92. params = {
  93. 'info_hash': self.site.address_sha1,
  94. 'peer_id': self.peer_id, 'port': port,
  95. 'uploaded': 0, 'downloaded': 0, 'left': 431102370, 'compact': 1, 'numwant': num_want,
  96. 'event': 'started'
  97. }
  98. url = protocol + "://" + tracker_address + "?" + urllib.parse.urlencode(params)
  99. s = time.time()
  100. response = None
  101. # Load url
  102. if config.tor == "always" or config.trackers_proxy != "disable":
  103. timeout = 60
  104. else:
  105. timeout = 30
  106. with gevent.Timeout(timeout, False): # Make sure of timeout
  107. req = self.httpRequest(url)
  108. response = req.read()
  109. req.close()
  110. req = None
  111. if not response:
  112. raise AnnounceError("No response after %.0fs" % (time.time() - s))
  113. # Decode peers
  114. try:
  115. peer_data = bencode_open.loads(response)[b"peers"]
  116. response = None
  117. peer_count = int(len(peer_data) / 6)
  118. peers = []
  119. for peer_offset in range(peer_count):
  120. off = 6 * peer_offset
  121. peer = peer_data[off:off + 6]
  122. addr, port = struct.unpack('!LH', peer)
  123. peers.append({"addr": socket.inet_ntoa(struct.pack('!L', addr)), "port": port})
  124. except Exception as err:
  125. raise AnnounceError("Invalid response: %r (%s)" % (response, Debug.formatException(err)))
  126. return peers