BitTorrent: Descarga no arranca

6 minutos de lectura

avatar de usuario
Cómodo

Estoy tratando de implementar un Rastreador de bittorrent en Laravel. Sin embargo, estoy atascado en este momento porque la descarga no comienza. Hay un par que parece estar sembrando y estoy 100% seguro de que es conectable. Pero, cuando ejecuto un segundo cliente en una máquina diferente, la descarga no comienza. Está atascado en “Conectarse a pares” (uTorrent).

Desde el rastreador estoy enviando la siguiente respuesta cuando el cliente hace un anuncio:

d8:intervali1000e12:min intervali300e5:peers18:�ؤ�i�ؑ�XÚJU�6e

En el cliente de descarga tengo los siguientes datos:

ingrese la descripción de la imagen aquí
ingrese la descripción de la imagen aquí

Aquí está mi código de anuncio:

<?php

namespace App\Http\Controllers\Announce;

use App\Helpers\BencodeHelper;
use App\Models\Peer;
use App\Models\PeerTorrent;
use App\Models\Torrent;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Log;

class AnnounceController extends Controller
{
    const __INTERVAL = 1000;
    const __TIMEOUT = 120;
    const __INTERVAL_MIN = 60;
    const __MAX_PPR = 20;

    public function announce(Request $request)
    {
        Log::info($request->fullUrl());
        $status = 200;
        $content = "";
        $passkey = Input::get('passkey');
        $peer_id = Input::get('peer_id');
        $port = Input::get('port');
        $info_hash = Input::get('info_hash');
        $downloaded = Input::get('uploaded') ? intval(Input::get('uploaded')) : 0;
        $uploaded = Input::get('uploaded') ? intval(Input::get('uploaded')) : 0;
        $left = Input::get('left') ? intval(Input::get('left')) : 0;
        $compact = Input::get('compact') ? intval(Input::get('compact')) : 0;
        $no_peer_id = Input::get('no_peer_id') ? intval(Input::get('no_peer_id')) : 0;

        $ipAddress="";
        // Check for X-Forwarded-For headers and use those if found
        if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && ('' !== trim($_SERVER['HTTP_X_FORWARDED_FOR']))) {
            $ipAddress = (trim($_SERVER['HTTP_X_FORWARDED_FOR']));
        } else {
            if (isset($_SERVER['REMOTE_ADDR']) && ('' !== trim($_SERVER['REMOTE_ADDR']))) {
                $ipAddress = (trim($_SERVER['REMOTE_ADDR']));
            }
        }

        $port = $_SERVER['REMOTE_PORT'];
        /*if(!$port || !ctype_digit($port) || intval($port) < 1 || intval($port) > 65535)
        {
            $content = BencodeHelper::track("Invalid client port.");
            $status = 401;
            return (new Response(AnnounceController::track($content), $status))
                ->header('Content-Type', $value);
        }

        if ($port == 999 && substr($peer_id, 0, 10) == '-TO0001-XX') {
            die("d8:completei0e10:incompletei0e8:intervali600e12:min intervali60e5:peersld2:ip12:72.14.194.184:port3:999ed2:ip11:72.14.194.14:port3:999ed2:ip12:72.14.194.654:port3:999eee");
        }*/


        if (!$passkey) {
            $content = BencodeHelper::track("Missing passkey.");
            $status = 401;
            return (new Response(AnnounceController::track($content), $status))
                ->header('Content-Type', $value);
        }


        $torrent = Torrent::getByInfoHash(sha1($info_hash));
        if (!$torrent || $torrent == null) {
            $content = "Torrent not registered with this tracker.";
            $status = 404;
            return (new Response(AnnounceController::track($content), $status))
                ->header('Content-Type', $value);
        }

        $user = User::has('passkeys', '=', $passkey)->get();

        if ($user == null) {
            $content = BencodeHelper::track("Invalid passkey.");
            $status = 401;
            return (new Response(AnnounceController::track($content), $status))
                ->header('Content-Type', $value);
        }

        $peer = Peer::getByHashAndPasskey(bin2hex($peer_id), $passkey);

        if ($peer == null) {
            $peer = Peer::create([
                'hash' => bin2hex($peer_id),
                'user_agent' => $_SERVER['HTTP_USER_AGENT'],
                'ip_address' => $ipAddress,
                'passkey' => $passkey,
                'port' => $port
            ]);
        }

        if (!$info_hash || strlen($info_hash) != 20) {
            $content = BencodeHelper::track("Invalid info_hash.");
            $status = 401;
            return (new Response(AnnounceController::track($content), $status))
                ->header('Content-Type', $value);
        }


        $peer_torrent = PeerTorrent::getByPeerAndTorrent($peer, $torrent);

        if ($peer_torrent == null) {
            $peer_torrent = PeerTorrent::create([
                'peer_id' => $peer->id,
                'torrent_id' => $torrent->id,
                'uploaded' => $uploaded,
                'downloaded' => $downloaded,
                'left' => $left,
                'stopped' => false
            ]);

        } else {
            $peer_torrent->uploaded = $uploaded;
            $peer_torrent->downloaded = $downloaded;
            $peer_torrent->left = $left;
            $peer_torrent->save();
        }

