Author: Arvid Norberg, arvid@libtorrent.org
Version: 1.2.0

home

Plugins

libtorrent has a plugin interface for implementing extensions to the protocol. These can be general extensions for transferring metadata or peer exchange extensions, or it could be used to provide a way to customize the protocol to fit a particular (closed) network.

In short, the plugin interface makes it possible to:

  • register extension messages (sent in the extension handshake), see extensions.
  • add data and parse data from the extension handshake.
  • send extension messages and standard bittorrent messages.
  • override or block the handling of standard bittorrent messages.
  • save and restore state via the session state
  • see all alerts that are posted

a word of caution

Writing your own plugin is a very easy way to introduce serious bugs such as dead locks and race conditions. Since a plugin has access to internal structures it is also quite easy to sabotage libtorrent's operation.

All the callbacks are always called from the libtorrent network thread. In case portions of your plugin are called from other threads, typically the main thread, you cannot use any of the member functions on the internal structures in libtorrent, since those require being called from the libtorrent network thread . Furthermore, you also need to synchronize your own shared data within the plugin, to make sure it is not accessed at the same time from the libtorrent thread (through a callback). If you need to send out a message from another thread, it is advised to use an internal queue, and do the actual sending in tick().

Since the plugin interface gives you easy access to internal structures, it is not supported as a stable API. Plugins should be considered specific to a specific version of libtorrent. Although, in practice the internals mostly don't change that dramatically.

plugin-interface

The plugin interface consists of three base classes that the plugin may implement. These are called plugin, torrent_plugin and peer_plugin. They are found in the <libtorrent/extensions.hpp> header.

These plugins are instantiated for each session, torrent and possibly each peer, respectively.

For plugins that only need per torrent state, it is enough to only implement torrent_plugin and pass a constructor function or function object to session::add_extension() or torrent_handle::add_extension() (if the torrent has already been started and you want to hook in the extension at run-time).

The signature of the function is:

std::shared_ptr<torrent_plugin> (*)(torrent_handle const&, void*);

The second argument is the userdata passed to session::add_torrent() or torrent_handle::add_extension().

The function should return a std::shared_ptr<torrent_plugin> which may or may not be 0. If it is a nullptr, the extension is simply ignored for this torrent. If it is a valid pointer (to a class inheriting torrent_plugin), it will be associated with this torrent and callbacks will be made on torrent events.

For more elaborate plugins which require session wide state, you would implement plugin, construct an object (in a std::shared_ptr) and pass it in to session::add_extension().

custom alerts

Since plugins are running within internal libtorrent threads, one convenient way to communicate with the client is to post custom alerts.

The expected interface of any alert, apart from deriving from the alert base class, looks like this:

static const int alert_type = <unique alert ID>;
virtual int type() const { return alert_type; }

virtual std::string message() const;

static const alert_category_t static_category = <bitmask of alert::category_t flags>;
virtual alert_category_t category() const { return static_category; }

virtual char const* what() const { return <string literal of the name of this alert>; }

The alert_type is used for the type-checking in alert_cast. It must not collide with any other alert. The built-in alerts in libtorrent will not use alert type IDs greater than user_alert_id. When defining your own alert, make sure it's greater than this constant.

type() is the run-time equivalence of the alert_type.

The message() virtual function is expected to construct a useful string representation of the alert and the event or data it represents. Something convenient to put in a log file for instance.

clone() is used internally to copy alerts. The suggested implementation of simply allocating a new instance as a copy of *this is all that's expected.

The static category is required for checking whether or not the category for a specific alert is enabled or not, without instantiating the alert. The category virtual function is the run-time equivalence.

The what() virtual function may simply be a string literal of the class name of your alert.

For more information, see the alert section.

plugin

Declared in "libtorrent/extensions.hpp"

this is the base class for a session plugin. One primary feature is that it is notified of all torrents that are added to the session, and can add its own torrent_plugins.

struct plugin
{
   virtual feature_flags_t implemented_features ();
   virtual std::shared_ptr<torrent_plugin> new_torrent (torrent_handle const&, void*);
   virtual void added (session_handle const&);
   virtual bool on_dht_request (string_view /* query */
      , udp::endpoint const& /* source */, bdecode_node const& /* message */
      , entry& /* response */);
   virtual void on_alert (alert const*);
   virtual bool on_unknown_torrent (sha1_hash const& /* info_hash */
      , peer_connection_handle const& /* pc */, add_torrent_params& /* p */);
   virtual void on_tick ();
   virtual uint64_t get_unchoke_priority (peer_connection_handle const& /* peer */);
   virtual void save_state (entry&);
   virtual void load_state (bdecode_node const&);

