C++ Anwendung statt PHP

05/28/2012 23:36 UND3RW0RLD#1
Hallo,

ich betreibe einen privaten Bittorrent-Tracker und habe leider das Problem, dass ab ca. 20.000 Peers der SQL-Server derart belastet ist, dass die Seite sich totlädt. Ich gehe davon aus, dass die Hauptbelastung von der "announce.php" kommt, weil diese mit den Clients kommuniziert.

Ist es möglich stattdessen eine Anwendung laufen zulassen?

Hier mal die PHP. Falls ihr noch irgendwelche Infos braucht, meldet Euch bitte. :)

PHP Code:
<?

/*
// +--------------------------------------------------------------------------+
// | Project:    NVTracker - NetVision BitTorrent Tracker                     |
// +--------------------------------------------------------------------------+
// | This file is part of NVTracker. NVTracker is based on BTSource,          |
// | originally by RedBeard of TorrentBits, extensively modified by           |
// | Gartenzwerg.                                                             |
// |                                                                          |
// | NVTracker is free software; you can redistribute it and/or modify        |
// | it under the terms of the GNU General Public License as published by     |
// | the Free Software Foundation; either version 2 of the License, or        |
// | (at your option) any later version.                                      |
// |                                                                          |
// | NVTracker is distributed in the hope that it will be useful,             |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of           |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            |
// | GNU General Public License for more details.                             |
// |                                                                          |
// | You should have received a copy of the GNU General Public License        |
// | along with NVTracker; if not, write to the Free Software Foundation,     |
// | Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA            |
// +--------------------------------------------------------------------------+
// | Obige Zeilen dürfen nicht entfernt werden!    Do not remove above lines! |
// +--------------------------------------------------------------------------+
 */

ob_start("ob_gzhandler");

require_once(
"include/bittorrent.php");
require_once(
"include/benc.php");

hit_start();

function 
hex2bin($hexdata) {
  
   for (
$i=0;$i<strlen($hexdata);$i+=2) {
     
$bindata.=chr(hexdec(substr($hexdata,$i,2)));
   }
  
   return 
$bindata;
}

function 
err($msg)
{
    
benc_resp(array("failure reason" => array(type => "string"value => $msg)));
    
hit_end();
    exit();
}

function 
benc_resp($d)
{
    
benc_resp_raw(benc(array(type => "dictionary"value => $d)));
}

function 
benc_resp_raw($x)
{
    
header("Content-Type: text/plain");
    
header("Pragma: no-cache");
    print(
$x);
}

function 
check_ip_limit() {
    global 
$userid;

    
// Check IP limit
    
$res mysql_query("SELECT DISTINCT(ip) AS ip FROM peers WHERE userid=$userid");
    
$count 0;
    
$found FALSE;
    while (
$row mysql_fetch_assoc($res)) {
        
$count++;
        if (
$row["ip"] == $ip) {
            
$found TRUE;
            break;
        }
    }
    
    if (!
$found && $count >= $GLOBALS["MAX_PASSKEY_IPS"])
        
err("Zu viele unterschiedliche IPs fuer diesen Benutzer (max ".$GLOBALS["MAX_PASSKEY_IPS"].")");
}

if (
in_array($_SERVER["HTTP_USER_AGENT"], $GLOBALS["BAN_USERAGENTS"]))
    
err("Du benutzt einen gebannten Client. Bitte lies die FAQ!");

$req "info_hash:peer_id:!ip:port:uploaded:downloaded:left:!event";
if (
$GLOBALS["CLIENT_AUTH"] == CLIENT_AUTH_PASSKEY) {
    if (
$GLOBALS["PASSKEY_SOURCE"] == PASSKEY_USE_PARAM) {
        
$req .= ":passkey";
        
// Using announce.php?passkey={KEY} will lead to an incorrect parameter list,
        // so the first parameter appended by the client gets appended to the passkey parameter.
        // This match will extract the parameter to $_GET
        
if (preg_match("/^([a-f0-9]{16})\\?(.*?)\\=(.*)$/is"$_GET["passkey"], $m)) {
            
$_GET["passkey"] = $m[1];
            
$_GET[$m[2]] = $m[3];
        }
    }
    if (
$GLOBALS["PASSKEY_SOURCE"] == PASSKEY_USE_SUBDOMAIN) {
        
preg_match("/^([a-f0-9]{16})\\./i"$_SERVER["HTTP_HOST"], $m);
        if (
strlen($m[1])==16)
            
$passkey $m[1];
        else
            
err("Fehlender Parameter fuer Announce: passkey");
    }
}