        $seeders = $torrent->getSeedersCount();
        $leechers = $torrent->getLeechersCount();
        $resp = "";
        if ($compact != 1) {
            $resp = "d" . $this->benc_str("interval") . "i" . AnnounceController::__INTERVAL . "e" . $this->benc_str("peers") . "l";
        } else {
            $resp = "d" . $this->benc_str("interval") . "i" . AnnounceController::__INTERVAL . "e" . $this->benc_str("min interval") . "i" . 300 . "e5:" . "peers";
        }

        $peer = array();

        $peer_num = 0;
        foreach ($torrent->getPeersArray() as $row) {
            if ($compact != 1) {
                if ($row["peer_id"] === $peer->hash) {
                    continue;
                }

                $resp .= "d" . $this->benc_str("ip") . $this->benc_str($row['ip']);

                if ($no_peer_id == 0) {
                    $resp .= $this->benc_str("peer id") . $this->benc_str($row["peer_id"]);
                }

                $resp .= $this->benc_str("port") . "i" . $row["port"] . "e" . "e";

            } else {
                $peer_ip = explode('.', $row["ip"]);
                $peer_ip = pack("C*", $peer_ip[0], $peer_ip[1], $peer_ip[2], $peer_ip[3]);
                $peer_port = pack("n*", (int)$row["port"]);
                $time = intval((time() % 7680) / 60);

                if ($left == 0) {
                    $time += 128;
                }

                $time = pack("C", $time);
                $peer[] = $time . $peer_ip . $peer_port;
                $peer_num++;
            }
        }

        if ($compact != 1) {
            $resp .= "ee";
        } else {
            $o = "";
            for ($i = 0; $i < $peer_num; $i++) {
                $o .= substr($peer[$i], 1, 6);
            }
            $resp .= strlen($o) . ':' . $o . 'e';
        }

        $this->benc_resp_raw($resp);
    }

    public function benc_resp($d)
    {
        return $this->benc_resp_raw($this->benc(array('type' => 'dictionary', 'value' => $d)));
    }

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

        if ($_SERVER['HTTP_ACCEPT_ENCODING'] == 'gzip') {
            header("Content-Encoding: gzip");
            echo gzencode($x, 9, FORCE_GZIP);
        } else {
            echo $x;
        }
    }

    function benc($obj)
    {
        if (!is_array($obj) || !isset($obj["type"]) || !isset($obj["value"]))
            return;
        $c = $obj["value"];
        switch ($obj["type"]) {
            case "string":
                return $this->benc_str($c);
            case "integer":
                return $this->benc_int($c);
            case "list":
                return $this->benc_list($c);
            case "dictionary":
                return $this->benc_dict($c);
            default:
                return;
        }
    }

    public function benc_str($s)
    {
        return strlen($s) . ":$s";
    }

    public function benc_int($i)
    {
        return "i" . $i . "e";
    }

    public function benc_list($a)
    {
        $s = "l";
        foreach ($a as $e) {
            $s .= $this->benc($e);
        }
        $s .= "e";
        return $s;
    }

    public function benc_dict($d)
    {
        $s = "d";
        $keys = array_keys($d);
        sort($keys);
        foreach ($keys as $k) {
            $v = $d[$k];
            $s .= $this->benc_str($k);
            $s .= $this->benc($v);
        }
        $s .= "e";
        return $s;
    }


    public function hex2bin($hex)
    {
        $r="";
        for ($i = 0; $i < strlen($hex); $i += 2) {
            $r .= chr(hexdec($hex{$i} . $hex{($i + 1)}));
        }
        return $r;
    }
}

No estoy muy seguro de lo que me estoy perdiendo aquí.

  • Si su cliente obtiene semillas (aparentemente 5) del rastreador y las direcciones parecen correctas, es posible que el rastreador esté funcionando bien. ¿Están las máquinas detrás de la misma IP pública, detrás de un NAT? Sospecharía un problema de cliente como ese en su lugar.

    – Jeremy

    26 de diciembre de 2015 a las 20:47


  • @JeremyBanks: No lo son. La máquina de “siembra” es de una caja de semillas y definitivamente es accesible al público. Las máquinas de sanguijuela están en diferentes hosts en diferentes ubicaciones e IP.

    – Confort

    26 dic 2015 a las 20:50

  • ¿Parece que la respuesta al anuncio tiene algunos problemas de codificación? Es muy poco probable que se esperen los símbolos � allí.

    – Vieja escuela

    29 de diciembre de 2015 a las 10:57

  • @Oldskool: creo que el carácter debería estar allí (debido a la codificación) ya que esa parte del texto proviene de la función ”pack ()” porque se supone que es una lista “compacta” de pares

    – Confort

    1 de enero de 2016 a las 22:09

  • ¿Descargar? Eso sucede en el lado del cliente.

    – Esclavo de pila

    11/07/2016 a las 21:56

avatar de usuario
Christoph Flotador

Tal vez sea porque estableces constantemente

->header('Content-Type', $value);

sin fijarse nunca $value? Entonces, ¿el “Anuncio-Respuesta” está mal formado?

   $port = $_SERVER['REMOTE_PORT'];

Creo que el problema es que el rastreador en lugar de registrar el puerto que envía el par de conexión en la cadena de anuncio, el rastreador registra el puerto remoto desde el que se conecta el par.
Es casi seguro que ese es el puerto incorrecto para usar.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad