org.klomp.snark.dht

## Class KRPC

- java.lang.Object
- - org.klomp.snark.dht.KRPC

- All Implemented Interfaces:
    I2PSessionListener, I2PSessionMuxedListener, DHT

      public class KRPC
      extends Object
      implements I2PSessionMuxedListener, DHT

  Kademlia Remote Procedure Call implementation for BitTorrent DHT (BEP 5) adapted for I2P.

  This class implements the DHT node communication protocol used for decentralized peer discovery in BitTorrent networks. It handles:

  - Kademlia routing table management
  - DHT queries (ping, find_node, get_peers, announce_peer)
  - Token-based peer announcement system
  - Node discovery and maintenance
  - Blacklist management for unreachable nodes

  ## I2P-Specific Modifications:

       - The UDP port need not be pinged after receiving a PORT message.

       - The UDP (datagram) port listed in the compact node info is used
         to receive repliable (signed) datagrams.
         This is used for queries, except for announces.
         We call this the "query port".
         In addition to that UDP port, we use a second datagram
         port equal to the signed port + 1. This is used to receive
         unsigned (raw) datagrams for replies, errors, and announce queries.
         We call this the "response port".

       - Compact peer info is 32 bytes (32 byte SHA256 Hash)
         instead of 4 byte IP + 2 byte port. There is no peer port.

       - Compact node info is 54 bytes (20 byte SHA1 Hash + 32 byte SHA256 Hash + 2 byte port)
         instead of 20 byte SHA1 Hash + 4 byte IP + 2 byte port.
         Port is the query port, the response port is always the query port + 1.

       - The trackerless torrent dictionary "nodes" key is a list of
         32 byte binary strings (SHA256 Hashes) instead of a list of lists
         containing a host string and a port integer.

  This implementation uses I2P's datagram infrastructure for secure, anonymous DHT operations within the I2P network.

