=pod

=encoding utf-8

=head1 NAME

Net::BitTorrent::DHT - BitTorrent Mainline DHT implementation

=head1 SYNOPSIS

    use Net::BitTorrent::DHT;
    use Net::BitTorrent::DHT::Security;

    # Initialize security and generate a BEP 42 compliant ID
    my $security = Net::BitTorrent::DHT::Security->new();
    my $node_id  = $security->generate_node_id('12.34.56.78'); # Use your external IP

    # Create the DHT instance
    my $dht = Net::BitTorrent::DHT->new(
        node_id_bin => $node_id,
        port        => 6881,
        v           => 'NB01' # Client version
    );

    # Join the network
    $dht->bootstrap();

    # Main Loop
    while (1) {
        # Process packets with a 100ms timeout
        my ($new_nodes, $found_peers, $data) = $dht->tick(0.1);

        # Handle discovered peers
        for my $peer (@$found_peers) {
            printf "Discovered peer: %s\n", $peer->to_string;
        }

        # Handle BEP 44 / 51 data
        if ($data) {
             # ... handle scrape, get, or sample results
        }

        # Periodic tasks (e.g. search for an infohash)
        # $dht->find_peers($info_hash) if $should_search;
    }

=head1 DESCRIPTION

C<Net::BitTorrent::DHT> is a feature-complete implementation of the BitTorrent Mainline DHT protocol. It supports IPv4,
IPv6 (BEP 32), Security Extensions (BEP 42), Scrapes (BEP 33), Arbitrary Data Storage (BEP 44), and Infohash Indexing
(BEP 51).

=head1 CONSTRUCTOR

=head2 C<new( %args )>

Creates a new DHT node instance.

    my $dht = Net::BitTorrent::DHT->new(
        port    => 6881,
        want_v6 => 1,
        bep44   => 1
    );

=over

=item C<node_id_bin>

A 20-byte binary string representing the local node ID. Defaults to a randomly generated ID if not provided. Note that
if BEP 42 is enabled, this ID may be automatically rotated if a new external IP is detected.

=item C<port>

The UDP port to listen on for DHT traffic. Defaults to C<6881>.

=item C<address>

The local IP address to bind the UDP socket to. Defaults to all available interfaces (C<0.0.0.0> and C<::>).

=item C<want_v4>

Enable or disable support for the IPv4 address family. Defaults to C<1>.

=item C<want_v6>

Enable or disable support for the IPv6 address family. Defaults to C<1>.

=item C<v>

A 4-byte client version string (BEP 20) used to identify the software to other nodes. Identifying strings are
conventionally two characters followed by two version digits (e.g. C<NB01>).

=item C<bep32>

Enable or disable support for IPv6 nodes and peers (BEP 32). Defaults to C<1>.

=item C<bep33>

Enable or disable DHT Scrapes (BEP 33), allowing queries for swarm seeder/leecher counts. Defaults to C<1>.

=item C<bep42>

Enable or disable DHT Security Extensions (BEP 42), which includes node ID validation and automatic ID rotation.
Defaults to C<1>.

=item C<bep44>

Enable or disable Arbitrary Data Storage (BEP 44) for storing and retrieving mutable and immutable data. Defaults to
C<1>.

=item C<bep51>

Enable or disable Infohash Indexing (BEP 51), which allows nodes to sample infohashes stored on the network. Defaults
to C<1>.

=item C<read_only>

If set to C<1>, the node will signal to other peers that it should not be added to their routing tables (BEP 43). This
is useful for low-bandwidth clients or those behind restrictive NATs. Defaults to C<0>.

=item C<boot_nodes>

An array reference of C<[[host, port], ...]> used for the initial bootstrap process. Defaults to a list of standard
public DHT routers including C<router.bittorrent.com> and C<router.utorrent.com>.

=item C<node_id_rotation_interval>

The interval, in seconds, at which the node ID should be automatically rotated if BEP 42 is enabled. Defaults to
C<7200> (2 hours).

=back

=head1 METHODS

=head2 C<bootstrap( )>

