arvidn / libtorrent

an efficient feature complete C++ bittorrent implementation

Home Page:http://libtorrent.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

invalid choke message

FlorianChevassu opened this issue · comments

Please provide the following information

libtorrent version (or branch): v2.0.9

platform/architecture: linux, x64

compiler and compiler version: gcc 11.2.0


We recently upgraded libtorrent from 2.0.8 to 2.0.9 in our solution, and now have a lot of "invalid choke message" errors. These do not seem to have a real impact on the application though, everything is still working as usual.

We tracked the introduction of the error to the following commit, but couldn't understand what was the issue.

Here is a sample application reproducing the issue. The error does not appear on each run, but a few runs should trigger it (I tested it using the following shell script: until ./invalid_choke_message | grep " choke" ; do echo $?; done;).

#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <thread>

#include <libtorrent/alert_types.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/torrent_info.hpp>
#include <libtorrent/session.hpp>

using namespace std::chrono_literals;

auto make_session(int port) {
    auto params = lt::session_params();
    params.settings.set_int(lt::settings_pack::send_buffer_low_watermark,
                            1 * 1024 * 1024);
    params.settings.set_str(lt::settings_pack::listen_interfaces,
                            std::string("0.0.0.0:") + std::to_string(port));
    params.settings.set_int(lt::settings_pack::alert_mask,
                            lt::alert_category::all);
    return std::make_unique<lt::session>(params);
}

auto build_torrent(std::filesystem::path root) {
    lt::file_storage file_storage;
    for (const auto& file :
         std::filesystem::recursive_directory_iterator(root)) {
        if (std::filesystem::is_regular_file(file.path())) {
            auto relative_path =
                    std::filesystem::relative(file.path(), root.parent_path());
            const auto file_size = std::filesystem::file_size(file.path());
            file_storage.add_file(relative_path, file_size);
        }
    }

    lt::create_torrent torrent_creator(file_storage);
    lt::set_piece_hashes(torrent_creator, root.parent_path());

    std::ostringstream data(std::ios_base::binary);
    lt::bencode(std::ostream_iterator<char>(data), torrent_creator.generate());

    return std::make_shared<lt::torrent_info>(data.str().data(),
                                              data.str().size());
}

void process_alerts(std::unique_ptr<lt::session>& ses) {
    std::vector<lt::alert*> alerts;
    ses->pop_alerts(&alerts);

    for (lt::alert const* alert : alerts) {
        std::cout << lt::alert_name(alert->type())
                  << " alert emitted (message: " << alert->message() << ")"
                  << std::endl;
    }
}

int main() {
    std::filesystem::path root_dir =
            std::filesystem::temp_directory_path() / "libtorrent_test";
    // cleanup previous runs
    std::filesystem::remove_all(root_dir);

    // Create sessions
    constexpr auto seed_port = 6881;
    auto seed_session = make_session(seed_port);
    constexpr auto leech_port = seed_port + 1;
    auto leech_session = make_session(leech_port);

    // Create torrent data
    auto torrent_data_path = root_dir / "torrent_data";
    {
        std::filesystem::remove_all(torrent_data_path);
        std::filesystem::create_directories(torrent_data_path);
        std::ofstream{torrent_data_path / "file1.txt"} << "file 1 content";
        std::ofstream{torrent_data_path / "file2.txt"} << "file 2 content";
    }

    // Copy data to seed session path
    auto seed_session_download_path = root_dir / "seed_download_path";
    std::filesystem::remove_all(seed_session_download_path);
    std::filesystem::create_directories(seed_session_download_path);
    std::filesystem::copy(
            torrent_data_path,
            seed_session_download_path / torrent_data_path.filename(),
            std::filesystem::copy_options::recursive);

    // add torrent to seed session
    lt::add_torrent_params torrent_params;
    torrent_params.save_path = seed_session_download_path;
    torrent_params.ti = build_torrent(torrent_data_path);
    auto seed_handle = seed_session->add_torrent(torrent_params);

    // add torrent to leech session
    auto leech_session_download_path = root_dir / "leech_download_path";
    torrent_params.save_path = leech_session_download_path;
    auto leech_torrent_handle = leech_session->add_torrent(torrent_params);

    // Wait for torrent to be downloading on leech
    while (leech_torrent_handle.status().state <
           lt::torrent_status::state_t::downloading) {
        continue;
    };

    // Connect leech to seed
    const lt::tcp::endpoint peer_endpoint{
            lt::address_v4{ntohl(inet_addr("127.0.0.1"))}, seed_port};
    leech_torrent_handle.connect_peer(peer_endpoint);

    // Wait for leech to have finished downloading
    while (!([&]() {
        process_alerts(seed_session);
        process_alerts(leech_session);
        return (leech_torrent_handle.status().state ==
                lt::torrent_status::state_t::finished) ||
               (leech_torrent_handle.status().state ==
                lt::torrent_status::state_t::seeding);
    }())) {
        std::this_thread::sleep_for(200ms);
        continue;
    }

    // The error will appear a bit after the torrent finishes downloading
    int i = 0;
    while (i++ < 5) {
        process_alerts(seed_session);
        process_alerts(leech_session);
        std::this_thread::sleep_for(1s);
    }
}

The errors suggest that other peers send invalid choke messages, right? I wonder if the uTP changes are incorrect, causing uTP connections that send a "choke" and disconnect, end up being truncated.

Yes, that's how we interpreted it.

I do not know if this is relevant, but please note that in our solution, all peers use the same "client" (our software), and there is no connection to the "outside world".