foreach (
explode(":"$req) as $x)
{
    if (
$x[0] == "!")
    {
        
$x substr($x1);
        
$opt 1;
    }
    else
        
$opt 0;
    if (!isset(
$_GET[$x]))
    {
        if (!
$opt)
            
err("Fehlender Parameter fuer Announce: $x");
        continue;
    }
    
$GLOBALS[$x] = unesc($_GET[$x]);
}

foreach (array(
"info_hash","peer_id") as $x)
{
    if (
strlen($GLOBALS[$x]) != 20)
        
err("Ungueltiger Wert fuer $x (" strlen($GLOBALS[$x]) . " - " urlencode($GLOBALS[$x]) . ")");
}

foreach (
$GLOBALS["BAN_PEERIDS"] as $banned_id)
{
    if (
substr($GLOBALS["peer_id"],0,strlen($banned_id)) == $banned_id)
        
err("Du benutzt einen gebannten Client. Bitte lies das FAQ!");
}

$ip getip();
$origip $ip;
$port $port;
$origport $port;
$downloaded $downloaded;
$uploaded $uploaded;
$left $left;

$rsize 50;
foreach(array(
"num want""numwant""num_want") as $k)
{
    if (isset(
$_GET[$k]))
    {
        
$rsize $_GET[$k];
        break;
    }
}

$agent $_SERVER["HTTP_USER_AGENT"];

// Deny access made with a browser...
if (ereg("^Mozilla\\/"$agent) || ereg("^Opera\\/"$agent) || ereg("^Links "$agent) || ereg("^Lynx\\/"$agent))
    
err("Dieser Torrent ist dem Tracker nicht bekannt");

if (!
$port || $port 0xffff)
    
err("Ungueltiges TCP-Port");

if (!isset(
$event))
    
$event "";

$seeder = ($left == 0) ? "yes" "no";

dbconn(false);

hit_count();

$res mysql_query("SELECT id, name, category, banned, activated, seeders + leechers AS numpeers, UNIX_TIMESTAMP(added) AS ts FROM torrents WHERE " hash_where("info_hash"$info_hash));

$torrent mysql_fetch_assoc($res);
if (!
$torrent)
    
err("Dieser Torrent ist dem Tracker nicht bekannt");

if (
$torrent["activated"] != "yes")
    
err("Dieser Torrent ist dem Tracker nicht bekannt");

$torrentid $torrent["id"];

$fields "seeder, peer_id, ip, port, uploaded, downloaded, userid";

$numpeers $torrent["numpeers"];
$limit "";
if (
$numpeers $rsize)
    
$limit "ORDER BY RAND() LIMIT $rsize";
$res mysql_query("SELECT $fields FROM peers WHERE torrent = $torrentid AND connectable = 'yes' $limit");

$resp "d" benc_str("interval") . "i" $GLOBALS["ANNOUNCE_INTERVAL"] . "e" benc_str("peers") . "l";
unset(
$self);
while (
$row mysql_fetch_assoc($res))
{
    
$row["peer_id"] = hash_pad($row["peer_id"]);

    if (
$row["peer_id"] === $peer_id)
    {
        
$userid $row["userid"];
        
$self $row;
        continue;
    }

    
$resp .= "d" .
        
benc_str("ip") . benc_str($row["ip"]) .
        
benc_str("peer id") . benc_str($row["peer_id"]) .
        
benc_str("port") . "i" $row["port"] . "e" .
        
"e";
}

$resp .= "ee";

$selfwhere "torrent = $torrentid AND " hash_where("peer_id"$peer_id);

if (!isset(
$self))
{
    
$res mysql_query("SELECT $fields FROM peers WHERE $selfwhere");
    
$row mysql_fetch_assoc($res);
    if (
$row)
    {
        
$userid $row["userid"];
        
        
$self $row;
    }
}


//// Up/down stats ////////////////////////////////////////////////////////////

