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