0byt3m1n1
Path:
/
data
/
applications
/
aps.bak
/
wordpress
/
plugins
/
podpress
/
[
Home
]
File: tracker.php
<?php header("Content-type: text/plain"); header("Pragma: no-cache"); ignore_user_abort(1); $GLOBALS["peer_id"] = ""; $summaryupdate = array(); require_once("config.php"); require_once("funcsv2.php"); // Hey, want to ban shareaza? Remove slashes on these lines //if (isset($_SERVER["HTTP_USER_AGENT"])) // if (stristr($_SERVER["HTTP_USER_AGENT"], "Shareaza") || substr($_SERVER["HTTP_USER_AGENT"], 0, 5) == "RAZA ") // showError("Shareaza is not allowed on this torrent."); // Prep database if ($GLOBALS["persist"]) $db = @mysql_pconnect($dbhost, $dbuser, $dbpass) or showError("Tracker error: can't connect to database. Contact the webmaster."); else $db = @mysql_connect($dbhost, $dbuser, $dbpass) or showError("Tracker error: can't connect to database. Contact the webmaster."); @mysql_select_db($database) or showError("Tracker error: can't open database. Contact the webmaster"); /* A version of the BitTorrent tracker written in PHP and using MySQL as a manager. These paragraphs outline my design decisions. BTTrack uses a whole database which can be shared. Each torrent uses its own table while a single "summary" table shows overall information on each individual torrent at a glance. Putting all the torrent data in one table is easy enought to do (a primary key), but that would have some speed implications on a server with a lot of torrents. Before you begin, you must have a MySQL server configured with an appropriate user for BitTorrent, a database with permissions, etc. Create a summary table using the command below, and run maketorrents.php for each torrent you want the server to handle. You can use the instal.php script to prepare the database if you have appropriate database permission. In the future, I'm planning on writing code to delete torrents. A consistency checked is available in sanity.php. */ if (isset ($_SERVER["PATH_INFO"])) { // Scrape interface if (substr($_SERVER["PATH_INFO"],-7) == '/scrape') { $usehash = false; if (isset($_GET["info_hash"])) { if (get_magic_quotes_gpc()) $info_hash = stripslashes($_GET["info_hash"]); else $info_hash = $_GET["info_hash"]; if (strlen($info_hash) == 20) $info_hash = bin2hex($info_hash); else if (strlen($info_hash) == 40) verifyHash($info_hash) or showError("Invalid info hash value."); else showError("Invalid info hash value."); $usehash = true; } if ($usehash) $query = mysql_query("SELECT info_hash, filename FROM BTPHP_namemap WHERE info_hash=\"$info_hash\""); else $query = mysql_query("SELECT info_hash, filename FROM BTPHP_namemap"); $namemap = array(); while ($row = mysql_fetch_row($query)) $namemap[$row[0]] = $row[1]; if ($usehash) $query = mysql_query("SELECT info_hash, seeds, leechers, finished FROM BTPHP_summary WHERE info_hash=\"$info_hash\"") or showError("Database error. Cannot complete request."); else $query = mysql_query("SELECT info_hash, seeds, leechers, finished FROM BTPHP_summary ORDER BY info_hash") or showError("Database error. Cannot complete request."); echo "d5:filesd"; while ($row = mysql_fetch_row($query)) { $hash = hex2bin($row[0]); echo "20:".$hash."d"; echo "8:completei".$row[1]."e"; echo "10:downloadedi".$row[3]."e"; echo "10:incompletei".$row[2]."e"; if (isset($namemap[$row[0]])) echo "4:name".strlen($namemap[$row[0]]).":".$namemap[$row[0]]; echo "e"; } echo "ee"; exit; } /*if ($_SERVER["PATH_INFO"] != '/announce' && strlen($_SERVER["PATH_INFO"]) > 0) { echo "Tracker.php error: ".$_SERVER["PATH_INFO"]." is unrecognized."; exit; }*/ // Ignore! } // end of isset($_SERVER["PATH_INFO"]) /////////////////////////////////////////////////////////////////// // Handling of parameters from the URL and other setup // Error: no web browsers allowed if (!isset($_GET["info_hash"]) || !isset($_GET["peer_id"])) { header("HTTP/1.0 400 Bad Request"); die("This file is for BitTorrent clients.\n"); } // Many thanks to KktoMx for figuring out this head-ache causer, // and to bideomex for showing me how to do it PROPERLY... :) if (get_magic_quotes_gpc()) { $info_hash = bin2hex(stripslashes($_GET["info_hash"])); $peer_id = bin2hex(stripslashes($_GET["peer_id"])); } else { $info_hash = bin2hex($_GET["info_hash"]); $peer_id = bin2hex($_GET["peer_id"]); } if (!isset($_GET["port"]) || !isset($_GET["downloaded"]) || !isset($_GET["uploaded"]) || !isset($_GET["left"])) showError("Invalid information received from BitTorrent client"); $port = $_GET["port"]; $ip = mysql_escape_string(str_replace("::ffff:", "", $_SERVER["REMOTE_ADDR"])); $downloaded = $_GET["downloaded"]; $uploaded = $_GET["uploaded"]; $left = $_GET["left"]; if (isset($_GET["event"])) $event = $_GET["event"]; else $event = ""; if (!isset($GLOBALS["ip_override"])) $GLOBALS["ip_override"] = true; if (isset($_GET["numwant"])) if ($_GET["numwant"] < $GLOBALS["maxpeers"] && $_GET["numwant"] >= 0) $GLOBALS["maxpeers"]=$_GET["numwant"]; if (isset($_GET["trackerid"])) { if (is_numeric($_GET["trackerid"])) $GLOBALS["trackerid"] = mysql_escape_string($_GET["trackerid"]); } if (!is_numeric($port) || !is_numeric($downloaded) || !is_numeric($uploaded) || !is_numeric($left)) showError("Invalid numerical field(s) from client"); ///////////////////////////////////////////////////// // Checks // Upgrade holdover: check for unset directives if (!isset($GLOBALS["countbytes"])) $GLOBALS["countbytes"] = true; if (!isset($GLOBALS["peercaching"])) $GLOBALS["peercaching"] = false; ///////////////////////////////////////////////////// // Any section of code might need to make a new peer, so this is a function here. // I don't want to put it into funcsv2, even though it should, just for consistency's sake. function start($info_hash, $ip, $port, $peer_id, $left) { if (isset($_SERVER["HTTP_X_FORWARDED_FOR"])) { foreach(explode(",",$_SERVER["HTTP_X_FORWARDED_FOR"]) as $address) { $addr = ip2long(trim($address)); if ($addr != -1) { if ($addr >= -1062731776 && $addr <= -1062666241) { // 192.168.x.x } else if ($addr >= -1442971648 && $addr <= -1442906113) { // 169.254.x.x } else if ($addr >= 167772160 && $addr <= 184549375) { // 10.x.x.x } else if ($addr >= 2130706432 && $addr <= 2147483647) { // 127.0.0.1 } else if ($addr >= -1408237568 && $addr <= -1407188993) { // 172.[16-31].x.x } else { // Finally, we can accept it as a "real" ip address. $ip = mysql_escape_string(trim($address)); break; } } } } if (isset($_GET["ip"]) && $GLOBALS["ip_override"]) { // compact check: valid IP address: if (ip2long($_GET["ip"]) == -1) showError("Invalid IP address. Must be standard dotted decimal (hostnames not allowed)"); $ip = mysql_escape_string($_GET["ip"]); } if ($left == 0) $status = "seeder"; else $status = "leecher"; if (@isFireWalled($info_hash, $peer_id, $ip, $port)) $nat = "'Y'"; else $nat = "'N'"; $results = @mysql_query("INSERT INTO x$info_hash SET peer_id=\"$peer_id\", port=\"$port\", ip=\"$ip\", lastupdate=UNIX_TIMESTAMP(), bytes=\"$left\", status=\"$status\", natuser=$nat"); // Special case: duplicated peer_id. if (!$results) { $error = mysql_error(); if (stristr($error, "key")) { // Duplicate peer_id! Check IP address $peer = getPeerInfo($peer_id, $info_hash); if ($ip == $peer["ip"]) { // Same IP address. Tolerate this error. updatePeer($peer_id, $info_hash); return "WHERE natuser='N'"; } //showError("Duplicated peer_id or changed IP address. Please restart BitTorrent."); // Different IP address. Assume they were disconnected, and alter the IP address. quickQuery("UPDATE x$info_hash SET ip=\"$ip\" WHERE peer_id=\"$peer_id\""); return "WHERE natuser='N'"; } error_log("PHPBTTracker: start: ".$error); showError("Tracker/database error. The details are in the error log."); } $GLOBALS["trackerid"] = mysql_insert_id(); if ($GLOBALS["peercaching"]) { $compact = mysql_escape_string(pack('Nn', ip2long($ip), $port)); $peerid = mysql_escape_string('2:ip' . strlen($ip) . ':' . $ip . '7:peer id20:' . hex2bin($peer_id) . "4:porti{$port}e"); $no_peerid = mysql_escape_string('2:ip' . strlen($ip) . ':' . $ip . "4:porti{$port}e"); mysql_query("INSERT INTO y$info_hash SET sequence=\"{$GLOBALS["trackerid"]}\", compact=\"$compact\", with_peerid=\"$peerid\", without_peerid=\"$no_peerid\""); // Let's just assume success... :/ } if ($left == 0) { summaryAdd("seeds", 1); return "WHERE status=\"leecher\" AND natuser='N'"; } else { summaryAdd("leechers", 1); return "WHERE natuser='N'"; } } /// End of function start //////////////////////////////////////////////////////////////////////////////////////// // Actual work. Depends on value of $event. (Missing event is mapped to '' above) if ($event == '') { verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port); $peer_exists = getPeerInfo($peer_id, $info_hash); $where = "WHERE natuser='N'"; if (!is_array($peer_exists)) $where = start($info_hash, $ip, $port, $peer_id, $left); if ($peer_exists["bytes"] != 0 && $left == 0) { quickQuery("UPDATE x$info_hash SET bytes=0, status=\"seeder\" WHERE sequence=\"${GLOBALS["trackerid"]}"); if (mysql_affected_rows() == 1) { summaryAdd("leechers", -1); summaryAdd("seeds", 1); summaryAdd("finished", 1); } } updatePeer($peer_id, $info_hash); collectBytes($peer_exists, $info_hash, $left); if ($GLOBALS["peercaching"]) sendRandomPeers($info_hash); else { $peers = getRandomPeers($info_hash, ""); sendPeerList($peers); } } else if ($event == "started") { verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port); $start = start($info_hash, $ip, $port, $peer_id, $left); // Don't send the tracker id for newly started clients. Send it next time. Make sure // they get a good random list of peers to begin with. if ($GLOBALS["peercaching"]) sendRandomPeers($info_hash); else { $peers = getRandomPeers($info_hash, ""); sendPeerList($peers); } } else if ($event == "stopped") { verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port); killPeer($peer_id, $info_hash, $left); // I don't know why, but the real tracker returns peers on event=stopped // but I'll just send an empty list. On the other hand, // TheSHADOW asked for this. if (isset($_GET["tracker"])) $peers = getRandomPeers($info_hash); else $peers = array("size" => 0); sendPeerList($peers); } else if ($event == "completed") // now the same as an empty string { verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port); $peer_exists = getPeerInfo($peer_id, $info_hash); if (!is_array($peer_exists)) start($info_hash, $ip, $port, $peer_id, $left); else { quickQuery("UPDATE x$info_hash SET bytes=0, status=\"seeder\" WHERE sequence=\"${GLOBALS["trackerid"]}\""); // Race check if (mysql_affected_rows() == 1) { summaryAdd("leechers", -1); summaryAdd("seeds", 1); summaryAdd("finished", 1); } } updatePeer($peer_id, $info_hash); collectBytes($peer_exists, $info_hash, $left); $peers=getRandomPeers($info_hash); sendPeerList($peers); } else showError("Invalid event= from client."); if ($GLOBALS["countbytes"]) { // Once every minute or so, we run the speed update checker. $query = @mysql_query("SELECT UNIX_TIMESTAMP() - lastSpeedCycle FROM BTPHP_summary WHERE info_hash=\"$info_hash\""); $results = mysql_fetch_row($query); if ($results[0] >= 60) { if (Lock("SPEED:$info_hash")) { @runSpeed($info_hash, $results[0]); Unlock("SPEED:$info_hash"); } } } /* * Under heavy loads, this will lighten the load slightly... very slightly... */ //if (mt_rand(1,10) == 4) trashCollector($info_hash, $report_interval); // Finally, it's time to do stuff to the summary table. if (!empty($summaryupdate)) { $stuff = ""; foreach ($summaryupdate as $column => $value) { $stuff .= ', '.$column. ($value[1] ? "=" : "=$column+") . $value[0]; } mysql_query("UPDATE BTPHP_summary SET ".substr($stuff, 1)." WHERE info_hash=\"$info_hash\""); } // EOF