Problem in debugging custom python libtorrent client - How to add DHT router

45 Views Asked by At

I'm currently trying to build a BitTorrent client using libtorrent in python. As I want to access the client through an API, I took the example client.py from the libtorrent github repo (which is a command line tool) and modified it. My result is as follows:

import libtorrent as lt
import time
import os.path
from loggers import logger

def add_torrent(ses, filename, save_path):
    atp = lt.add_torrent_params()
    ti = lt.torrent_info(filename)

    # resume_file = os.path.join(save_path,
    #                            ti.name() + '.fastresume')
    # try:
    #     # Try to read resume file
    #     atp = lt.read_resume_data(open(resume_file, 'rb').read())
    # except Exception as e:
    #     logger.exception(f"Failed to open resume file '{resume_file}': {str(e)}")

    atp.ti = ti

    atp.save_path = save_path

    atp.storage_mode = lt.storage_mode_t.storage_mode_sparse

    atp.flags |= lt.torrent_flags.duplicate_is_error \
        | lt.torrent_flags.auto_managed \
        | lt.torrent_flags.duplicate_is_error

    ses.async_add_torrent(atp)


def add_suffix(val):
    # Some parsing logic to get the right unit - see client.py, but should not be relevant

settings = {
        'user_agent': 'python-client/' + lt.__version__,
        'listen_interfaces': '127.0.0.5:6881',
        'download_rate_limit': 0,
        'upload_rate_limit': 10*1024*1024,
        'alert_mask': lt.alert.category_t.all_categories,
        'outgoing_interfaces': '',
}

ses = lt.session(settings)

# This throws an error!
#ses.add_dht_router()

torrents = {}
alerts_log = []

torrent_path = "/path/to/torrent/file/ubuntu-23.10.1-desktop-amd64.iso.torrent"
save_path = "/my/save/path/torrents/"
add_torrent(ses, torrent_path, save_path)

alive = True
while alive:
    print(f"DHT enabled: {ses.is_dht_running()}")
    print(f"DHT nodes: {ses.status().dht_nodes}")
    
    for h, t in torrents.items():
        if t.state != lt.torrent_status.seeding:
            print(f"Error state: {t.error}")
            print(f"Torrent status: {t.paused}")
            print(f"Trackers: {h.trackers()}")

    alerts = ses.pop_alerts()
    for a in alerts:
        alerts_log.append(a.message())
       
        if isinstance(a, lt.add_torrent_alert):
            h = a.handle
            h.set_max_connections(60)
            h.set_max_uploads(-1)
            torrents[h] = h.status()

        if isinstance(a, lt.state_update_alert):
            for s in a.status:
                torrents[s.handle] = s

        if isinstance(a, lt.tracker_reply_alert):
            print(f"Number of peers of tracker: {a.num_peers}")

        if len(alerts_log) > 20:
            alerts_log = alerts_log[-20:]

    time.sleep(0.5)

    ses.post_torrent_updates()

ses.pause()
for h, t in torrents.items():
    if not h.is_valid() or not t.has_metadata:
        continue
    h.save_resume_data()


while len(torrents) > 0:
    alerts = ses.pop_alerts()
    for a in alerts:
        if isinstance(a, lt.save_resume_data_alert):
            print(a)
            data = lt.write_resume_data_buf(a.params)
            h = a.handle
            if h in torrents:
                open(
                    os.path.join(save_path,
                                 torrents[h].name + '.fastresume'),
                    'wb').write(data)
                del torrents[h]
        if isinstance(a, lt.save_resume_data_failed_alert):
            h = a.handle
            if h in torrents:
                print('failed to save resume data for ', torrents[h].name)
                del torrents[h]
    time.sleep(0.5)

I followed the troubleshooting guide and ended at the node:

The DHT is probably not working correctly. You might want to add a DHT bootstrap node through session::add_dht_router(), or have a torrent with DHT nodes in it, or peer connections of peers that are part of the DHT network.

My output so far from the script:

