Security Advisories (4)
CVE-2026-57079 (2026-06-30)

Net::BitTorrent versions through 2.0.1 for Perl write files outside the download directory via path traversal in peer-supplied metadata. Net::BitTorrent validates file path components only on the .torrent-file ingest path. The peer and magnet metadata path (_on_metadata_received, reached from the BEP09 ut_metadata extension) passes attacker-supplied file names straight to Storage::add_file and Storage::_parse_file_tree, where Path::Tiny's child() does not collapse "..". A v2 file tree key, a v1 files[].path element, or a single-file name containing ".." segments therefore resolves outside the download directory. Because the peer also controls the piece hashes and the served bytes, content verification passes, so a malicious magnet or peer writes attacker-chosen content to an attacker-chosen path on the downloading host.

CVE-2026-57080 (2026-06-30)

Net::BitTorrent versions through 2.0.1 for Perl allow remote memory exhaustion via an uncapped peer-wire message-length prefix. The peer-wire framing in _process_messages trusts the 4-byte length prefix sent by a connected peer with no upper bound, while receive_data appends every inbound byte to the input buffer. A peer announces a length prefix of up to about 4 GiB and then streams bytes; the decoder waits until the buffer holds the full message before processing it, so the buffer grows without limit. Peer connections are unauthenticated, so any peer in the swarm exhausts the downloading process's memory. The largest legitimate message is a 16 KiB piece block, so any announced length far above that is anomalous.

CVE-2026-57082 (2026-06-30)

Net::BitTorrent versions through 2.0.1 for Perl generate the MSE Diffie-Hellman private key with a non-cryptographic PRNG. The MSE (Message Stream Encryption) handshake derives its 160-bit Diffie-Hellman private key from Perl's rand(), a non-cryptographic drand48-class generator seeded once per process, in KeyExchange.pm. The shared secret and the RC4 keys derived from it (the SHA-1 of "keyA" or "keyB", the shared secret, and the infohash) therefore depend entirely on a predictable PRNG. The same handshake sends, in cleartext, random padding drawn from the same rand() sequence in _random_pad, immediately after the public key and the private-key draw. A passive observer of the handshake recovers the PRNG state from the cleartext padding, reconstructs the private key, computes the shared secret from the peer's public key on the wire, derives the RC4 keys, and decrypts the connection, defeating the passive-observation obfuscation MSE provides.

CVE-2026-57081 (2026-06-30)

Net::BitTorrent versions through 2.0.1 for Perl allow remote memory exhaustion via deeply nested bencoded input. bdecode recurses once per nested list or dictionary level with no depth cap, and each recursive call receives the remaining buffer by value while the list and dictionary branches capture the whole remainder, so every live recursion frame keeps its own copy of the shrinking buffer (O(N^2) bytes for an N-deep input). The decoder runs on every untrusted bencode source: .torrent files, BEP09 metadata fetched from peers, DHT messages, and tracker responses. A bencoded input of roughly 150,000 nested lists (about 150 KB on the wire) drives multi-gigabyte peak memory, so one short message from any peer, or one crafted .torrent file or magnet link, terminates the client.

NAME

Net::BitTorrent::Peer - Individual Peer Connection & State Tracking

SYNOPSIS

use Net::BitTorrent::Types qw[:encryption];

# Usually managed automatically by Net::BitTorrent::Torrent
my $peer = Net::BitTorrent::Peer->new(
    protocol   => $handler,
    torrent    => $torrent,
    transport  => $tcp_transport,
    ip         => '1.2.3.4',
    port       => 6881,
    encryption => ENCRYPTION_REQUIRED
);

# Access transport layer
my $transport = $peer->transport;

# Manual choking control
$peer->unchoke( );

# Check peer state
say 'Peer is interested' if $peer->peer_interested;
say 'Download rate: ' . ($peer->rate_down / 1024) . ' KB/s';

DESCRIPTION

Net::BitTorrent::Peer represents a single connection to a remote BitTorrent client. It maintains the choking/interested state machine and handles all message routing for a specific neighbor.

Reputation & Security

This class implements peer reputation tracking to protect the swarm from malicious or buggy clients:

  • Peers start with a reputation of 100.

  • Providing a verified piece increases reputation by 1.

  • Providing a corrupt piece (failed hash) decreases reputation by 20.

  • If reputation falls to 50 or below, the peer is automatically disconnected and blacklisted for the session.

  • The score is capped only by the number of successful transfers (can exceed 100).

METHODS

new( %params )

Creates a new Peer instance.

my $peer = Net::BitTorrent::Peer->new(
    protocol   => $handler,
    torrent    => $torrent,
    transport  => $tcp_transport,
    ip         => '1.2.3.4',
    port       => 6881,
    encryption => ENCRYPTION_REQUIRED
);

This method initializes a new peer connection manager.

Expected parameters:

protocol

An instance of Net::BitTorrent::Protocol::PeerHandler.

torrent

An instance of Net::BitTorrent::Torrent this peer belongs to.

transport

An instance of Net::BitTorrent::Transport::TCP or Net::uTP::Connection.

ip

The IP address of the peer.

port

The port of the peer.

encryption - optional

Connection security policy (ENCRYPTION_NONE, ENCRYPTION_PREFERRED, ENCRYPTION_REQUIRED). Defaults to ENCRYPTION_PREFERRED.

unchoke( )

Unchokes the peer.

$peer->unchoke();

This method sends an UNCHOKE message to the peer, allowing them to request blocks.

choke( )

Chokes the peer.

$peer->choke();

This method sends a CHOKE message to the peer, preventing them from requesting blocks.

