📜 ⬆️ ⬇️

We write the simplest torrent tracker on php

File sharing has always attracted people, for this, in fact, the BitTorrent protocol was invented.

Most torrent trackers are written in PHP, although there are some that are written in C # language, but we will use PHP for your reference.

Let's look at what a tracker is.

')
Programs called bit-torrent clients, such as utorrent, bittorrent, etc.

they send data to the tracker that the tracker processes and decides what to do with them or keep a record of statistics or give peers (addresses of other clients participating in the exchange of information) for exchange.

Data is transmitted by a GET request, to view this data you can install an http-sniffer such as HttpAnalyzerStd.

The analysis shows what, where, how it is transmitted. The approximate type of request will be:

  1. / announce.php?info_hash =% c4N % 8f % cc % f4 % e5lz % af % 7b % b6M % c0 % cez % da % 25 % ac % e7 % 8f & peer_id =- UT2000 - xGv % adq % 1b % b7 % e4 % 29 % a1 % f1 % ad & port = 57911 & uploaded = 0 & downloaded = 0 & left = 2335117312 & corrupt = 0 & key = AEC92B93 & event = started & numwant = 200 & compact = 1 & no_peer_id = 1


From the data received from the client, the announcer will know whether such a torrent is registered on the tracker, and if so, it gives a list of peers for file sharing.

In order for the tracker to function, we need a database such as mysql.

Create a peers table so that we can store data.

  1. CREATE TABLE IF NOT EXISTS `peers` (
  2. `info _ hash` binary ( 20 ) NOT NULL ,
  3. `ip` int ( 11 ) NOT NULL ,
  4. `port` smallint ( 5 ) unsigned NOT NULL ,
  5. `peer _ id` binary ( 20 ) NOT NULL ,
  6. `uploaded` bigint ( 20 ) unsigned NOT NULL default '0' ,
  7. `downloaded` bigint ( 20 ) unsigned NOT NULL default '0' ,
  8. `left` bigint ( 20 ) unsigned NOT NULL default '0' ,
  9. `update _ time` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP ,
  10. `expire _ time` timestamp NOT NULL default '0000-00-00 00:00:00' ,
  11. PRIMARY KEY ( `info _ hash` , ` ip` , `port` )
  12. ) ENGINE = MEMORY DEFAULT CHARSET = cp1251 ;


Next, proceed to PHP

Create a file such as tracker.php, and work with it.

Connect to base:

  1. <? php
  2. $ db_server = 'localhost' ;
  3. $ db_user = 'user base' ;
  4. $ db_pass = 'User Password' ;
  5. $ db_db = 'Name byzy' ;
  6. $ db_table = 'peers' ; // data storage table
  7. $ min_announce_interval = 900 ; // about this variable in the next post
  8. $ max_announce_rate = 500 ; // about this variable in the next post
  9. $ expire_factor = 1.2 ; // about this variable in the next post
  10. $ scrape_factor = 0.5 ; // about this variable in the next post
  11. $ require_announce_protocol = 'standard' ; // about this variable in the next post
  12. ?>


In order for PHP to "understand" and share information with the client, we need a class for working with torrent files.

  1. <?
  2. require_once 'bencoding.php' ;
  3. ?>


We need to create a function that will send a response to the client in case of invalid data:

  1. <? php
  2. function errorexit ( $ reason ) {
  3. exit ( bencode ( array ( 'failure reason' => $ reason ) ) ) ;
  4. }
  5. ?>


And also write a function to convert the ip-address
  1. <? php
  2. function resolve_ip ( $ host ) {
  3. $ ip = ip2long ( $ host ) ;
  4. if ( ( $ ip === false ) || ( $ ip == - 1 ) ) {
  5. $ ip = ip2long ( gethostbyname ( $ host ) ) ;
  6. if ( ( $ ip === false ) || ( $ ip == - 1 ) ) {
  7. return false ;
  8. }
  9. }
  10. return $ ip ;
  11. }
  12. ?>


Since the data we have only text will install:

  1. <? php
  2. header ( 'Content-Type: text / plain' ) ;
  3. ?>


Next, we need to check whether all the data has been transferred to us:

  1. <? php
  2. if ( empty ( $ _GET [ 'info_hash' ] ) || empty ( $ _GET [ 'port' ] ) || ! is_numeric ( $ _GET [ 'port' ] ) || empty ( $ _GET [ 'peer_id' ] ) | | ! isset ( $ _GET [ 'uploaded' ] ) || ! is_numeric ( $ _GET [ 'uploaded' ] ) || ! isset ( $ _GET [ 'downloaded' ] ) || ! is_numeric ( $ _GET [ 'downloaded' ] ) || ! isset ( $ _GET [ 'left' ] ) || ! is_numeric ( $ _GET [ 'left' ] ) || ( ! empty ( $ _GET [ 'event' ] ) && ( $ _GET [ 'event' ] ! = 'started' ) && ( $ _GET [ 'event' ] ! = 'completed' ) && ( $ _GET [ 'event' ] ! = 'stopped' ) ) ) {
  3. errorexit ( 'invalid request (see bitconjurer.org/BitTorrent/protocol.html)' ) ;
  4. }
  5. ?>