   static constexpr feature_flags_t optimistic_unchoke_feature = 1_bit;
   static constexpr feature_flags_t tick_feature = 2_bit;
   static constexpr feature_flags_t dht_request_feature = 3_bit;
   static constexpr feature_flags_t alert_feature = 4_bit;
};

implemented_features()

virtual feature_flags_t implemented_features ();

This function is expected to return a bitmask indicating which features this plugin implements. Some callbacks on this object may not be called unless the corresponding feature flag is returned here. Note that callbacks may still be called even if the corresponding feature is not specified in the return value here. See feature_flags_t for possible flags to return.

new_torrent()

virtual std::shared_ptr<torrent_plugin> new_torrent (torrent_handle const&, void*);

this is called by the session every time a new torrent is added. The torrent* points to the internal torrent object created for the new torrent. The void* is the userdata pointer as passed in via add_torrent_params.

If the plugin returns a torrent_plugin instance, it will be added to the new torrent. Otherwise, return an empty shared_ptr to a torrent_plugin (the default).

added()

virtual void added (session_handle const&);

called when plugin is added to a session

on_dht_request()

virtual bool on_dht_request (string_view /* query */
      , udp::endpoint const& /* source */, bdecode_node const& /* message */
      , entry& /* response */);

called when a dht request is received. If your plugin expects this to be called, make sure to include the flag dht_request_feature in the return value from implemented_features().

on_alert()

virtual void on_alert (alert const*);

called when an alert is posted alerts that are filtered are not posted. If your plugin expects this to be called, make sure to include the flag alert_feature in the return value from implemented_features().

on_unknown_torrent()

virtual bool on_unknown_torrent (sha1_hash const& /* info_hash */
      , peer_connection_handle const& /* pc */, add_torrent_params& /* p */);

return true if the add_torrent_params should be added

on_tick()

virtual void on_tick ();

called once per second. If your plugin expects this to be called, make sure to include the flag tick_feature in the return value from implemented_features().

get_unchoke_priority()

virtual uint64_t get_unchoke_priority (peer_connection_handle const& /* peer */);

called when choosing peers to optimistically unchoke. The return value indicates the peer's priority for unchoking. Lower return values correspond to higher priority. Priorities above 2^63-1 are reserved. If your plugin has no priority to assign a peer it should return 2^64-1. If your plugin expects this to be called, make sure to include the flag optimistic_unchoke_feature in the return value from implemented_features(). If multiple plugins implement this function the lowest return value (i.e. the highest priority) is used.

save_state()

virtual void save_state (entry&);

called when saving settings state

load_state()

virtual void load_state (bdecode_node const&);

called when loading settings state

optimistic_unchoke_feature
include this bit if your plugin needs to alter the order of the optimistic unchoke of peers. i.e. have the on_optimistic_unchoke() callback be called.
tick_feature
include this bit if your plugin needs to have on_tick() called
dht_request_feature
include this bit if your plugin needs to have on_dht_request() called
alert_feature
include this bit if your plugin needs to have on_alert() called

torrent_plugin

Declared in "libtorrent/extensions.hpp"

Torrent plugins are associated with a single torrent and have a number of functions called at certain events. Many of its functions have the ability to change or override the default libtorrent behavior.

struct torrent_plugin
{
   virtual std::shared_ptr<peer_plugin> new_connection (peer_connection_handle const&);
   virtual void on_piece_failed (piece_index_t);
   virtual void on_piece_pass (piece_index_t);
   virtual void tick ();
   virtual bool on_resume ();
   virtual bool on_pause ();
   virtual void on_files_checked ();
   virtual void on_state (torrent_status::state_t);
   virtual void on_add_peer (tcp::endpoint const&,
      peer_source_flags_t, add_peer_flags_t);

   static constexpr add_peer_flags_t first_time = 1_bit;
   static constexpr add_peer_flags_t filtered = 2_bit;
};

new_connection()

virtual std::shared_ptr<peer_plugin> new_connection (peer_connection_handle const&);

