Question

I have some troubleshoot with µTorrent and I think you can help me.

My µTorrent doesn't receive any data about seeds and leeches

enter image description here

Here is my scrapement code:

<?php
class scrapement extends Core {
        public function renderPage() {
                if (!isset($_GET['info_hash']) || (strlen($_GET['info_hash']) != 20))
                        $this->error('Invalid hash');

                $query = $this->query("SELECT `info_hash`, `seeders`, `leechers`, `times_completed` FROM `torrents` WHERE `info_hash` = '".$this->checkValues($_GET['info_hash'], 0)."'");

                if(!mysql_num_rows($query)) {
                        $this->error('No torrent with that hash found');
                }

                $benc = "d5:files";
                while ($row = $this->fetch($query))
                {
                        $benc .= "d20:".str_pad($row["info_hash"], 20)."d8:completei".$row['seeders']."e10:downloadedi".$row['times_completed']."e10:incompletei".$row['leechers']."eee";
                }
                $benc .= 'ed5:flagsd20:min_request_intervali1800eee';

                $this->getLog($benc);

                header("Content-Type: text/plain");
                header("Pragma: no-cache");
                echo $benc;
        }

        private function error($err) {
                header('Content-Type: text/plain; charset=UTF-8');
                header('Pragma: no-cache');
                exit("d14:failure reason".strlen($err).":{$err}ed5:flagsd20:min_request_intervali1800eeee");
        }
}
?>

The logs from scrapement script: ($this->getLog($benc);)