Queries bootstrap nodes to join the network.

    $dht->bootstrap( );

=head2 C<tick( [$timeout] )>

The heartbeat of the DHT. Call this in your loop to process I/O. This method also handles automatic node ID rotation if
the rotation interval has elapsed.

    my ( $nodes, $peers, $data ) = $dht->tick( 0.1 );

=head2 C<ping( $addr, $port )>

Sends a C<ping> query.

    $dht->ping( 'router.bittorrent.com', 6881 );

=head2 C<find_node_remote( $target_id, $addr, $port )>

Sends a C<find_node> query.

    $dht->find_node_remote( $target_id, '1.2.3.4', 6881 );

=head2 C<get_peers( $info_hash, $addr, $port )>

Sends a C<get_peers> query.

    $dht->get_peers( $info_hash, '1.2.3.4', 6881 );

=head2 C<announce_infohash( $info_hash, $port )>

Finds nodes closest to the given infohash and announces that the local node is downloading/seeding on the specified
port. This is a high-level method that combines multiple C<get_peers> and C<announce_peer> calls.

    $dht->announce_infohash( $info_hash, 6881 );

=head2 C<find_peers( $info_hash )>

Queries nodes in the local routing table closest to the infohash. Note that this is a single-step (one-hop) lookup. For
a full iterative Kademlia search, see the C<eg/full_search.pl> example in the distribution.

    $dht->find_peers( $info_hash );

=head2 C<scrape( $info_hash )>

Queries closest nodes for swarm statistics (BEP 33). This is also a single-step lookup.

    $dht->scrape( $info_hash );

=head2 C<sample( $target_id )>

Queries closest nodes for infohash samples (BEP 51). Single-step lookup.

    $dht->sample( $target_id );

=head2 C<announce_peer( $info_hash, $token, $announce_port, $addr, $port, [$seed] )>

Announces your presence to a remote node. Requires a token obtained from a previous C<get_peers> call to that node.

    $dht->announce_peer( $hash, $token, 6881, '1.2.3.4', 6881 );

=head2 C<get_remote( $target, $addr, $port )>

Retrieves data (BEP 44) from a specific node.

    $dht->get_remote( $target_hash, '1.2.3.4', 6881 );

=head2 C<put_remote( \%args, $addr, $port )>

Stores data (BEP 44) on a specific node.

    # Immutable
    $dht->put_remote( { v => 'data', token => $t }, '1.2.3.4', 6881 );

    # Mutable
    $dht->put_remote({
        v     => 'new data',
        k     => $pubkey,
        sig   => $signature,
        seq   => 2,
        cas   => 1, # Optional: only update if current seq is 1
        token => $t
    }, '1.2.3.4', 6881);

=head2 C<scrape_peers_remote( $info_hash, $addr, $port )>

Directly queries a specific node for swarm statistics.

    $dht->scrape_peers_remote( $hash, '1.2.3.4', 6881 );

=head2 C<sample_infohashes_remote( $target_id, $addr, $port )>

Directly queries a specific node for infohash samples.

    $dht->sample_infohashes_remote( $target, '1.2.3.4', 6881 );

=head2 C<routing_table_stats( )>

Returns a hash reference containing the count of nodes in each bucket for both IPv4 and IPv6 tables.

    my $stats = $dht->routing_table_stats( );
    printf "Bucket 0 has %d nodes\n", $stats->{v4}[0]{count};

=head2 C<export_state( )>

Returns a hash representation of the current routing table, peer storage, and data storage.

    my $state = $dht->export_state( );

=head2 C<import_state( $state )>

Restores the DHT state from a hash generated by C<export_state()>.

    $dht->import_state( $state );

=head2 C<set_node_id( $new_id )>

Updates the local node ID and refreshes the routing tables.

    $dht->set_node_id( $new_id );

=head2 C<external_ip( )>

Returns the current external IP address string as detected by the network (consensus of 5+ nodes).

    if ( my $ip = $dht->external_ip ) {
        say "Network sees us as: $ip";
    }

=head2 C<on( $event, $cb )>