We also check the standards of the bit torrent protocol:

  1. <? php
  2. if ( $ require_announce_protocol == 'no_peer_id' ) {
  3. if ( empty ( $ _GET [ 'compact' ] ) && empty ( $ _GET [ 'no_peer_id' ] ) ) {
  4. errorexit ( 'standard announces not allowed; use no_peer_id or compact option' ) ;
  5. }
  6. }
  7. else if ( $ require_announce_protocol == 'compact' ) {
  8. if ( empty ( $ _GET [ 'compact' ] ) ) {
  9. errorexit ( 'tracker requires use of compact option' ) ;
  10. }
  11. }
  12. $ ip = resolve_ip ( empty ( $ _GET [ 'ip' ] ) ? $ _SERVER [ 'REMOTE_ADDR' ] : $ _GET [ 'ip' ] ) ;
  13. if ( $ ip === false ) {
  14. errorexit ( "unable to resolve host name $ _GET [ip] " ) ;
  15. }
  16. ?>


We connect to the database, and request the data:

  1. <? php
  2. @ mysql_pconnect ( $ db_server , $ db_user , $ db_pass ) or errorexit ( 'Connection failed' ) ;
  3. @ mysql_select_db ( $ db_db ) or errorexit ( 'Error base' ) ;
  4. $ query = @ mysql_query ( "SELECT COUNT (*) FROM` $ db_table `WHERE` expire_time`> NOW (); " ) or errorexit ( 'database error' ) ;
  5. $ num_peers = mysql_result ( $ query , 0 ) ;
  6. $ query = @ mysql_query ( "SELECT COUNT (*) FROM` $ db_table `WHERE` update_time`> NOW () - INTERVAL 1 MINUTE; " ) or errorexit ( 'database error' ) ;
  7. $ announce_rate = mysql_result ( $ query , 0 ) ;
  8. $ announce_interval = max ( $ num_peers * $ announce_rate / ( $ max_announce_rate * $ max_announce_rate ) * 60 , $ min_announce_interval ) ;
  9. ?>


Check what event (stopped, running):

  1. <? php
  2. if ( ! empty ( $ _GET [ 'event' ] ) && ( $ _GET [ 'event' ] == 'stopped' ) ) {
  3. $ expire_time = 0 ;
  4. }
  5. else {
  6. $ expire_time = $ announce_interval * $ expire_factor ;
  7. }
  8. ?>


We insert / update data in the database

  1. <? php
  2. $ columns = `` info_hash`, `ip`,` port`, `peer_id`,` uploaded`, `downloaded`,` left`, `expire_time` ' ;
  3. $ values = '\' '. mysql_escape_string ($ _ GET [' info_hash ']). ' \ ', ' . $ ip . ',' . $ _GET [ 'port' ] . ', \' '. mysql_escape_string ($ _ GET [' peer_id ']). ' \ ', ' . $ _GET [ 'uploaded' ] . ',' . $ _GET [ 'downloaded' ] . ',' . $ _GET [ 'left' ] . ", NOW () + INTERVAL $ expire_time SECOND" ;
  4. @ mysql_query ( "REPLACE INTO` $ db_table `( $ columns ) VALUES ( $ values );" ) or errorexit ( 'database error' ) ;
  5. ?>


Get a list of peers, with the same info_hash:

  1. <? php
  2. $ peers = array ( ) ;
  3. $ numwant = empty ( $ _GET [ 'numwant' ] ) ? 50 : intval ( $ _GET [ 'numwant' ] ) ;
  4. $ query = @ mysql_query ( "SELECT` ip`, `port`,` peer_id` FROM ` $ db_table` WHERE `info_hash` = '" . mysql_escape_string ( $ _GET [ ' info_hash ' ] ) . "' AND` expire_time`> NOW () ORDER BY RAND () LIMIT $ numwant ; " ) or errorexit ( 'database error' ) ;
  5. if ( ! empty ( $ _REQUEST [ 'compact' ] ) ) {
  6. $ peers = " ;
  7. while ( $ array = mysql_fetch_assoc ( $ query ) ) {
  8. $ peers . = pack ( 'Nn' , $ array [ 'ip' ] , $ array [ 'port' ] ) ;
  9. }
  10. }
  11. else if ( ! empty ( $ _REQUEST [ 'no_peer_id' ] ) ) {
  12. while ( $ array = mysql_fetch_assoc ( $ query ) ) {
  13. $ peers [ ] = array ( 'ip' => long2ip ( $ array [ 'ip' ] ) , 'port' => intval ( $ array [ 'port' ] ) ) ;
  14. }
  15. }
  16. else {
  17. while ( $ array = mysql_fetch_assoc ( $ query ) ) {
  18. $ peers [ ] = array ( 'ip' => long2ip ( $ array [ 'ip' ] ) , 'port' => intval ( $ array [ 'port' ] ) , 'peer id' => $ array [ 'peer_id ' ] ) ;
  19. }
  20. }
  21. ?>


Well, finally send the data to the client:

<?php

exit(bencode(array('interval' => intval($announce_interval), 'peers' => $peers)));

?>

Source: https://habr.com/ru/post/96880/


All Articles