/** * The announce action * @return $this * @internal param Request $request */ public function announce(Request $request) { Log::info($request->fullUrl()); // GET params sent by the client $passkey = Input::get('passkey'); $peer_id = base64_encode(Input::get('peer_id')); $info_hash = Input::get('info_hash'); $downloaded = Input::get('downloaded') ? intval(Input::get('downloaded')) : 0; $uploaded = Input::get('uploaded') ? intval(Input::get('uploaded')) : 0; $left = Input::get('left') ? intval(Input::get('left')) : 0; $compact = Input::get('compact') ? intval(Input::get('compact')) : 0; $no_peer_id = Input::get('no_peer_id') ? intval(Input::get('no_peer_id')) : 0; $event = strip_tags(Input::get('event')); // Client IP address $ipAddress = ''; // Check for X-Forwarded-For headers and use those if found if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && '' !== trim($_SERVER['HTTP_X_FORWARDED_FOR'])) { $ipAddress = trim($_SERVER['HTTP_X_FORWARDED_FOR']); } else { if (isset($_SERVER['REMOTE_ADDR']) && '' !== trim($_SERVER['REMOTE_ADDR'])) { $ipAddress = trim($_SERVER['REMOTE_ADDR']); } } // Check client ports $port = $_SERVER['REMOTE_PORT']; if (!$port || !ctype_digit($port) || intval($port) < 1 || intval($port) > 65535) { return BencodeHelper::bencodedResponseRaw("Invalid client port", 103); } if ($port == 999 && substr($peer_id, 0, 10) == '-TO0001-XX') { return BencodeHelper::bencodedResponseRaw("d8:completei0e10:incompletei0e8:intervali600e12:min intervali60e5:peersld2:ip12:72.14.194.184:port3:999ed2:ip11:72.14.194.14:port3:999ed2:ip12:72.14.194.654:port3:999eee", 103); } // Check passkey param if (!$passkey) { return BencodeHelper::bencodedResponseRaw("Missing passkey", 900); } // Find passkey-related user $user = User::has('passkeys', '=', $passkey)->get(); if ($user == null) { return BencodeHelper::bencodedResponseRaw("Invalid passkey", 900); } // Check info_hash param if (!$info_hash) { return BencodeHelper::bencodedResponseRaw("Missing info hash", 101); } $info_hash = strtoupper(bin2hex($info_hash)); // Check torrent hash $torrent = Torrent::getByInfoHash($info_hash); if (!$torrent || $torrent == null) { return BencodeHelper::bencodedResponseRaw("Torrent not registered with this tracker.", 900); } // Check if peer already exists $peer = Peer::getByIPAndPasskey($ipAddress, $passkey); // Create a new one if it does not if ($peer == null) { $attributes = ['hash' => $peer_id, 'user_agent' => $_SERVER['HTTP_USER_AGENT'], 'ip_address' => $ipAddress, 'passkey' => $passkey, 'port' => $port]; $peer = Peer::create($attributes); } // Check info_hash (must be 20 chars long) if (!$info_hash) { return BencodeHelper::bencodedResponseRaw("Invalid info hash.", 150); } // Check if peer is already assigned to torrent $peer_torrent = PeerTorrent::getByPeerAndTorrent($peer, $torrent); // Assign it, if not if ($peer_torrent == null) { $attributes = ['peer_id' => $peer->id, 'torrent_id' => $torrent->id, 'uploaded' => $uploaded, 'downloaded' => $downloaded, 'left' => $left]; // Event handling if ($event === BitTorrent::EVENT_STARTED || $event == BitTorrent::EVENT_COMPLETED) { $attributes['stopped'] = false; } else { $attributes['stopped'] = true; } PeerTorrent::create($attributes); } else { // Or update the existing one $peer_torrent->uploaded = $uploaded; $peer_torrent->downloaded = $downloaded; $peer_torrent->left = $left; $peer_torrent->save(); } // Perform announce on the torrent $torrent->announce(); // Build response $resp = BencodeHelper::buildAnnounceResponse($torrent, $peer_torrent, $compact, $no_peer_id); return BencodeHelper::bencodedResponseRaw($resp); }
/** * Builds tracker announce response in bencoded format * * @param Torrent $torrent * @param PeerTorrent $peerTorrent * @param $compact * @param int $no_peer_id * @return string */ public static function buildAnnounceResponse(Torrent $torrent, PeerTorrent $peerTorrent, $compact, $no_peer_id = 0) { // Compact mode if ($compact != 1) { $resp = "d" . self::bencodeString("interval") . "i" . self::__INTERVAL . "e" . self::bencodeString("peers") . "l"; } else { $resp = "d" . self::bencodeString("interval") . "i" . self::__INTERVAL . "e" . self::bencodeString("min interval") . "i" . 300 . "e5:" . "peers"; } $peer = array(); $peer_num = 0; foreach ($torrent->getPeersArray() as $row) { if ($compact != 1) { if ($row["peer_id"] === $peerTorrent->peerInstance()->hash) { continue; } $resp .= "d" . self::bencodeString("ip") . self::bencodeString($row['ip']); if ($no_peer_id == 0) { $resp .= self::bencodeString("peer id") . self::bencodeString($row["peer_id"]); } $resp .= self::bencodeString("port") . "i" . $row["port"] . "e" . "e"; } else { $peer_ip = explode('.', $row["ip"]); $peer_ip = pack("C*", $peer_ip[0], $peer_ip[1], $peer_ip[2], $peer_ip[3]); $peer_port = pack("n*", (int) $row["port"]); $time = intval(time() % 7680 / 60); if ($peerTorrent->left == 0) { $time += 128; } $time = pack("C", $time); $peer[] = $time . $peer_ip . $peer_port; $peer_num++; } } if ($compact != 1) { $resp .= "ee"; } else { $o = ""; for ($i = 0; $i < $peer_num; $i++) { $o .= substr($peer[$i], 1, 6); } $resp .= strlen($o) . ':' . $o . 'e'; } return $resp; }
/** * Updates leechers field and returns the object * * @return $this */ public function updateLeechers() { $this->leechers = count(PeerTorrent::getLeechers($this)); $this->save(); return $this; }