Registers an event handler.

    $dht->on( external_ip_detected => sub ( $ip ) {
        say "New IP: $ip";
    });

=head2 C<run( )>

Blocking loop for simple standalone usage.

    $dht->run( );

=head2 C<handle_incoming( [$data, $sender] )>

Processes a packet. Can be called with raw data for custom I/O.

    my ( $nodes, $peers, $data ) = $dht->handle_incoming( $raw_data, $sender_sockaddr );

=head1 Event Loop Integration

This module is designed to be protocol-agnostic regarding the event loop.

=head2 Using with IO::Select (Default)

Simply call C<tick( $timeout )> in your own loop.

=head2 Using with IO::Async

    my $handle = IO::Async::Handle->new(
        handle => $dht->socket,
        on_read_ready => sub {
            my ($nodes, $peers) = $dht->handle_incoming( );
            # ...
        },
    );
    $loop->add( $handle );

=head1 Supported BEPs

This module implements the following BitTorrent Enhancement Proposals (BEPs):

=head2 BEP 5: Mainline DHT Protocol

The core protocol implementation. It allows for decentralized peer discovery without a tracker.

=head2 BEP 32: IPv6 Extensions

Adds support for IPv6 nodes and peers. Can be toggled via the C<bep32> constructor argument.

=head2 BEP 33: DHT Scrapes

Allows querying for the number of seeders and leechers for a specific infohash. Can be toggled via the C<bep33>
constructor argument.

=head2 BEP 42: DHT Security Extensions

Implements node ID validation to mitigate specific attacks. Can be toggled via the C<bep42> constructor argument. When
enabled, the node will automatically rotate its C<node_id> if a consensus regarding a new external IP address is
reached.

=head2 BEP 43: Read-only DHT Nodes

Allows the node to participate in the DHT without being added to other nodes' routing tables. Useful for mobile devices
or low-bandwidth clients. Set the C<read_only> constructor argument to a true value.

=head2 BEP 44: Storing Arbitrary Data

Enables C<get> and C<put> operations for storing immutable and mutable data items in the DHT. Can be explicitly
disabled via the C<bep44> constructor argument.

Requests are tracked using internal Transaction IDs (TIDs) to ensure that tokens received from C<get> queries are
correctly matched to their intended targets during subsequent C<put> operations.

The node strictly enforces BEP 44 security requirements:

=over

=item Signatures must be valid and follow the bencoded alphabetical field order.

=item Sequence numbers must strictly increase for updates.

=item CAS (Compare-and-Swap) is supported to prevent race conditions during concurrent updates.

=back

In order to handle mutable data, L<Crypt::PK::Ed25519> or L<Crypt::Perl::Ed25519::PublicKey> must be installed.

=head2 BEP 51: Infohash Indexing

Adds the C<sample_infohashes> RPC to allow indexing of the DHT's content. Supported and enabled by default.

=head1 SECURITY

This module aims to protect the node and the network with these following features:

=over

=item * BEP 42 (Node ID Validation) mitigates Sybil attacks and routing table poisoning.

=item * Peers that attempt to update mutable data with an invalid signature (BEP 44) are automatically blacklisted. All subsequent queries and responses from their IP address will be ignored for the duration of the session.

=back

=head1 SEE ALSO

L<Algorithm::Kademlia>, L<Net::BitTorrent::Protocol::BEP03::Bencode>

L<BEP05|https://www.bittorrent.org/beps/bep_0005.html>, L<BEP20|https://www.bittorrent.org/beps/bep_0020.html>,
L<BEP32|https://www.bittorrent.org/beps/bep_0032.html>, L<BEP33|https://www.bittorrent.org/beps/bep_0033.html>,
L<BEP42|https://www.bittorrent.org/beps/bep_0042.html>, L<BEP43|https://www.bittorrent.org/beps/bep_0043.html>,
L<BEP44|https://www.bittorrent.org/beps/bep_0044.html>, L<BEP51|https://www.bittorrent.org/beps/bep_0051.html>.

=head1 AUTHOR

Sanko Robinson E<lt>sanko@cpan.orgE<gt>

=head1 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.

=cut