- - ### Field Summary

    Fields
    Modifier and Type | Field and Description
    --- | ---
    `static String` | `DHT_FILE_SUFFIX`
    `static NID` | `FAKE_NID` all-zero NID used for pings
    `static boolean` | `SECURE_NID`

  - ### Constructor Summary

    Constructors
    Constructor and Description
    `KRPC(I2PAppContext ctx, String baseName, I2PSession session)`

  - ### Method Summary

    All Methods Instance Methods Concrete Methods
    Modifier and Type | Method and Description
    --- | ---
    `void` | `announce(byte[] ih, boolean isSeed)` Announce to ourselves.
    `void` | `announce(byte[] ih, byte[] peerHash, boolean isSeed)` Announce somebody else we know about to ourselves.
    `int` | `announce(byte[] ih, int max, long maxWait, boolean isSeed)` Not recommended - use getPeersAndAnnounce().
    `void` | `clear()` Clears the tracker and DHT data.
    `void` | `disconnected(I2PSession session)` Notify the client that the session has been terminated.
    `void` | `errorOccurred(I2PSession session, String message, Throwable error)` Notify the client that some error occurred.
    `List<NodeInfo>` | `findClosest(byte[] ih, int max)` Local lookup only
    `Collection<Hash>` | `getPeersAndAnnounce(byte[] ih, int max, long maxWait, int annMax, long annMaxWait, boolean isSeed, boolean noSeeds)` Get peers for a torrent, and announce to the closest annMax nodes we find.
    `int` | `getPort()`
    `int` | `getRPort()`
    `(package private) NodeInfo` | `heardAbout(NodeInfo nInfo)` Called for bootstrap or for all nodes in a receiveNodes reply.
    `void` | `messageAvailable(I2PSession session, int msgId, long size)` for non-muxed
    `void` | `messageAvailable(I2PSession session, int msgId, long size, int proto, int fromPort, int toPort)` Instruct the client that the given session has received a message
    `void` | `ping(Destination dest, int port)` Ping.
    `String` | `renderStatusHTML()` Debug info, HTML formatted
    `void` | `reportAbuse(I2PSession session, int severity)` Instruct the client that the session specified seems to be under attack and that the client may wish to move its destination to another router.
    `int` | `size()` Known nodes, not estimated total network size.
    `void` | `start()` Loads the DHT from file.
    `void` | `stop()` Stop everything.
    `void` | `unannounce(byte[] ih)` Remove reference to ourselves in the local tracker.

    - ### Methods inherited from class java.lang.Object

      `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- - ### Field Detail

    - #### DHT_FILE_SUFFIX

          public static final String DHT_FILE_SUFFIX

      See Also:
        Constant Field Values

    - #### FAKE_NID

          public static final NID FAKE_NID

      all-zero NID used for pings

    - #### SECURE_NID

          public static final boolean SECURE_NID

      See Also:
        Constant Field Values

  - ### Constructor Detail

    - #### KRPC

          public KRPC(I2PAppContext ctx,
                      String baseName,
                      I2PSession session)

      Parameters:
        `baseName` - generally "i2psnark"

  - ### Method Detail

    - #### announce

          public void announce(byte[] ih,
                               boolean isSeed)

      Announce to ourselves. Non-blocking.
      Specified by:
        `announce` in interface `DHT`
      Parameters:
        `ih` - the Info Hash (torrent)

    - #### announce

          public void announce(byte[] ih,
                               byte[] peerHash,
                               boolean isSeed)

      Announce somebody else we know about to ourselves. Non-blocking.
      Specified by:
        `announce` in interface `DHT`
      Parameters:
        `ih` - the Info Hash (torrent)
        `peerHash` - the peer's Hash

    - #### announce

          public int announce(byte[] ih,
                              int max,
                              long maxWait,
                              boolean isSeed)

      Not recommended - use getPeersAndAnnounce().

      Announce to the closest peers in the local DHT. This is NOT iterative - call getPeers() first to get the closest peers into the local DHT. Blocking unless maxWait <= 0 Caller should run in a thread. This also automatically announces ourself to our local tracker. For best results do a getPeersAndAnnounce() instead, as this announces to the closest in the kbuckets, it does NOT sort through the known nodes hashmap.

      Specified by:
        `announce` in interface `DHT`
      Parameters:
        `ih` - the Info Hash (torrent)
        `max` - maximum number of peers to announce to
        `maxWait` - the maximum total time to wait (ms) or 0 to do all in parallel and return immediately.
        `isSeed` - true if seed, false if leech
      Returns:
        the number of successful announces, not counting ourselves.

    - #### clear

          public void clear()

      Clears the tracker and DHT data. Call after saving DHT data to disk.

    - #### disconnected

          public void disconnected(I2PSession session)

      Description copied from interface: `I2PSessionMuxedListener`
      Notify the client that the session has been terminated. All registered listeners will be called.
      Specified by:
        `disconnected` in interface `I2PSessionListener`
      Specified by:
        `disconnected` in interface `I2PSessionMuxedListener`

    - #### errorOccurred

          public void errorOccurred(I2PSession session,
                                    String message,
                                    Throwable error)

      Description copied from interface: `I2PSessionMuxedListener`
      Notify the client that some error occurred. All registered listeners will be called.
      Specified by:
        `errorOccurred` in interface `I2PSessionListener`
      Specified by:
        `errorOccurred` in interface `I2PSessionMuxedListener`
        `error` - can be null? or not?

    - #### findClosest

          public List<NodeInfo> findClosest(byte[] ih,
                                            int max)

      Local lookup only
      Parameters:
        `ih` - a 20-byte info hash
        `max` - max to return
      Returns:
        list or empty list (never null)

    - #### getPeersAndAnnounce

          public Collection<Hash> getPeersAndAnnounce(byte[] ih,
                                                      int max,
                                                      long maxWait,
                                                      int annMax,
                                                      long annMaxWait,
                                                      boolean isSeed,
                                                      boolean noSeeds)

      Get peers for a torrent, and announce to the closest annMax nodes we find. This is an iterative lookup in the DHT. Blocking! Caller should run in a thread.
      Specified by:
        `getPeersAndAnnounce` in interface `DHT`
      Parameters:
        `ih` - the Info Hash (torrent)
        `max` - maximum number of peers to return
        `maxWait` - the maximum time to wait (ms) must be > 0
        `annMax` - the number of peers to announce to
        `annMaxWait` - the maximum total time to wait for announces, may be 0 to return immediately without waiting for acks
        `isSeed` - true if seed, false if leech
        `noSeeds` - true if we do not want seeds in the result
      Returns:
        possibly empty (never null)

    - #### getPort

          public int getPort()

      Specified by:
        `getPort` in interface `DHT`
      Returns:
        The UDP query port

    - #### getRPort

          public int getRPort()

      Specified by:
        `getRPort` in interface `DHT`
      Returns:
        The UDP response port

    - #### heardAbout

          NodeInfo heardAbout(NodeInfo nInfo)

      Called for bootstrap or for all nodes in a receiveNodes reply. Package private for PersistDHT.
      Returns:
        non-null nodeInfo from DB if present, otherwise the nInfo parameter is returned

    - #### messageAvailable

          public void messageAvailable(I2PSession session,
                                       int msgId,
                                       long size)

      for non-muxed
      Specified by:
        `messageAvailable` in interface `I2PSessionListener`
      Specified by:
        `messageAvailable` in interface `I2PSessionMuxedListener`
      Parameters:
        `session` - session to notify
        `msgId` - message number available
        `size` - size of the message - why it's a long and not an int is a mystery

    - #### messageAvailable

          public void messageAvailable(I2PSession session,
                                       int msgId,
                                       long size,
                                       int proto,
                                       int fromPort,
                                       int toPort)

      Instruct the client that the given session has received a message

      Will be called only if you register via addMuxedSessionListener(). Will be called only for the proto(s) and toPort(s) you register for.

      Specified by:
        `messageAvailable` in interface `I2PSessionMuxedListener`
      Parameters:
        `session` - session to notify
        `msgId` - message number available
        `size` - size of the message - why it's a long and not an int is a mystery
        `proto` - 1-254 or 0 for unspecified
        `fromPort` - 1-65535 or 0 for unspecified
        `toPort` - 1-65535 or 0 for unspecified

    - #### ping

          public void ping(Destination dest,
                           int port)

      Ping. We don't have a NID yet so the node is presumed to be absent from our DHT. Non-blocking, does not wait for pong. If and when the pong is received the node will be inserted in our DHT.
      Specified by:
        `ping` in interface `DHT`

    - #### renderStatusHTML

          public String renderStatusHTML()

      Debug info, HTML formatted
      Specified by:
        `renderStatusHTML` in interface `DHT`

    - #### reportAbuse

          public void reportAbuse(I2PSession session,
                                  int severity)

      Description copied from interface: `I2PSessionMuxedListener`
      Instruct the client that the session specified seems to be under attack and that the client may wish to move its destination to another router. All registered listeners will be called. Unused. Not fully implemented.
      Specified by:
        `reportAbuse` in interface `I2PSessionListener`
      Specified by:
        `reportAbuse` in interface `I2PSessionMuxedListener`
      Parameters:
        `session` - session to report abuse to
        `severity` - how bad the abuse is

    - #### size

          public int size()

      Known nodes, not estimated total network size.
      Specified by:
        `size` in interface `DHT`

    - #### start

          public void start()

      Loads the DHT from file. Can't be restarted after stopping?

    - #### stop

          public void stop()

      Stop everything.
      Specified by:
        `stop` in interface `DHT`

    - #### unannounce

          public void unannounce(byte[] ih)

      Remove reference to ourselves in the local tracker. Use when shutting down the torrent locally. Non-blocking.
      Specified by:
        `unannounce` in interface `DHT`
      Parameters:
        `ih` - the Info Hash (torrent)