PageRenderTime 27ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/podpress/tracker.php

https://github.com/AJenbo/ubuntudanmark.dk
PHP | 418 lines | 332 code | 48 blank | 38 comment | 75 complexity | 7fe506a0a3b42adc4fc49fae778d107c MD5 | raw file
  1. <?php header("Content-type: text/plain");
  2. header("Pragma: no-cache");
  3. ignore_user_abort(1);
  4. $GLOBALS["peer_id"] = "";
  5. $summaryupdate = array();
  6. require_once("config.php");
  7. require_once("funcsv2.php");
  8. // Hey, want to ban shareaza? Remove slashes on these lines
  9. //if (isset($_SERVER["HTTP_USER_AGENT"]))
  10. // if (stristr($_SERVER["HTTP_USER_AGENT"], "Shareaza") || substr($_SERVER["HTTP_USER_AGENT"], 0, 5) == "RAZA ")
  11. // showError("Shareaza is not allowed on this torrent.");
  12. // Prep database
  13. if ($GLOBALS["persist"])
  14. $db = @mysql_pconnect($dbhost, $dbuser, $dbpass) or showError("Tracker error: can't connect to database. Contact the webmaster.");
  15. else
  16. $db = @mysql_connect($dbhost, $dbuser, $dbpass) or showError("Tracker error: can't connect to database. Contact the webmaster.");
  17. @mysql_select_db($database) or showError("Tracker error: can't open database. Contact the webmaster");
  18. /*
  19. A version of the BitTorrent tracker written in PHP and using
  20. MySQL as a manager. These paragraphs outline my design decisions.
  21. BTTrack uses a whole database which can be shared. Each torrent uses its
  22. own table while a single "summary" table shows overall information
  23. on each individual torrent at a glance. Putting all the torrent
  24. data in one table is easy enought to do (a primary key), but
  25. that would have some speed implications on a server with a lot of
  26. torrents.
  27. Before you begin, you must have a MySQL server configured with
  28. an appropriate user for BitTorrent, a database with permissions, etc.
  29. Create a summary table using the command below, and run
  30. maketorrents.php for each torrent you want the server to handle.
  31. You can use the instal.php script to prepare the database if you
  32. have appropriate database permission.
  33. In the future, I'm planning on writing code to delete torrents.
  34. A consistency checked is available in sanity.php.
  35. */
  36. if (isset ($_SERVER["PATH_INFO"]))
  37. {
  38. // Scrape interface
  39. if (substr($_SERVER["PATH_INFO"],-7) == '/scrape')
  40. {
  41. $usehash = false;
  42. if (isset($_GET["info_hash"]))
  43. {
  44. if (get_magic_quotes_gpc())
  45. $info_hash = stripslashes($_GET["info_hash"]);
  46. else
  47. $info_hash = $_GET["info_hash"];
  48. if (strlen($info_hash) == 20)
  49. $info_hash = bin2hex($info_hash);
  50. else if (strlen($info_hash) == 40)
  51. verifyHash($info_hash) or showError("Invalid info hash value.");
  52. else
  53. showError("Invalid info hash value.");
  54. $usehash = true;
  55. }
  56. if ($usehash)
  57. $query = mysql_query("SELECT info_hash, filename FROM BTPHP_namemap WHERE info_hash=\"$info_hash\"");
  58. else
  59. $query = mysql_query("SELECT info_hash, filename FROM BTPHP_namemap");
  60. $namemap = array();
  61. while ($row = mysql_fetch_row($query))
  62. $namemap[$row[0]] = $row[1];
  63. if ($usehash)
  64. $query = mysql_query("SELECT info_hash, seeds, leechers, finished FROM BTPHP_summary WHERE info_hash=\"$info_hash\"") or showError("Database error. Cannot complete request.");
  65. else
  66. $query = mysql_query("SELECT info_hash, seeds, leechers, finished FROM BTPHP_summary ORDER BY info_hash") or showError("Database error. Cannot complete request.");
  67. echo "d5:filesd";
  68. while ($row = mysql_fetch_row($query))
  69. {
  70. $hash = hex2bin($row[0]);
  71. echo "20:".$hash."d";
  72. echo "8:completei".$row[1]."e";
  73. echo "10:downloadedi".$row[3]."e";
  74. echo "10:incompletei".$row[2]."e";
  75. if (isset($namemap[$row[0]]))
  76. echo "4:name".strlen($namemap[$row[0]]).":".$namemap[$row[0]];
  77. echo "e";
  78. }
  79. echo "ee";
  80. exit;
  81. }
  82. /*if ($_SERVER["PATH_INFO"] != '/announce' && strlen($_SERVER["PATH_INFO"]) > 0)
  83. {
  84. echo "Tracker.php error: ".$_SERVER["PATH_INFO"]." is unrecognized.";
  85. exit;
  86. }*/ // Ignore!
  87. } // end of isset($_SERVER["PATH_INFO"])
  88. ///////////////////////////////////////////////////////////////////
  89. // Handling of parameters from the URL and other setup
  90. // Error: no web browsers allowed
  91. if (!isset($_GET["info_hash"]) || !isset($_GET["peer_id"]))
  92. {
  93. header("HTTP/1.0 400 Bad Request");
  94. die("This file is for BitTorrent clients.\n");
  95. }
  96. // Many thanks to KktoMx for figuring out this head-ache causer,
  97. // and to bideomex for showing me how to do it PROPERLY... :)
  98. if (get_magic_quotes_gpc())
  99. {
  100. $info_hash = bin2hex(stripslashes($_GET["info_hash"]));
  101. $peer_id = bin2hex(stripslashes($_GET["peer_id"]));
  102. }
  103. else
  104. {
  105. $info_hash = bin2hex($_GET["info_hash"]);
  106. $peer_id = bin2hex($_GET["peer_id"]);
  107. }
  108. if (!isset($_GET["port"]) || !isset($_GET["downloaded"]) || !isset($_GET["uploaded"]) || !isset($_GET["left"]))
  109. showError("Invalid information received from BitTorrent client");
  110. $port = $_GET["port"];
  111. $ip = mysql_escape_string(str_replace("::ffff:", "", $_SERVER["REMOTE_ADDR"]));
  112. $downloaded = $_GET["downloaded"];
  113. $uploaded = $_GET["uploaded"];
  114. $left = $_GET["left"];
  115. if (isset($_GET["event"]))
  116. $event = $_GET["event"];
  117. else
  118. $event = "";
  119. if (!isset($GLOBALS["ip_override"]))
  120. $GLOBALS["ip_override"] = true;
  121. if (isset($_GET["numwant"]))
  122. if ($_GET["numwant"] < $GLOBALS["maxpeers"] && $_GET["numwant"] >= 0)
  123. $GLOBALS["maxpeers"]=$_GET["numwant"];
  124. if (isset($_GET["trackerid"]))
  125. {
  126. if (is_numeric($_GET["trackerid"]))
  127. $GLOBALS["trackerid"] = mysql_escape_string($_GET["trackerid"]);
  128. }
  129. if (!is_numeric($port) || !is_numeric($downloaded) || !is_numeric($uploaded) || !is_numeric($left))
  130. showError("Invalid numerical field(s) from client");
  131. /////////////////////////////////////////////////////
  132. // Checks
  133. // Upgrade holdover: check for unset directives
  134. if (!isset($GLOBALS["countbytes"]))
  135. $GLOBALS["countbytes"] = true;
  136. if (!isset($GLOBALS["peercaching"]))
  137. $GLOBALS["peercaching"] = false;
  138. /////////////////////////////////////////////////////
  139. // Any section of code might need to make a new peer, so this is a function here.
  140. // I don't want to put it into funcsv2, even though it should, just for consistency's sake.
  141. function start($info_hash, $ip, $port, $peer_id, $left)
  142. {
  143. if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]))
  144. {
  145. foreach(explode(",",$_SERVER["HTTP_X_FORWARDED_FOR"]) as $address)
  146. {
  147. $addr = ip2long(trim($address));
  148. if ($addr != -1)
  149. {
  150. if ($addr >= -1062731776 && $addr <= -1062666241)
  151. {
  152. // 192.168.x.x
  153. }
  154. else if ($addr >= -1442971648 && $addr <= -1442906113)
  155. {
  156. // 169.254.x.x
  157. }
  158. else if ($addr >= 167772160 && $addr <= 184549375)
  159. {
  160. // 10.x.x.x
  161. }
  162. else if ($addr >= 2130706432 && $addr <= 2147483647)
  163. {
  164. // 127.0.0.1
  165. }
  166. else if ($addr >= -1408237568 && $addr <= -1407188993)
  167. {
  168. // 172.[16-31].x.x
  169. }
  170. else
  171. {
  172. // Finally, we can accept it as a "real" ip address.
  173. $ip = mysql_escape_string(trim($address));
  174. break;
  175. }
  176. }
  177. }
  178. }
  179. if (isset($_GET["ip"]) && $GLOBALS["ip_override"])
  180. {
  181. // compact check: valid IP address:
  182. if (ip2long($_GET["ip"]) == -1)
  183. showError("Invalid IP address. Must be standard dotted decimal (hostnames not allowed)");
  184. $ip = mysql_escape_string($_GET["ip"]);
  185. }
  186. if ($left == 0)
  187. $status = "seeder";
  188. else
  189. $status = "leecher";
  190. if (@isFireWalled($info_hash, $peer_id, $ip, $port))
  191. $nat = "'Y'";
  192. else
  193. $nat = "'N'";
  194. $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");
  195. // Special case: duplicated peer_id.
  196. if (!$results)
  197. {
  198. $error = mysql_error();
  199. if (stristr($error, "key"))
  200. {
  201. // Duplicate peer_id! Check IP address
  202. $peer = getPeerInfo($peer_id, $info_hash);
  203. if ($ip == $peer["ip"])
  204. {
  205. // Same IP address. Tolerate this error.
  206. updatePeer($peer_id, $info_hash);
  207. return "WHERE natuser='N'";
  208. }
  209. //showError("Duplicated peer_id or changed IP address. Please restart BitTorrent.");
  210. // Different IP address. Assume they were disconnected, and alter the IP address.
  211. quickQuery("UPDATE x$info_hash SET ip=\"$ip\" WHERE peer_id=\"$peer_id\"");
  212. return "WHERE natuser='N'";
  213. }
  214. error_log("PHPBTTracker: start: ".$error);
  215. showError("Tracker/database error. The details are in the error log.");
  216. }
  217. $GLOBALS["trackerid"] = mysql_insert_id();
  218. if ($GLOBALS["peercaching"])
  219. {
  220. $compact = mysql_escape_string(pack('Nn', ip2long($ip), $port));
  221. $peerid = mysql_escape_string('2:ip' . strlen($ip) . ':' . $ip . '7:peer id20:' . hex2bin($peer_id) . "4:porti{$port}e");
  222. $no_peerid = mysql_escape_string('2:ip' . strlen($ip) . ':' . $ip . "4:porti{$port}e");
  223. mysql_query("INSERT INTO y$info_hash SET sequence=\"{$GLOBALS["trackerid"]}\", compact=\"$compact\", with_peerid=\"$peerid\", without_peerid=\"$no_peerid\"");
  224. // Let's just assume success... :/
  225. }
  226. if ($left == 0)
  227. {
  228. summaryAdd("seeds", 1);
  229. return "WHERE status=\"leecher\" AND natuser='N'";
  230. }
  231. else
  232. {
  233. summaryAdd("leechers", 1);
  234. return "WHERE natuser='N'";
  235. }
  236. }
  237. /// End of function start
  238. ////////////////////////////////////////////////////////////////////////////////////////
  239. // Actual work. Depends on value of $event. (Missing event is mapped to '' above)
  240. if ($event == '')
  241. {
  242. verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port);
  243. $peer_exists = getPeerInfo($peer_id, $info_hash);
  244. $where = "WHERE natuser='N'";
  245. if (!is_array($peer_exists))
  246. $where = start($info_hash, $ip, $port, $peer_id, $left);
  247. if ($peer_exists["bytes"] != 0 && $left == 0)
  248. {
  249. quickQuery("UPDATE x$info_hash SET bytes=0, status=\"seeder\" WHERE sequence=\"${GLOBALS["trackerid"]}");
  250. if (mysql_affected_rows() == 1)
  251. {
  252. summaryAdd("leechers", -1);
  253. summaryAdd("seeds", 1);
  254. summaryAdd("finished", 1);
  255. }
  256. }
  257. updatePeer($peer_id, $info_hash);
  258. collectBytes($peer_exists, $info_hash, $left);
  259. if ($GLOBALS["peercaching"])
  260. sendRandomPeers($info_hash);
  261. else
  262. {
  263. $peers = getRandomPeers($info_hash, "");
  264. sendPeerList($peers);
  265. }
  266. }
  267. else if ($event == "started")
  268. {
  269. verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port);
  270. $start = start($info_hash, $ip, $port, $peer_id, $left);
  271. // Don't send the tracker id for newly started clients. Send it next time. Make sure
  272. // they get a good random list of peers to begin with.
  273. if ($GLOBALS["peercaching"])
  274. sendRandomPeers($info_hash);
  275. else
  276. {
  277. $peers = getRandomPeers($info_hash, "");
  278. sendPeerList($peers);
  279. }
  280. }
  281. else if ($event == "stopped")
  282. {
  283. verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port);
  284. killPeer($peer_id, $info_hash, $left);
  285. // I don't know why, but the real tracker returns peers on event=stopped
  286. // but I'll just send an empty list. On the other hand,
  287. // TheSHADOW asked for this.
  288. if (isset($_GET["tracker"]))
  289. $peers = getRandomPeers($info_hash);
  290. else
  291. $peers = array("size" => 0);
  292. sendPeerList($peers);
  293. }
  294. else if ($event == "completed") // now the same as an empty string
  295. {
  296. verifyTorrent($info_hash) or evilReject($ip, $peer_id,$port);
  297. $peer_exists = getPeerInfo($peer_id, $info_hash);
  298. if (!is_array($peer_exists))
  299. start($info_hash, $ip, $port, $peer_id, $left);
  300. else
  301. {
  302. quickQuery("UPDATE x$info_hash SET bytes=0, status=\"seeder\" WHERE sequence=\"${GLOBALS["trackerid"]}\"");
  303. // Race check
  304. if (mysql_affected_rows() == 1)
  305. {
  306. summaryAdd("leechers", -1);
  307. summaryAdd("seeds", 1);
  308. summaryAdd("finished", 1);
  309. }
  310. }
  311. updatePeer($peer_id, $info_hash);
  312. collectBytes($peer_exists, $info_hash, $left);
  313. $peers=getRandomPeers($info_hash);
  314. sendPeerList($peers);
  315. }
  316. else
  317. showError("Invalid event= from client.");
  318. if ($GLOBALS["countbytes"])
  319. {
  320. // Once every minute or so, we run the speed update checker.
  321. $query = @mysql_query("SELECT UNIX_TIMESTAMP() - lastSpeedCycle FROM BTPHP_summary WHERE info_hash=\"$info_hash\"");
  322. $results = mysql_fetch_row($query);
  323. if ($results[0] >= 60)
  324. {
  325. if (Lock("SPEED:$info_hash"))
  326. {
  327. @runSpeed($info_hash, $results[0]);
  328. Unlock("SPEED:$info_hash");
  329. }
  330. }
  331. }
  332. /*
  333. * Under heavy loads, this will lighten the load slightly... very slightly...
  334. */
  335. //if (mt_rand(1,10) == 4)
  336. trashCollector($info_hash, $report_interval);
  337. // Finally, it's time to do stuff to the summary table.
  338. if (!empty($summaryupdate))
  339. {
  340. $stuff = "";
  341. foreach ($summaryupdate as $column => $value)
  342. {
  343. $stuff .= ', '.$column. ($value[1] ? "=" : "=$column+") . $value[0];
  344. }
  345. mysql_query("UPDATE BTPHP_summary SET ".substr($stuff, 1)." WHERE info_hash=\"$info_hash\"");
  346. }
  347. // EOF