DHT enabled: True
DHT nodes: 0
Error state: 
Torrent status: False
Trackers: [{'url': 'https://torrent.ubuntu.com/announce', 'trackerid': '', 'tier': 0, 'fail_limit': 0, 'source': 1, 'verified': False, 'message': '', 'last_error': {'value': 22, 'category': 'system'}, 'next_announce': 1706636536, 'min_announce': 1706636536, 'scrape_incomplete': -1, 'scrape_complete': -1, 'scrape_downloaded': -1, 'fails': 1, 'updating': False, 'start_sent': False, 'complete_sent': False, 'endpoints': [{'local_address': ('127.0.0.5', 6881), 'info_hashes': [{'message': '', 'last_error': {'value': 22, 'category': 'system'}, 'next_announce': 1706636536, 'min_announce': 1706636536, 'scrape_incomplete': -1, 'scrape_complete': -1, 'scrape_downloaded': -1, 'fails': 1, 'updating': False, 'start_sent': False, 'complete_sent': False}, {'message': '', 'last_error': {'value': 0, 'category': 'system'}, 'next_announce': 1706636536, 'min_announce': 1706636536, 'scrape_incomplete': -1, 'scrape_complete': -1, 'scrape_downloaded': -1, 'fails': 0, 'updating': False, 'start_sent': False, 'complete_sent': False}], 'message': '', 'last_error': {'value': 22, 'category': 'system'}, 'next_announce': 1706636536, 'min_announce': 1706636536, 'scrape_incomplete': -1, 'scrape_complete': -1, 'scrape_downloaded': -1, 'fails': 1, 'updating': False, 'start_sent': False, 'complete_sent': False}], 'send_stats': False}, {'url': 'https://ipv6.torrent.ubuntu.com/announce', 'trackerid': '', 'tier': 1, 'fail_limit': 0, 'source': 1, 'verified': False, 'message': '', 'last_error': {'value': 22, 'category': 'system'}, 'next_announce': 1706636536, 'min_announce': 1706636536, 'scrape_incomplete': -1, 'scrape_complete': -1, 'scrape_downloaded': -1, 'fails': 1, 'updating': False, 'start_sent': False, 'complete_sent': False, 'endpoints': [{'local_address': ('127.0.0.5', 6881), 'info_hashes': [{'message': '', 'last_error': {'value': 22, 'category': 'system'}, 'next_announce': 1706636536, 'min_announce': 1706636536, 'scrape_incomplete': -1, 'scrape_complete': -1, 'scrape_downloaded': -1, 'fails': 1, 'updating': False, 'start_sent': False, 'complete_sent': False}, {'message': '', 'last_error': {'value': 0, 'category': 'system'}, 'next_announce': 1706636536, 'min_announce': 1706636536, 'scrape_incomplete': -1, 'scrape_complete': -1, 'scrape_downloaded': -1, 'fails': 0, 'updating': False, 'start_sent': False, 'complete_sent': False}], 'message': '', 'last_error': {'value': 22, 'category': 'system'}, 'next_announce': 1706636536, 'min_announce': 1706636536, 'scrape_incomplete': -1, 'scrape_complete': -1, 'scrape_downloaded': -1, 'fails': 1, 'updating': False, 'start_sent': False, 'complete_sent': False}], 'send_stats': False}]

As you can see, the tracker_reply_alert does not get triggered, thus we don't have any output for "Number of peers of tracker". I then followed the next step and tried calling ses.add_dht_router(). This however throws the following error:

Boost.Python.ArgumentError: Python argument types in
    session.add_dht_router(session)
did not match C++ signature:
    add_dht_router(libtorrent::session {lvalue}, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > router, int port)

As this is the first time I'm working with torrents, I really don't have a clue what else I might try. I would be very glad if somebody with experience could take a look at my code and maybe point me in the right direction. Thank you already in advance!

1

There are 1 best solutions below

0
Arvid On

the trouble-shooting guide appears to be out-of-date.

The way to add dht bootstrap nodes is via the settings pack:

dht_bootstrap_nodes

https://libtorrent.org/reference-Settings.html#dht_bootstrap_nodes