-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathannounce.php
More file actions
112 lines (89 loc) · 2.52 KB
/
announce.php
File metadata and controls
112 lines (89 loc) · 2.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<?php
declare(strict_types=1);
const ANNOUNCE_INTERVAL = 1800; // seconds
const PEER_TTL = 5400; // reap after 90 min
const MAX_PEERS = 50;
function fail(string $msg): never {
echo bencode(['failure reason' => $msg]);
exit;
}
function bencode(mixed $v): string {
if (is_int($v)) return 'i' . $v . 'e';
if (is_string($v)) return strlen($v) . ':' . $v;
if (is_array($v)) {
$isList = array_keys($v) === range(0, count($v) - 1);
$out = $isList ? 'l' : 'd';
foreach ($v as $k => $val) {
if (!$isList) $out .= bencode((string)$k);
$out .= bencode($val);
}
return $out . 'e';
}
return '';
}
function getBinary(string $s, int $len): string {
$raw = rawurldecode($s);
if (strlen($raw) !== $len) fail('invalid parameter');
return $raw;
}
$path = trim($_SERVER['PATH_INFO'] ?? '', '/');
if ($path === '') fail('missing passkey');
$passkey = $path;
$infoHash = getBinary($_GET['info_hash'] ?? '', 20);
$peerId = getBinary($_GET['peer_id'] ?? '', 20);
$port = (int)($_GET['port'] ?? 0);
$left = (int)($_GET['left'] ?? -1);
if ($port < 1 || $port > 65535 || $left < 0) {
fail('bad request');
}
$isSeed = ($left === 0);
$ip = inet_pton($_SERVER['REMOTE_ADDR']);
$now = time();
$pdo = new PDO(
'mysql:host=127.0.0.1;dbname=tracker;charset=utf8mb4',
'tracker',
'trackerpass',
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]
);
$u = $pdo->prepare('SELECT id FROM users WHERE passkey=? AND status="active"');
$u->execute([$passkey]);
$user = $u->fetch();
if (!$user) fail('unauthorized');
$t = $pdo->prepare('SELECT id FROM torrents WHERE info_hash=?');
$t->execute([$infoHash]);
$torrent = $t->fetch();
if (!$torrent) fail('unknown torrent');
$pdo->prepare(
'REPLACE INTO peers
(torrent_id,user_id,peer_id,ip,port,is_seed,last_seen)
VALUES (?,?,?,?,?,?,?)'
)->execute([
$torrent['id'],
$user['id'],
$peerId,
$ip,
$port,
$isSeed ? 1 : 0,
$now
]);
$pdo->prepare(
'DELETE FROM peers WHERE last_seen < ?'
)->execute([$now - PEER_TTL]);
$p = $pdo->prepare(
'SELECT ip, port FROM peers
WHERE torrent_id = ?
AND NOT (user_id = ? AND peer_id = ?)
LIMIT ' . MAX_PEERS
);
$p->execute([$torrent['id'], $user['id'], $peerId]);
$peers = '';
foreach ($p as $row) {
$peers .= $row['ip'] . pack('n', (int)$row['port']);
}
echo bencode([
'interval' => ANNOUNCE_INTERVAL,
'peers' => $peers
]);