This function is called each time a new peer is connected to the torrent. You may choose to ignore this by just returning a default constructed shared_ptr (in which case you don't need to override this member function).

If you need an extension to the peer connection (which most plugins do) you are supposed to return an instance of your peer_plugin class. Which in turn will have its hook functions called on event specific to that peer.

The peer_connection_handle will be valid as long as the shared_ptr is being held by the torrent object. So, it is generally a good idea to not keep a shared_ptr to your own peer_plugin. If you want to keep references to it, use weak_ptr.

If this function throws an exception, the connection will be closed.

on_piece_failed() on_piece_pass()

virtual void on_piece_failed (piece_index_t);
virtual void on_piece_pass (piece_index_t);

These hooks are called when a piece passes the hash check or fails the hash check, respectively. The index is the piece index that was downloaded. It is possible to access the list of peers that participated in sending the piece through the torrent and the piece_picker.

tick()

virtual void tick ();

This hook is called approximately once per second. It is a way of making it easy for plugins to do timed events, for sending messages or whatever.

on_resume() on_pause()

virtual bool on_resume ();
virtual bool on_pause ();

These hooks are called when the torrent is paused and resumed respectively. The return value indicates if the event was handled. A return value of true indicates that it was handled, and no other plugin after this one will have this hook function called, and the standard handler will also not be invoked. So, returning true effectively overrides the standard behavior of pause or resume.

Note that if you call pause() or resume() on the torrent from your handler it will recurse back into your handler, so in order to invoke the standard handler, you have to keep your own state on whether you want standard behavior or overridden behavior.

on_files_checked()

virtual void on_files_checked ();

This function is called when the initial files of the torrent have been checked. If there are no files to check, this function is called immediately.

i.e. This function is always called when the torrent is in a state where it can start downloading.

on_state()

virtual void on_state (torrent_status::state_t);

called when the torrent changes state the state is one of torrent_status::state_t enum members

on_add_peer()

virtual void on_add_peer (tcp::endpoint const&,
      peer_source_flags_t, add_peer_flags_t);

called every time a new peer is added to the peer list. This is before the peer is connected to. For flags, see torrent_plugin::flags_t. The source argument refers to the source where we learned about this peer from. It's a bitmask, because many sources may have told us about the same peer. For peer source flags, see peer_info::peer_source_flags.

first_time
this is the first time we see this peer
filtered
this peer was not added because it was filtered by the IP filter

peer_plugin

Declared in "libtorrent/extensions.hpp"

peer plugins are associated with a specific peer. A peer could be both a regular bittorrent peer (bt_peer_connection) or one of the web seed connections (web_peer_connection or http_seed_connection). In order to only attach to certain peers, make your torrent_plugin::new_connection only return a plugin for certain peer connection types

struct peer_plugin
{
   virtual string_view type () const;
   virtual void add_handshake (entry&);
   virtual void on_disconnect (error_code const&);
   virtual void on_connected ();
   virtual bool on_handshake (span<char const>);
   virtual bool on_extension_handshake (bdecode_node const&);
   virtual bool on_bitfield (bitfield const& /*bitfield*/);
   virtual bool on_dont_have (piece_index_t);
   virtual bool on_request (peer_request const&);
   virtual bool on_unchoke ();
   virtual bool on_interested ();
   virtual bool on_have_all ();
   virtual bool on_allowed_fast (piece_index_t);
   virtual bool on_have_none ();
   virtual bool on_choke ();
   virtual bool on_not_interested ();
   virtual bool on_have (piece_index_t);
   virtual bool on_piece (peer_request const& /*piece*/
      , span<char const> /*buf*/);
   virtual bool on_suggest (piece_index_t);
   virtual bool on_reject (peer_request const&);
   virtual bool on_cancel (peer_request const&);
   virtual void sent_unchoke ();
   virtual void sent_payload (int /* bytes */);
   virtual bool can_disconnect (error_code const& /*ec*/);
   virtual bool on_extended (int /*length*/, int /*msg*/,
      span<char const> /*body*/);
   virtual bool on_unknown_message (int /*length*/, int /*msg*/,
      span<char const> /*body*/);
   virtual void on_piece_failed (piece_index_t);
   virtual void on_piece_pass (piece_index_t);
   virtual void tick ();
   virtual bool write_request (peer_request const&);
};

type()

virtual string_view type () const;

This function is expected to return the name of the plugin.

add_handshake()

virtual void add_handshake (entry&);

can add entries to the extension handshake this is not called for web seeds

on_disconnect()

virtual void on_disconnect (error_code const&);

called when the peer is being disconnected.

on_connected()

virtual void on_connected ();

called when the peer is successfully connected. Note that incoming connections will have been connected by the time the peer plugin is attached to it, and won't have this hook called.

on_handshake()

virtual bool on_handshake (span<char const>);

this is called when the initial bittorrent handshake is received. Returning false means that the other end doesn't support this extension and will remove it from the list of plugins. this is not called for web seeds

on_extension_handshake()

virtual bool on_extension_handshake (bdecode_node const&);

called when the extension handshake from the other end is received if this returns false, it means that this extension isn't supported by this peer. It will result in this peer_plugin being removed from the peer_connection and destructed. this is not called for web seeds

on_bitfield() on_have_none() on_unchoke() on_have() on_choke() on_request() on_not_interested() on_interested() on_allowed_fast() on_have_all() on_dont_have()

virtual bool on_bitfield (bitfield const& /*bitfield*/);
virtual bool on_dont_have (piece_index_t);
virtual bool on_request (peer_request const&);
virtual bool on_unchoke ();
virtual bool on_interested ();
virtual bool on_have_all ();
virtual bool on_allowed_fast (piece_index_t);
virtual bool on_have_none ();
virtual bool on_choke ();
virtual bool on_not_interested ();
virtual bool on_have (piece_index_t);

returning true from any of the message handlers indicates that the plugin has handled the message. it will break the plugin chain traversing and not let anyone else handle the message, including the default handler.

on_piece()

virtual bool on_piece (peer_request const& /*piece*/
      , span<char const> /*buf*/);

This function is called when the peer connection is receiving a piece. buf points (non-owning pointer) to the data in an internal immutable disk buffer. The length of the data is specified in the length member of the piece parameter. returns true to indicate that the piece is handled and the rest of the logic should be ignored.

sent_unchoke()

virtual void sent_unchoke ();

called after a choke message has been sent to the peer

sent_payload()

virtual void sent_payload (int /* bytes */);

called after piece data has been sent to the peer this can be used for stats book keeping

can_disconnect()

virtual bool can_disconnect (error_code const& /*ec*/);

called when libtorrent think this peer should be disconnected. if the plugin returns false, the peer will not be disconnected.

on_extended()

virtual bool on_extended (int /*length*/, int /*msg*/,
      span<char const> /*body*/);

called when an extended message is received. If returning true, the message is not processed by any other plugin and if false is returned the next plugin in the chain will receive it to be able to handle it. This is not called for web seeds. thus function may be called more than once per incoming message, but only the last of the calls will the body size equal the length. i.e. Every time another fragment of the message is received, this function will be called, until finally the whole message has been received. The purpose of this is to allow early disconnects for invalid messages and for reporting progress of receiving large messages.

on_unknown_message()

virtual bool on_unknown_message (int /*length*/, int /*msg*/,
      span<char const> /*body*/);

this is not called for web seeds

on_piece_failed() on_piece_pass()

virtual void on_piece_failed (piece_index_t);
virtual void on_piece_pass (piece_index_t);

called when a piece that this peer participated in either fails or passes the hash_check

tick()

virtual void tick ();

called approximately once every second

write_request()

virtual bool write_request (peer_request const&);

called each time a request message is to be sent. If true is returned, the original request message won't be sent and no other plugin will have this function called.

crypto_plugin

Declared in "libtorrent/extensions.hpp"

struct crypto_plugin
{
   virtual void set_outgoing_key (span<char const> key) = 0;
   virtual void set_incoming_key (span<char const> key) = 0;
   encrypt (span<span<char>> /*send_vec*/) = 0;
   virtual std::tuple<int, int, int> decrypt (span<span<char>> /*receive_vec*/) = 0;
};

decrypt()

virtual std::tuple<int, int, int> decrypt (span<span<char>> /*receive_vec*/) = 0;

decrypt the provided buffers. returns is a tuple representing the values (consume, produce, packet_size)

consume is set to the number of bytes which should be trimmed from the head of the buffers, default is 0

produce is set to the number of bytes of payload which are now ready to be sent to the upper layer. default is the number of bytes passed in receive_vec

packet_size is set to the minimum number of bytes which must be read to advance the next step of decryption. default is 0

create_smart_ban_plugin()

Declared in "libtorrent/extensions/smart_ban.hpp"

std::shared_ptr<torrent_plugin> create_smart_ban_plugin (torrent_handle const&, void*);

constructor function for the smart ban extension. The extension keeps track of the data peers have sent us for failing pieces and once the piece completes and passes the hash check bans the peers that turned out to have sent corrupt data. This function can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().

create_ut_metadata_plugin()

Declared in "libtorrent/extensions/ut_metadata.hpp"

std::shared_ptr<torrent_plugin> create_ut_metadata_plugin (torrent_handle const&, void*);

constructor function for the ut_metadata extension. The ut_metadata extension allows peers to request the .torrent file (or more specifically the 'info'-dictionary of the .torrent file) from each other. This is the main building block in making magnet links work. This extension is enabled by default unless explicitly disabled in the session constructor.

This can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().

create_ut_pex_plugin()

Declared in "libtorrent/extensions/ut_pex.hpp"

std::shared_ptr<torrent_plugin> create_ut_pex_plugin (torrent_handle const&, void*);

constructor function for the ut_pex extension. The ut_pex extension allows peers to gossip about their connections, allowing the swarm stay well connected and peers aware of more peers in the swarm. This extension is enabled by default unless explicitly disabled in the session constructor.

This can either be passed in the add_torrent_params::extensions field, or via torrent_handle::add_extension().