interested( )

Expresses interest in the peer.

$peer->interested();

This method sends an INTERESTED message to the peer.

not_interested( )

Expresses lack of interest in the peer.

$peer->not_interested();

This method sends a NOT_INTERESTED message to the peer.

request( $index, $begin, $len )

Requests a block of data from the peer.

$peer->request( 0, 0, 16384 );

This method sends a REQUEST message for a specific block within a piece.

Expected parameters:

$index

The zero-based piece index.

$begin

The byte offset within the piece.

$len

The number of bytes to request.

send_suggest( $index )

Sends a SUGGEST message (BEP 06).

$peer->send_suggest(42);

This method hints to the peer that a specific piece is available and cheap to download.

Expected parameters:

$index

The piece index to suggest.

send_allowed_fast( $index )

Sends an ALLOWED_FAST message (BEP 06).

$peer->send_allowed_fast(42);

This method informs the peer that they can request a specific piece even if choked.

Expected parameters:

$index

The piece index to allow.

protocol( )

Returns the protocol handler.

my $handler = $peer->protocol();

This method returns the Net::BitTorrent::Protocol object currently managing the connection.

is_encrypted( )

Checks if the connection is encrypted.

say 'Encrypted!' if $peer->is_encrypted;

This method returns a boolean indicating if the connection is using MSE/PE.

is_seeder( )

Checks if the peer is a seeder.

say 'Peer is a seeder' if $peer->is_seeder;

This method returns true if the peer has reported having all pieces.

flags( )

Returns peer status flags.

my $flags = $peer->flags();

This method returns a bitmask of peer status flags (e.g., encrypted, seeder).

set_protocol( $protocol )

Sets the protocol handler.

$peer->set_protocol( $handler );

This method replaces the current protocol handler (e.g., when upgrading from handshake-only to full protocol).

Expected parameters:

$protocol

The new protocol handler object.

set_torrent( $torrent )

Sets the parent torrent.

$peer->set_torrent( $torrent );

This method associates the peer with a Net::BitTorrent::Torrent object.

Expected parameters:

$torrent

The parent torrent object.

on_data( $data )

Handles incoming raw data.

$peer->on_data( $chunk );

This method feeds raw data from the transport layer into the peer's protocol handler.

Expected parameters:

$data

The raw data string.

receive_data( $data )

Processes incoming data.

$peer->receive_data( $data );

Internal alias for on_data which also updates rate limits and stats.

Expected parameters:

$data

The raw data string.

write_buffer( )

Flushes the write buffer to the transport.

$peer->write_buffer();

This method gathers pending messages from the protocol handler and sends them over the transport layer, respecting rate limits.

handle_message( $id, $payload )

Dispatches a BitTorrent message.

$peer->handle_message( 0, '' ); # CHOKE

This method is called by the protocol handler when a full message is received.

Expected parameters:

$id

The numeric message ID.

$payload

The raw message payload.

is_allowed_fast( $index )

Checks if a piece is in the allowed fast set.

say 'Can request even if choked' if $peer->is_allowed_fast( 5 );

This method returns true if the specified piece index was received in an ALLOWED_FAST message.

Expected parameters:

$index

The piece index.

disconnected( )

Handles connection loss.

$peer->disconnected();

This method cleans up state and notifies the parent torrent when the connection is closed.

tick( )

Performs periodic maintenance.

$peer->tick();

This method updates transfer rates, processes transport logic, and flushes outgoing data.

rate_down( )

Returns the download rate.

my $bps = $peer->rate_down;

This method returns the current download speed in bytes per second.

rate_up( )

Returns the upload rate.

my $bps = $peer->rate_up;

This method returns the current upload speed in bytes per second.

reputation( )

Returns the peer's reputation.

my $score = $peer->reputation();

This method returns the current reputation score of the peer.

adjust_reputation( $delta )

Adjusts the peer's reputation.

$peer->adjust_reputation(-20);

This method modifies the peer's reputation score. The connection is closed if the score drops below 50.

Expected parameters:

$delta

The amount to change the reputation by (positive or negative).

torrent( )

Returns the parent torrent object.

my $t = $peer->torrent();

transport( )

Returns the transport object.

my $trans = $peer->transport();

ip( )

Returns the peer's IP address.

my $ip = $peer->ip();

port( )

Returns the peer's port number.

my $port = $peer->port();

am_choking( )

Returns true if we are choking the peer.

if ($peer->am_choking) { ... }

peer_choking( )

Returns true if the peer is choking us.

if ($peer->peer_choking) { ... }

am_interested( )

Returns true if we are interested in the peer.

if ($peer->am_interested) { ... }

peer_interested( )

Returns true if the peer is interested in us.

if ($peer->peer_interested) { ... }

blocks_inflight( )

Returns the number of pending block requests.

my $count = $peer->blocks_inflight();

bitfield_status( )

Returns the bitfield status string ('all', 'none', or raw data).

my $status = $peer->bitfield_status();

set_bitfield_status( $status )

Sets the bitfield status.

$peer->set_bitfield_status('all');

Expected parameters:

$status

The status string.

Specifications

  • BEP 03: Core Peer Wire Protocol

  • BEP 06: Fast Extension (HAVE_ALL, HAVE_NONE, SUGGEST, ALLOWED_FAST)

  • BEP 10: Extension Protocol support

  • BEP 11: PEX (Peer Exchange) data handling

  • BEP 16: Super-seeding support

AUTHOR

Sanko Robinson <sanko@cpan.org>

COPYRIGHT

Copyright (C) 2008-2026 by Sanko Robinson.

This library is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.