if (!isset($self))
{
    if (
$GLOBALS["CLIENT_AUTH"] == CLIENT_AUTH_PASSKEY) {
        
$rz mysql_query("SELECT id, uploaded, downloaded, class, tlimitseeds, tlimitleeches, tlimitall FROM users WHERE passkey=".sqlesc(hex2bin($passkey))." AND enabled = 'yes' ORDER BY last_access DESC LIMIT 1") or err("Tracker error 2");
        if (
$MEMBERSONLY && mysql_num_rows($rz) == 0)
            
err("Ungueltiger PassKey. Lies das FAQ!");
    } else {
        
$rz mysql_query("SELECT id, uploaded, downloaded, class, tlimitseeds, tlimitleeches, tlimitall FROM users WHERE ip=".sqlesc($ip)." AND enabled = 'yes' ORDER BY last_access DESC LIMIT 1") or err("Tracker error 2");
        if (
$MEMBERSONLY && mysql_num_rows($rz) == 0)
            
err("Unbekannte IP. Lies das FAQ!");
    }
    
    
$az mysql_fetch_assoc($rz);
    
$userid $az["id"];

    
// Wartezeit prüfen
    
$wait get_wait_time($az["id"], $torrentidFALSE$left);
    if ((
$left || !$GLOBALS["ONLY_LEECHERS_WAIT"]) && $wait)
        
err("Wartezeit (noch " . ($wait) . "h) - Bitte lies das FAQ!");
    
    
// Torrent-Limit prüfen
    // $az["tlimitall"] < 0 entspricht unlimitiert!
    
if ($az["tlimitall"] >= 0) {
        
$arr mysql_fetch_assoc(mysql_query("SELECT COUNT(*) AS cnt FROM peers WHERE userid=$userid"));
        
$numtorrents $arr["cnt"];
        
$arr mysql_fetch_assoc(mysql_query("SELECT COUNT(*) AS cnt FROM peers WHERE userid=$userid AND seeder='yes'"));
        
$seeds $arr["cnt"];
        
$leeches $numtorrents $seeds;
        
$limit get_torrent_limits($az);

        if (   (
$limit["total"] > 0)
            &&((
$numtorrents >= $limit["total"])
            || (
$left == && $seeds >= $limit["seeds"])
            || (
$left && $leeches >= $limit["leeches"])))
                
err("Maximales Torrent-Limit erreicht ($limit[seeds] Seeds, $limit[leeches] Leeches, $limit[total] Gesamt)");
        
    }
    
    
check_ip_limit();
    
    
// Create traffic log entry if not existent
    
$res mysql_query("SELECT * FROM `traffic` WHERE `userid`=$userid AND `torrentid`=$torrentid");
    if (@
mysql_num_rows($res) == 0)
        
mysql_query("INSERT INTO `traffic` (`userid`,`torrentid`) VALUES ($userid$torrentid)");
}
else
{
    
// We won't check the IP again, since this would disrupt a user's download if he
    // uses another IP to continue browsing on the tracker.
    
if ($GLOBALS["CLIENT_AUTH"] == CLIENT_AUTH_PASSKEY) {
        
$res mysql_query("SELECT passkey,id FROM users WHERE id=$userid AND enabled = 'yes'");
        
$pkrow mysql_fetch_assoc($res);
        
$passkey hex2bin($passkey);
        if (
$passkey != $pkrow["passkey"])
            
err("Ungueltiger PassKey. Lies das FAQ!");
            
        
check_ip_limit();
    }
    
    
$upthis max(0$uploaded $self["uploaded"]);
    
$downthis max(0$downloaded $self["downloaded"]);

    
$arr mysql_fetch_assoc(mysql_query("SELECT UNIX_TIMESTAMP(last_action) AS lastaction FROM peers WHERE $selfwhere"));
    
$interval time() - $arr["lastaction"];
    
    if (
$interval == 0$interval 1;

    
// If speed is higher than 4 MB/sec, the user is apparently cheating.
    // Frustrate him by adding his desired upload to his download ^^
    
if (($upthis $interval) > $GLOBALS["RATIOFAKER_THRESH"]) {
        
// Write mod comment
        
write_modcomment($userid0"announce.php: Ratiofaker-Tool verwendet: ".mksize($upthis)." Upload, Fake Rate: ".mksize($upthis $interval)."/sek");
        
        
// Add upload as download and zero out upload.
        
$downthis += $upthis;
        
$upthis 0;
    }

    if (
$upthis || $downthis 0) {
        
// Update traffic for current torrent
        
if ($self["seeder"] == "yes")
            
mysql_query("UPDATE `traffic` SET `downloaded`=`downloaded`+$downthis, `uploaded`=`uploaded`+$upthis, `uploadtime`=`uploadtime`+$interval WHERE `userid`=$userid AND `torrentid`=$torrentid");
        else
            
mysql_query("UPDATE `traffic` SET `downloaded`=`downloaded`+$downthis, `uploaded`=`uploaded`+$upthis, `downloadtime`=`downloadtime`+$interval,`uploadtime`=`uploadtime`+$interval WHERE `userid`=$userid AND `torrentid`=$torrentid");
        
mysql_query("UPDATE users SET uploaded = uploaded + $upthis, downloaded = downloaded + $downthis WHERE id=$userid") or err("Tracker error 3");
    }
}

// Bad user? If yes, we'll provide him with an invalid peer list :)
$acctdata mysql_fetch_assoc(mysql_query("SELECT baduser FROM accounts WHERE userid=$userid"));
if (
$acctdata["baduser"]==1) {
    
$resarr bdec($resp);
    for (
$I=0$I<count($resarr["value"]["peers"]["value"]); $I++) {
        
$tmpip $resarr["value"]["peers"]["value"][$I]["value"]["ip"]["value"];
        
$resarr["value"]["peers"]["value"][$I]["value"]["port"]["value"] += mt_rand(100,250);
        
        
$rndnum mt_rand(1,254);
        
$resarr["value"]["peers"]["value"][$I]["value"]["ip"]["value"] =
            
preg_replace("/\\.(\\d{1,3})$/"".$rndnum"$tmpip);
        
    }
    
$resp benc($resarr);
    
    
$rndnum mt_rand(1,254);
    
$ip preg_replace("/\\.(\\d{1,3})$/"".$rndnum"$ip);
    
$port += mt_rand(100,250);
    
}


///////////////////////////////////////////////////////////////////////////////

function portblacklisted($port)
{
    
// direct connect
    
if ($port >= 411 && $port <= 413) return true;

    
// bittorrent
    
if ($port >= 6881 && $port <= 6889) return true;

    
// kazaa
    
if ($port == 1214) return true;

    
// gnutella
    
if ($port >= 6346 && $port <= 6347) return true;

    
// emule
    
if ($port == 4662) return true;

    
// winmx
    
if ($port == 6699) return true;

    return 
false;
}

$updateset = array();

if (
$event == "stopped")
{
    if (isset(
$self))
    {
        
mysql_query("DELETE FROM peers WHERE $selfwhere");
        if (
mysql_affected_rows())
        {
            if (
$self["seeder"] == "yes")
                
$updateset[] = "seeders = seeders - 1";
            else
                
$updateset[] = "leechers = leechers - 1";
        }
    }
    
mysql_query("INSERT INTO startstoplog (userid,event,`datetime`,torrent,ip,peerid,useragent) VALUES ($userid,'stop',NOW(),$torrentid,".sqlesc($_SERVER["REMOTE_ADDR"]).",".sqlesc($peer_id).",".sqlesc($agent).")");
    
    
$announcedelay = @mysql_fetch_assoc(@mysql_query("SELECT * FROM `announcedelay` WHERE `peer_id`=".sqlesc($peer_id)));
    if (
is_array($announcedelay)) {
        if (
$announcedelay["first"] && $announcedelay["second"] && $announcedelay["quantity"]) {
            
$duration1 $announcedelay["second"]-$announcedelay["first"];
            
$duration2 time() - $announcedelay["second"];
            if (
$duration1 310 && $duration2 10 && $uploaded $announcedelay["quantity"] == 0) {
                
write_modcomment($userid0"announce.php: Evtl. Ratiomaker 0.5+ benutzt: ".mksize($uploaded)." Upload / ".mksize($downloaded)." Download, Fake Rate: ".mksize($uploaded $duration1)."/sek, Delays: {$duration1}s / {$duration2}s");
            }
        }
    }
    
    
$resp benc_resp(array("failure reason" => array(type => "string"value => "Kein Fehler - Torrent gestoppt.")));
}
else
{
    if (
$event == "completed") {
        
$updateset[] = "times_completed = times_completed + 1";
        
mysql_query("INSERT INTO completed (user_id, torrent_id, torrent_name, torrent_category, complete_time) VALUES ($userid$torrentid, ".sqlesc($torrent["name"]).", ".$torrent["category"].", NOW())");
    }
    
    if (isset(
$self))
    {
        
$announcedelay = @mysql_fetch_assoc(@mysql_query("SELECT * FROM `announcedelay` WHERE `peer_id`=".sqlesc($peer_id)));
        if (
is_array($announcedelay)) {
            if (
$announcedelay["second"] == 0)
                
mysql_query("UPDATE `announcedelay` SET `second`=UNIX_TIMESTAMP(),`quantity`=$uploaded WHERE `peer_id`=".sqlesc($peer_id));
        }
        
        
mysql_query("UPDATE peers SET uploaded = $uploaded, downloaded = $downloaded, to_go = $left, last_action = NOW(), seeder = '$seeder'"
            
. ($seeder == "yes" && $self["seeder"] != $seeder ", finishedat = " time() : "") . " WHERE $selfwhere");
        if (
mysql_affected_rows() && $self["seeder"] != $seeder)
        {
            if (
$seeder == "yes")
            {
                
$updateset[] = "seeders = seeders + 1";
                
$updateset[] = "leechers = leechers - 1";
            }
            else
            {
                
$updateset[] = "seeders = seeders - 1";
                
$updateset[] = "leechers = leechers + 1";
            }
        }
    }
    else
    {
        if (
portblacklisted($origport))
            
err("Der TCP-Port $origport ist nicht erlaubt.");
        else
        {
            
$sockres = @fsockopen($origip$origport$errno$errstr5);
            if (!
$sockres)
                
$connectable "no";
            else
            {
                
$connectable "yes";
                @
fclose($sockres);
            }
        }

        
$ret mysql_query("INSERT INTO peers (connectable, torrent, peer_id, ip, port, uploaded, downloaded, to_go, started, last_action, seeder, userid, agent, uploadoffset, downloadoffset) VALUES ('$connectable', $torrentid, " sqlesc($peer_id) . ", " sqlesc($ip) . ", $port$uploaded$downloaded$left, NOW(), NOW(), '$seeder', $userid, " sqlesc($agent) . ", $uploaded$downloaded)");
        if (
$ret)
        {
            if (
$seeder == "yes")
                
$updateset[] = "seeders = seeders + 1";
            else
                
$updateset[] = "leechers = leechers + 1";
        }
        
mysql_query("INSERT INTO startstoplog (userid,event,`datetime`,torrent,ip,peerid,useragent) VALUES ($userid,'start',NOW(),$torrentid,".sqlesc($_SERVER["REMOTE_ADDR"]).",".sqlesc($peer_id).",".sqlesc($agent).")");
        
mysql_query("INSERT INTO announcedelay (peer_id, first) VALUES (".sqlesc($peer_id).", UNIX_TIMESTAMP())");
        
mysql_query("DELETE FROM announcedelay WHERE `first`<UNIX_TIMESTAMP()-900");
    }
}

if (
$seeder == "yes")
{
    if (
$torrent["banned"] != "yes")
        
$updateset[] = "visible = 'yes'";
    
$updateset[] = "last_action = NOW()";
}

if (
count($updateset))
    
mysql_query("UPDATE torrents SET " join(","$updateset) . " WHERE id = $torrentid");

benc_resp_raw($resp);

hit_end();

?>
05/29/2012 02:43 MrSm!th#2
Also wenn es der SQL Server ist, den du überlastest, dann macht es doch keinen Unterschied, ob du auf diesen nativ oder per PHP zugreifst.
05/29/2012 08:18 UND3RW0RLD#3
Also z.B. Tracker mit XBT-Source können derart viele Peers verwalten, dass ich glaube, dass ein C++ Backend schon einen Vorteil bringt. Bitreactor zum Beispiel verwaltet derzeit rund 8 Mio Peers.