]>
Commit | Line | Data |
---|---|---|
940227cb MT |
1 | #!/usr/bin/python |
2 | ||
9068dba1 | 3 | import re |
940227cb MT |
4 | import tornado.web |
5 | ||
f1349ebc | 6 | from backend.tracker import bencode, bdecode, encode_hex, decode_hex |
940227cb MT |
7 | from handlers_base import * |
8 | ||
9 | ||
10 | class TrackerIndexHandler(BaseHandler): | |
11 | def get(self): | |
938d083d | 12 | releases = self.releases.get_all() |
940227cb | 13 | |
938d083d MT |
14 | limit = 5 |
15 | releases = releases[:limit] | |
940227cb | 16 | |
938d083d | 17 | self.render("tracker-torrents.html", releases=releases) |
940227cb MT |
18 | |
19 | ||
20 | class TrackerDetailHandler(BaseHandler): | |
938d083d MT |
21 | def get(self, torrent_hash): |
22 | file = self.releases.get_file_for_torrent_hash(torrent_hash) | |
940227cb | 23 | |
938d083d MT |
24 | if not file: |
25 | raise tornado.web.HTTPError(404, "Could not find torrent file for hash: %s" % torrent_hash) | |
940227cb | 26 | |
938d083d | 27 | peers = self.tracker.get_peers(torrent_hash) |
940227cb | 28 | |
938d083d | 29 | self.render("tracker-torrent-detail.html", release=file.release, |
6d524cba | 30 | file=file, peers=peers) |
940227cb MT |
31 | |
32 | ||
fadcfd00 MT |
33 | class TrackerDownloadHandler(BaseHandler): |
34 | def get(self, torrent_hash): | |
938d083d | 35 | file = self.releases.get_file_for_torrent_hash(torrent_hash) |
fadcfd00 MT |
36 | |
37 | if not file: | |
938d083d | 38 | raise tornado.web.HTTPError(404, "Could not find torrent file for hash: %s" % torrent_hash) |
fadcfd00 MT |
39 | |
40 | # Redirect the user to the download redirector. | |
938d083d | 41 | self.redirect("http://downloads.ipfire.org/%s.torrent" % file.filename) |
fadcfd00 MT |
42 | |
43 | ||
9068dba1 | 44 | class TrackerBaseHandler(BaseHandler): |
940227cb MT |
45 | def get_hexencoded_argument(self, name, all=False): |
46 | try: | |
47 | arguments = self.request.arguments[name] | |
48 | except KeyError: | |
49 | return None | |
50 | ||
51 | arguments_new = [] | |
52 | for argument in arguments: | |
f1349ebc | 53 | arguments_new.append(encode_hex(argument)) |
940227cb MT |
54 | |
55 | arguments = arguments_new | |
56 | ||
57 | if all: | |
58 | return arguments | |
59 | ||
60 | return arguments[0] | |
61 | ||
62 | def send_tracker_error(self, error_message): | |
9068dba1 MT |
63 | msg = bencode({"failure reason" : error_message }) |
64 | self.finish(msg) | |
940227cb MT |
65 | |
66 | ||
67 | class TrackerAnnounceHandler(TrackerBaseHandler): | |
9068dba1 | 68 | def prepare(self): |
940227cb MT |
69 | self.set_header("Content-Type", "text/plain") |
70 | ||
9068dba1 MT |
71 | def get_ipv6_address(self, default_port): |
72 | # Get the external IP address of the client. | |
73 | addr = self.get_remote_ip() | |
74 | ||
75 | if ":" in addr: | |
76 | return addr, default_port | |
77 | ||
78 | # IPv6 | |
79 | ipv6 = self.get_argument("ipv6", None) | |
80 | if ipv6: | |
81 | port = default_port | |
82 | ||
83 | m = re.match("^\[(.*)\]\:(\d)$", ipv6) | |
84 | if m: | |
85 | ipv6, port = (m.group(1), m.group(2)) | |
86 | ||
87 | return ipv6, port | |
88 | ||
89 | return None, None | |
90 | ||
91 | def get_ipv4_address(self, default_port): | |
92 | # Get the external IP address of the client. | |
93 | addr = self.get_remote_ip() | |
94 | ||
ea324f48 MT |
95 | if addr.startswith("172.28.1."): |
96 | addr = "178.63.73.246" | |
97 | ||
9068dba1 MT |
98 | if not ":" in addr: |
99 | return addr, default_port | |
100 | ||
101 | # IPv4 | |
102 | ipv4 = self.get_argument("ipv4", None) | |
103 | if ipv4: | |
104 | return ipv4, default_port | |
105 | ||
106 | ip = self.get_argument("ip", None) | |
107 | if ip: | |
108 | return ip, default_port | |
109 | ||
110 | return None, None | |
111 | ||
112 | def get_port(self): | |
113 | # Get the port and check it for sanity | |
114 | port = self.get_argument("port", None) | |
115 | ||
116 | try: | |
117 | port = int(port) | |
118 | ||
119 | if port < 0 or port > 65535: | |
120 | raise ValueError | |
121 | except (TypeError, ValueError): | |
122 | port = None | |
123 | ||
124 | return port | |
125 | ||
126 | def get(self): | |
127 | # Get the info hash | |
940227cb MT |
128 | info_hash = self.get_hexencoded_argument("info_hash") |
129 | if not info_hash: | |
9068dba1 | 130 | self.send_tracker_error("Your client forgot to send your torrent's info_hash") |
940227cb MT |
131 | return |
132 | ||
9068dba1 MT |
133 | # Get the peer id |
134 | peer_id = self.get_hexencoded_argument("peer_id") | |
f611ae02 | 135 | |
9068dba1 MT |
136 | # Get the port and check it for sanity |
137 | port = self.get_port() | |
138 | if not port: | |
139 | self.send_tracker_error("Invalid port number or port number missing") | |
940227cb MT |
140 | return |
141 | ||
9068dba1 MT |
142 | addr_ipv6, port_ipv6 = self.get_ipv6_address(port) |
143 | addr_ipv4, port_ipv4 = self.get_ipv4_address(port) | |
940227cb | 144 | |
9068dba1 MT |
145 | # Handle events |
146 | event = self.get_argument("event", None) | |
147 | if event: | |
148 | if not event in ("started", "stopped", "completed"): | |
149 | self.send_tracker_error("Got unknown event") | |
940227cb MT |
150 | return |
151 | ||
08f85138 | 152 | self.tracker.handle_event(event, peer_id, info_hash) |
940227cb | 153 | |
c300b349 MT |
154 | if event == "stopped": |
155 | return | |
156 | ||
9068dba1 MT |
157 | peer_info = { |
158 | "address6" : addr_ipv6, | |
159 | "port6" : port_ipv6, | |
160 | "address4" : addr_ipv4, | |
161 | "port4" : port_ipv4, | |
162 | "downloaded" : self.get_argument("downloaded", 0), | |
163 | "uploaded" : self.get_argument("uploaded", 0), | |
164 | "left_data" : self.get_argument("left", 0), | |
165 | } | |
940227cb | 166 | |
9068dba1 | 167 | self.tracker.update_peer(peer_id, info_hash, **peer_info) |
940227cb MT |
168 | |
169 | no_peer_id = self.get_argument("no_peer_id", False) | |
170 | numwant = self.get_argument("numwant", self.tracker.numwant) | |
171 | ||
9068dba1 MT |
172 | peers = self.tracker.get_peers(info_hash, limit=numwant, no_peer_id=no_peer_id) |
173 | ||
174 | response = bencode({ | |
175 | "tracker id" : self.tracker.tracker_id, | |
176 | "interval" : self.tracker.interval, | |
940227cb | 177 | "min interval" : self.tracker.min_interval, |
9068dba1 MT |
178 | "peers" : peers, |
179 | "complete" : self.tracker.complete(info_hash), | |
180 | "incomplete" : self.tracker.incomplete(info_hash), | |
181 | }) | |
182 | self.finish(response) | |
183 | ||
184 | def on_finish(self): | |
185 | """ | |
186 | Cleanup after every request. | |
187 | """ | |
188 | self.tracker.cleanup_peers() | |
940227cb MT |
189 | |
190 | ||
191 | class TrackerScrapeHandler(TrackerBaseHandler): | |
192 | def get(self): | |
193 | info_hashes = self.get_hexencoded_argument("info_hash", all=True) | |
194 | ||
9068dba1 MT |
195 | response = self.tracker.scrape(info_hashes) |
196 | ||
197 | self.finish(bencode(response)) |