d5:filesd20:êzo¦G{9…NÑ´ò43d8:completei1e10:downloadedi21e10:incompletei0eeeed5:flagsd20:min_request_intervali1800eee

as µTorrent has not any logs to view I tried with Ratio Master ...and... the scrapement it's working

[02:49:10] GET /scrapement?passkey=fe4d2xxxxxx&info_hash=B%c3%2c%e7%be%ec%2a%5c%a1%c4c%f8%c4M35%3f%f3%c6%e8 HTTP/1.1
Host: 
User-Agent: uTorrent/1800
Accept-Encoding: gzip

enter image description here

so why the uTorrent doesn't receive any data?

Was it helpful?

Solution

You have an error in your bencoded data format. I recommend you use a proper encoding routine to convert data structures to bencoded format, rather than manually constructing the strings by concatenation. Here is a class which handles both encoding and decoding:

/**
 * Encodes and decodes bencode formatted strings
 */
class BEncoder
{
    /**
     * Encode a value using the bencode format
     *
     * @param mixed $data The value to encode
     *
     * @return string The encoded value
     *
     * @throws \InvalidArgumentException When an unencodable value is encountered
     */
    public function encode($data)
    {
        if (is_resource($data)) {
            throw new \InvalidArgumentException('Resources cannot be bencoded');
        }

        // convert objects to arrays of public properties
        // this makes it possible to sort dictionaries as required
        if (is_object($data)) {
            $data = get_object_vars($data);
        }

        if (is_string($data)) {
            // byte sequence
            $result = strlen($data) . ':' . $data;
        } else if (is_int($data) || is_float($data) || is_bool($data)) {
            // integer
            $result = 'i' . round($data, 0) . 'e';
        } else if (is_array($data)) {
            if (array_values($data) === $data) {
                // list
                $result = 'l';
                foreach ($data as $value) {
                    $result .= $this->encode($value);
                }
            } else if (is_array($data)) {
                // dictionary
                ksort($data);
                $result = 'd';
                foreach ($data as $key => $value) {
                    $result .= $this->encode((string) $key) . $this->encode($value);
                }
            }
            $result .= 'e';
        }

        return $result;
    }

    /**
     * Decode a value using the bencode format
     *
     * @param string $data The value to decode
     *
     * @return mixed The decoded value
     *
     * @throws \InvalidArgumentException When an undecodable value is encountered
     */
    public function decode($data)
    {
        if (!is_string($data)) {
            throw new \InvalidArgumentException('Data is not a valid bencoded string');
        }
        $data = trim($data);

        try {
            $result = $this->decodeComponent($data, $position);

            if ($data !== '') {
                throw new \InvalidArgumentException('Data found after end of value');
            }
        } catch (\InvalidArgumentException $e) {
            $err = 'Syntax error at character ' . $position . ' "' . substr($data, 0, 7) . '..."): ' . $e->getMessage();
            throw new \InvalidArgumentException($err);
        }

        return $result;
    }

    /**
     * Move the pointer in the data currently being decoded
     *
     * @param string $data     The data being decoded
     * @param int    $position The position pointer
     * @param int    $count    The number of bytes to move the pointer
     */
    private function movePointer(&$data, &$position, $count)
    {
        $data = (string) substr($data, $count);
        $position += $count;
    }

    /**
     * Recursively decode a structure from a data string
     *
     * @param string $data     The data being decoded
     * @param int    $position The position pointer
     *
     * @return mixed The decoded value
     *
     * @throws \InvalidArgumentException When an undecodable value is encountered
     */
    private function decodeComponent(&$data, &$position = 0)
    {
        switch ($data[0]) {
            case 'i':
                if (!preg_match('/^i(-?\d+)e/', $data, $matches)) {
                    throw new \InvalidArgumentException('Invalid integer');
                }

                $this->movePointer($data, $position, strlen($matches[0]));
                return (int) $matches[1];

            case 'l':
                $this->movePointer($data, $position, 1);
                if ($data === '') {
                    throw new \InvalidArgumentException('Unexpected end of list');
                }

                $result = array();
                while ($data[0] !== 'e') {
                    $value = $this->decodeComponent($data, $position);
                    if ($data === '') {
                        throw new \InvalidArgumentException('Unexpected end of list');
                    }

                    $result[] = $value;
                }

                $this->movePointer($data, $position, 1);
                return $result;

            case 'd':
                $this->movePointer($data, $position, 1);
                if ($data === '') {
                    throw new \InvalidArgumentException('Unexpected end of dictionary');
                }

                $result = array();
                while ($data[0] !== 'e') {
                    $key = $this->decodeComponent($data, $position);
                    if ($data === '') {
                        throw new \InvalidArgumentException('Unexpected end of dictionary');
                    }
                    $value = $this->decodeComponent($data, $position);
                    if ($data === '') {
                        throw new \InvalidArgumentException('Unexpected end of dictionary');
                    }

                    $result[$key] = $value;
                }

                $this->movePointer($data, $position, 1);
                return $result;

            default:
                if (!preg_match('/^(\d+):/', $data, $matches)) {
                    throw new \InvalidArgumentException('Unknown data type');
                }

                $this->movePointer($data, $position, strlen($matches[0]));
                if (strlen($data) < $matches[1]) {
                    $this->movePointer($data, $position, strlen($data));
                    throw new \InvalidArgumentException('Unexpected end of byte string');
                }

                $result = substr($data, 0, $matches[1]);
                $this->movePointer($data, $position, $matches[1]);

                return $result;
        }
    }
}

To use it with your code you would do something like this:

class scrapement extends Core
{
    private $bencoder;

    public function __construct($bencoder)
    {
        // inject an instance of the BEncoder class into this object
        $this->bencoder = $bencoder;
    }

    public function renderPage()
    {
        if (!isset($_GET['info_hash']) || (strlen($_GET['info_hash']) != 20)) {
            $this->error('Invalid hash');
        }

        $query = $this->query("
            SELECT `info_hash`, `seeders`, `leechers`, `times_completed`
            FROM `torrents`
            WHERE `info_hash` = '" . $this->checkValues($_GET['info_hash'], 0) . "'
        ");
        if (!mysql_num_rows($query)) {
            $this->error('No torrent with that hash found');
        }

        $data = array(
            'flags' => array(
                'min_request_interval' => 1800
            ),
            'files' => array()
        );

        while ($row = $this->fetch($query)) {
            $hash = str_pad($row["info_hash"], 20);
            $data['files'][$hash] = array(
                'complete'   => $row['seeders'],
                'incomplete' => $row['leechers'],
                'downloaded' => $row['times_completed']
            );
        }

        $data = $this->bencoder->encode($data);
        $this->getLog($data);

        header("Content-Type: text/plain");
        header("Pragma: no-cache");
        echo $data;
    }

    private function error($err)
    {
        $data = array(
            'flags' => array(
                'min_request_interval' => 1800
            ),
            'failure reason' => $err
        );

        $data = $this->bencoder->encode($data);
        $this->getLog($data);

        header('Content-Type: text/plain; charset=UTF-8');
        header('Pragma: no-cache');
        echo $data;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top