Created
August 1, 2013 11:31
-
-
Save xboston/6130534 to your computer and use it in GitHub Desktop.
php torrent seed+leeches http udp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class Torrent | |
{ | |
public function scrape(array $announce = [ ] , $hash_info = null) | |
{ | |
$r = [ | |
'seeders' => 0 , | |
'leechers' => 0 | |
]; | |
foreach ( $announce as $an ) { | |
$d = get_remote_peers($an , $hash_info); | |
if ( isset($d['seeders']) ) { | |
$r['seeders'] += $d['seeders']; | |
} | |
if ( isset($d['leechers']) ) { | |
$r['leechers'] += $d['leechers']; | |
} | |
} | |
return $r; | |
} | |
} | |
/** | |
* Gets amout of remote tracker peers. May be recursivity. | |
* | |
* @param string $url Announce url of request | |
* @param string $info_hash Info-hash of torrent to be parsed | |
* @param string $method Method of gathering amount of peers. May be scrape or announce. Default 'scrape'. If scrape fails, recursivety swithes to announce and executes again. | |
* | |
* @return array Result array ('tracker','seeders','leechers','state'); | |
*/ | |
function get_remote_peers($url , $info_hash , $method = 'scrape') | |
{ | |
if ( $method == "announce" ) { | |
$get_params = array( | |
"info_hash" => pack("H*" , $info_hash) , | |
"peer_id" => "-UT1820-5dmPcUOYGnrx" , | |
"port" => rand(10000 , 65535) , | |
"uploaded" => 0 , | |
"no_peer_id" => 1 , | |
"downloaded" => 0 , | |
"compact" => 1 , | |
"left" => 1 , | |
"numwant" => 9999 | |
); | |
} else { | |
$urlorig = $url; | |
$url = str_replace('announce' , 'scrape' , $url); | |
$get_params = array( | |
"info_hash" => pack("H*" , $info_hash) | |
); | |
} | |
$urlInfo = @parse_url($url); | |
$http_host = $urlInfo['host']; | |
$http_port = getUrlPort($urlInfo); | |
$scheme = $urlInfo['scheme']; | |
if ( $http_port === 0 ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:no_port_detected' , 'method' => "$scheme:$method" , 'remote_method' => 'N/A' ); | |
} | |
$http_path = $urlInfo['path']; | |
$get_request_params = @explode('&' , $urlInfo['query']); | |
$new_get_request_params = false; | |
foreach ( array_filter($get_request_params) as $array_value ) { | |
list($key , $value) = explode('=' , $array_value); | |
$new_get_request_params[$key] = $value; | |
} | |
if ( !$new_get_request_params ) { | |
$new_get_request_params = array(); | |
} | |
// Params gathering complete | |
// Creating params | |
$http_params = @http_build_query(@array_merge($new_get_request_params , $get_params)); | |
$opts = array( | |
$scheme => array( | |
'method' => 'GET' , | |
'header' => 'User-Agent: uTorrent/1820' , | |
'timeout' => 3 | |
//'Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2', | |
) | |
); | |
$req_uri = $scheme . '://' . $http_host . ':' . $http_port . $http_path . ($http_params ? '?' . $http_params : ''); | |
if ( $scheme == 'udp' && $method == 'scrape' ) { | |
$transaction_id = mt_rand(0 , 65535); | |
$fp = @fsockopen($scheme . '://' . $http_host , $http_port , $errno , $errstr); //sockets only | |
if ( !$fp ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:timeout' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
stream_set_timeout($fp , 3); | |
$current_connid = "\x00\x00\x04\x17\x27\x10\x19\x80"; | |
//Connection request | |
$packet = $current_connid . pack("N" , 0) . pack("N" , $transaction_id); | |
fwrite($fp , $packet); | |
//Connection response | |
$ret = fread($fp , 16); | |
if ( strlen($ret) < 1 ) { | |
return get_remote_peers($urlorig , $info_hash , "announce"); | |
} | |
if ( strlen($ret) < 16 ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_packet' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
$retd = unpack("Naction/Ntransid" , $ret); | |
if ( $retd['action'] != 0 || $retd['transid'] != $transaction_id ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_response' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
$current_connid = substr($ret , 8 , 8); | |
//Scrape request | |
$packet = $current_connid . pack("N" , 2) . pack("N" , $transaction_id) . pack('H*' , $info_hash); | |
fwrite($fp , $packet); | |
//Scrape response | |
$readlength = 20; //8 + (12); | |
$ret = fread($fp , $readlength); | |
if ( strlen($ret) < 1 ) { | |
return get_remote_peers($urlorig , $info_hash , "announce"); | |
} | |
if ( strlen($ret) < 8 ) { | |
array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_packet' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
$retd = unpack("Naction/Ntransid" , $ret); | |
// Todo check for error string if response = 3 | |
if ( $retd['action'] != 2 || $retd['transid'] != $transaction_id ) { | |
array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_response' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
if ( strlen($ret) < $readlength ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_packet' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
$index = 8; | |
$retd = unpack("Nseeders/Ncompleted/Nleechers" , substr($ret , $index , 12)); | |
return array( 'tracker' => $http_host , 'seeders' => $retd['seeders'] , 'leechers' => $retd['leechers'] , 'state' => 'ok' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} elseif ( $scheme == 'udp' && $method == 'announce' ) { | |
$transaction_id = mt_rand(0 , 65535); | |
$fp = @fsockopen($scheme . '://' . $http_host , $http_port , $errno , $errstr); //sockets only | |
if ( !$fp ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:timeout' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
stream_set_timeout($fp , 3); | |
$current_connid = "\x00\x00\x04\x17\x27\x10\x19\x80"; | |
//Connection request | |
$packet = $current_connid . pack("N" , 0) . pack("N" , $transaction_id); | |
//var_dump($packet); | |
fwrite($fp , $packet); | |
//Connection response | |
$ret = fread($fp , 16); | |
if ( strlen($ret) < 1 ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:no_udp_data' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
if ( strlen($ret) < 16 ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_packet' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
$retd = unpack("Naction/Ntransid" , $ret); | |
if ( $retd['action'] != 0 || $retd['transid'] != $transaction_id ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_response' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
$current_connid = substr($ret , 8 , 8); | |
//Announce request | |
$downloaded = "\x30\x30\x30\x30\x30\x30\x30\x30"; | |
$left = $downloaded; | |
$uploaded = $downloaded; | |
$packet = $current_connid . pack("N" , 1) . pack("N" , $transaction_id) . pack('H*' , $info_hash) . pack( | |
'H*' , | |
'ee3eb1acec1dc7adc73eda16d05a495bea1ddab1' | |
) . $downloaded . $left . $uploaded . pack("N" , 2) . pack("N" , ip2long($_SERVER['SERVER_ADDR'])) . pack("N" , 69) . pack("N" , 500) . pack("N" , rand(0 , 65535)); | |
fwrite($fp , $packet); | |
//Announce response | |
$readlength = 20; //8 + (12); | |
$ret = fread($fp , $readlength); | |
if ( strlen($ret) < 1 ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:no_udp_data' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
if ( strlen($ret) < $readlength ) { | |
array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_packet' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
$retd = unpack("Naction/Ntransid" , $ret); | |
// Todo check for error string if response = 3 | |
if ( $retd['action'] != 2 || $retd['transid'] != $transaction_id ) { | |
array( 'tracker' => $http_host , 'state' => 'failed:invalid_udp_response' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} | |
$index = 8; | |
$retd = unpack("Nleechers/Nseeders" , substr($ret , $index , 8)); | |
return array( 'tracker' => $http_host , 'seeders' => $retd['seeders'] , 'leechers' => $retd['leechers'] , 'state' => 'ok' , 'method' => "$scheme:$method" , 'remote_method' => 'socket' ); | |
} elseif ( function_exists('curl_init') ) { | |
if ( $ch = @curl_init() ) { | |
@curl_setopt($ch , CURLOPT_URL , $req_uri); | |
@curl_setopt($ch , CURLOPT_PORT , $http_port); | |
@curl_setopt($ch , CURLOPT_HEADER , false); | |
@curl_setopt($ch , CURLOPT_FOLLOWLOCATION , true); | |
@curl_setopt($ch , CURLOPT_RETURNTRANSFER , true); | |
@curl_setopt($ch , CURLOPT_CONNECTTIMEOUT , 3); | |
@curl_setopt($ch , CURLOPT_BUFFERSIZE , 1000); | |
@curl_setopt($ch , CURLOPT_USERAGENT , 'uTorrent/1820'); | |
$result = @curl_exec($ch); | |
@curl_close($ch); | |
} | |
$remote_method = 'curl'; | |
} elseif ( function_exists('fsockopen') ) { | |
if ( $fp = fsockopen($http_host , preg_replace("#[\D]#i" , "" , $http_port) , $errno , $errstr , 3) ) { | |
$h = "GET " . $http_path . ($http_params ? '?' . $http_params : '') . " HTTP/1.0\r\n"; | |
$h .= "Host: {$http_host}\r\n"; | |
$h .= "Connection: close\r\n"; | |
$h .= "User-Agent: uTorrent/1820\r\n\r\n"; | |
fputs($fp , $h); | |
$buff = ''; | |
while ( !feof($fp) ) { | |
$buff .= fgets($fp , 128); | |
} | |
fclose($fp); | |
if ( $buff ) { | |
$data = explode("\r\n\r\n" , $buff); | |
$result = $data[1]; | |
} | |
} | |
$remote_method = 'socket'; | |
} elseif ( function_exists('file_get_contents') && ini_get('allow_url_fopen') == 1 ) { | |
$context = @stream_context_create($opts); | |
$result = @file_get_contents($req_uri , false , $context); | |
$remote_method = 'file'; | |
} | |
if ( !$result ) { | |
if ( $method == 'scrape' ) { | |
return get_remote_peers($urlorig , $info_hash , "announce"); | |
} else { | |
return array( 'tracker' => $http_host , 'state' => 'failed:no_benc_result_or_timeout' , 'method' => "$scheme:$method" , 'remote_method' => $remote_method ); | |
} | |
} | |
//var_dump($method); | |
$resulttemp = $result; | |
$result = bdec($result); | |
if ( !is_array($result) ) { | |
return array( 'tracker' => $http_host , 'state' => 'failed:unable_to_bdec:' . $resulttemp , 'method' => "$scheme:$method" , 'remote_method' => $remote_method ); | |
} | |
unset($resulttemp); | |
// print('<pre>'); var_dump($result); | |
if ( $method == 'scrape' ) { | |
if ( $result['value']['files']['value'] ) { | |
$peersarray = @array_shift($result['value']['files']['value']); | |
return array( | |
'tracker' => $http_host , | |
'seeders' => $peersarray['value']['complete']['value'] , | |
'leechers' => $peersarray['value']['incomplete']['value'] , | |
'state' => check_fail($result) , | |
'method' => "$scheme:$method" , | |
'remote_method' => $remote_method | |
); | |
} else { | |
return get_remote_peers($urlorig , $info_hash , "announce"); | |
} | |
} | |
if ( $method == 'announce' ) { | |
return array( | |
'tracker' => $http_host , | |
'seeders' => (is_array($result['value']['peers']['value']) ? count($result['value']['peers']['value']) : (strlen($result['value']['peers']['value']) / 6)) , | |
'leechers' => 0 , | |
'state' => check_fail($result) , | |
'method' => "$scheme:$method" , | |
'remote_method' => $remote_method | |
); | |
} | |
} | |
/** | |
* Gets port from adress | |
* | |
* @param string $urlInfo URL to be parsed | |
* | |
* @return int Port | |
*/ | |
function getUrlPort($urlInfo) | |
{ | |
if ( isset($urlInfo['port']) ) { | |
$port = $urlInfo['port']; | |
} else { // no port specified; get default port | |
if ( isset($urlInfo['scheme']) ) { | |
switch ( $urlInfo['scheme'] ) { | |
case 'http': | |
$port = 80; // default for http | |
break; | |
case 'udp': //default for udp | |
$port = 80; | |
break; | |
case 'https': | |
$port = 443; // default for https | |
break; | |
default: | |
$port = 0; // error; unsupported scheme | |
break; | |
} | |
} else { | |
$port = 80; // error; unknown scheme, using default 80 port | |
} | |
} | |
return $port; | |
} | |
/** | |
* Binary decodes a Value | |
* | |
* @param string $s Value to be decoded | |
* | |
* @return array Decoded value | |
*/ | |
function bdec($s) | |
{ | |
if ( preg_match('/^(\d+):/' , $s , $m) ) { | |
$l = $m[1]; | |
$pl = strlen($l) + 1; | |
$v = substr($s , $pl , $l); | |
$ss = substr($s , 0 , $pl + $l); | |
if ( strlen($v) != $l ) { | |
return; | |
} | |
return array( 'type' => "string" , 'value' => $v , 'strlen' => strlen($ss) , 'string' => $ss ); | |
} | |
if ( preg_match('/^i(-?\d+)e/' , $s , $m) ) { | |
$v = $m[1]; | |
$ss = "i" . $v . "e"; | |
if ( $v === "-0" ) { | |
return; | |
} | |
if ( $v[0] == "0" && strlen($v) != 1 ) { | |
return; | |
} | |
return array( 'type' => "integer" , 'value' => $v , 'strlen' => strlen($ss) , 'string' => $ss ); | |
} | |
switch ( $s[0] ) { | |
case "l": | |
return bdec_list($s); | |
case "d": | |
return bdec_dict($s); | |
default: | |
return; | |
} | |
} | |
/** | |
* Binary decodes a dictionary | |
* | |
* @param string $s Dictionary to be decoded | |
* | |
* @return array Decoded dictionary | |
*/ | |
function bdec_dict($s) | |
{ | |
if ( $s[0] != "d" ) { | |
return; | |
} | |
$sl = strlen($s); | |
$i = 1; | |
$v = array(); | |
$ss = "d"; | |
for ( ; ; ) { | |
if ( $i >= $sl ) { | |
return; | |
} | |
if ( $s[$i] == "e" ) { | |
break; | |
} | |
$ret = bdec(substr($s , $i)); | |
if ( !isset($ret) || !is_array($ret) || $ret["type"] != "string" ) { | |
return; | |
} | |
$k = $ret["value"]; | |
$i += $ret["strlen"]; | |
$ss .= $ret["string"]; | |
if ( $i >= $sl ) { | |
return; | |
} | |
$ret = bdec(substr($s , $i)); | |
if ( !isset($ret) || !is_array($ret) ) { | |
return; | |
} | |
$v[$k] = $ret; | |
$i += $ret["strlen"]; | |
$ss .= $ret["string"]; | |
} | |
$ss .= "e"; | |
return array( 'type' => "dictionary" , 'value' => $v , 'strlen' => strlen($ss) , 'string' => $ss ); | |
} | |
/** | |
* Checks that tracker returns failed event. | |
* | |
* @param string $result Bencoded result to be parsed | |
* | |
* @return string String to be used in remote tracker statistics | |
*/ | |
function check_fail($result) | |
{ | |
if ( isset($result['value']['failure reason']['value']) ) { | |
return 'failed:' . $result['value']['failure reason']['value']; | |
} elseif ( is_array($result) ) { | |
return 'failed'; | |
} else { | |
return 'ok'; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment