java.lang.Object
org.klomp.snark.Snark
All Implemented Interfaces:
CoordinatorListener, ShutdownListener, StorageListener
Direct Known Subclasses:
FetchAndAdd

public class Snarkextends Object implements StorageListener, CoordinatorListener, ShutdownListener
Main Snark program startup class.
  • Field Details

    • PROP_MAX_CONNECTIONS

      public static final String PROP_MAX_CONNECTIONS
      Maximum connections.
      See Also:
    • torrent

      private String torrent
      Most of these used to be public; use accessors below instead.
    • meta

      private MetaInfo meta
      Metadata of the torrent being downloaded.
    • storage

      private Storage storage
      Storage of the downloaded files.
    • coordinator

      private PeerCoordinator coordinator
      Coordinator of the peer connections.
    • acceptor

      private ConnectionAcceptor acceptor
      Acceptor of incoming peer connections.
    • trackerclient

      private TrackerClient trackerclient
      The tracker client.
    • rootDataDir

      private final File rootDataDir
      Root directory of the downloaded data.
    • completeListener

      private final CompleteListener completeListener
      Listener notified when the download completes.
    • stopped

      private volatile boolean stopped
      True once the download has stopped.
    • starting

      private volatile boolean starting
      True while the download is starting up.
    • id

      private final byte[] id
      Our 20-byte peer id for this download.
    • infoHash

      private final byte[] infoHash
      The torrent's info hash.
    • additionalTrackerURL

      private String additionalTrackerURL
      Extra tracker URL from the config, may be null.
    • _dest

      private volatile TorrentDest _dest
      The transient destination, when multi-dest is enabled; null otherwise or on stop.
    • _util

      protected final I2PSnarkUtil _util
      The util.
    • _log

      private final Log _log
      Logger for the download.
    • _peerCoordinatorSet

      private final PeerCoordinatorSet _peerCoordinatorSet
      Shared set of connected peers.
    • trackerProblems

      private volatile String trackerProblems
      Last tracker error message, null if none.
    • lastTrackerResponse

      private volatile long lastTrackerResponse
      When the tracker last replied.
    • trackerSeenPeers

      private volatile int trackerSeenPeers
      Peers reported by the tracker.
    • _scrapeSeeders

      private volatile int _scrapeSeeders
      Seeders from the last scrape.
    • _scrapeLeechers

      private volatile int _scrapeLeechers
      Leechers from the last scrape.
    • _scrapePartials

      private volatile int _scrapePartials
      Partial completions from the last scrape.
    • _autoStoppable

      private volatile boolean _autoStoppable
      True if the download may stop itself.
    • savedUploaded

      private long savedUploaded
      Uploaded bytes saved in the stats file.
    • _startedTime

      private long _startedTime
      When the download started.
    • _comments

      private CommentSet _comments
      Comments fetched for the torrent.
    • _commentLock

      private final Object _commentLock
      Lock for the comments state.
    • __RPCID

      private static final AtomicInteger __RPCID
      Counter numbering the downloads for the console.
    • _rpcID

      private final int _rpcID
      This download's console id.
    • RETRY_INITIAL_MS

      private static final long RETRY_INITIAL_MS
      Initial startup retry delay after a failed per-torrent session creation, in milliseconds.
      See Also:
    • RETRY_MAX_MS

      private static final long RETRY_MAX_MS
      Maximum startup retry delay (backoff cap), in milliseconds.
      See Also:
    • RETRY_TOTAL_MS

      private static final long RETRY_TOTAL_MS
      Give up on a per-torrent session after this long, in milliseconds.
      See Also:
    • _retryEvent

      private volatile SimpleTimer2.TimedEvent _retryEvent
      Startup retry event, when a per-torrent session creation failed.
    • _retryDelay

      private long _retryDelay
      Current backoff delay for the next session creation retry.
    • _retryStart

      private long _retryStart
      Wall-clock time of the first failed session creation, for the give-up window.
    • _stopCount

      private long _stopCount
      Stop count, incremented by stopTorrent() so a retry event that already fired cannot reconnect a torrent stopped while its event was pending.
    • _storageCreationFailed

      private volatile boolean _storageCreationFailed
      Set when gotMetaInfo(PeerCoordinator, MetaInfo) could not create the storage. The torrent is then left with no metainfo and possibly with some of its files already created on disk, so a restart would fetch the metainfo again only to fail on the pre-existing data location. startTorrent() refuses to start such a torrent; the user has to remove the leftover files (or the torrent) and add it again.
    • allChecked

      private boolean allChecked
      True when every file has been checked.
    • checking

      private boolean checking
      True while a file check is running.
    • storageCompleted

      public boolean storageCompleted
      Whether the torrent storage has reported completion.
    • MIN_TOTAL_UPLOADERS

      static final int MIN_TOTAL_UPLOADERS
      Maintain a configurable total uploader cap CoordinatorListener.
      See Also:
    • MAX_TOTAL_UPLOADERS

      static final int MAX_TOTAL_UPLOADERS
      Maximum peers we upload to across all torrents.
      See Also:
    • notificationSent

      private boolean notificationSent
      True once the completion notice was sent.
  • Constructor Details

    • Snark

      public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port, StorageListener slistener, CoordinatorListener clistener, CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet, ConnectionAcceptor connectionAcceptor, String rootDir)
      Create the torrent in multitorrent mode.

      Will not start itself. Caller must call startTorrent() if desired.

      Parameters:
      util - the I2PSnarkUtil
      torrent - the .torrent file name, used as the storage base name
      ip - the IP address
      user_port - the user port
      slistener - the storage listener
      clistener - the coordinator listener
      complistener - the complete listener
      peerCoordinatorSet - the peer coordinator set
      connectionAcceptor - the connection acceptor
      rootDir - the root data directory
      Throws:
      RuntimeException - if the storage for the torrent cannot be created
      Snark.RouterException - if the torrent cannot be bound to I2P
    • Snark

      public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port, StorageListener slistener, CoordinatorListener clistener, CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet, ConnectionAcceptor connectionAcceptor, String rootDir, File baseFile)
      Create the torrent in multitorrent mode.

      Will not start itself. Caller must call startTorrent() if desired.

      Parameters:
      util - the I2PSnarkUtil
      torrent - the .torrent file name, used as the storage base name
      ip - the IP address
      user_port - the user port
      slistener - the storage listener
      clistener - the coordinator listener
      complistener - the complete listener
      peerCoordinatorSet - the peer coordinator set
      connectionAcceptor - the connection acceptor
      rootDir - the root data directory
      baseFile - if null, use rootDir/torrentName; if non-null, use it instead
      Throws:
      RuntimeException - if the storage for the torrent cannot be created
      Snark.RouterException - if the torrent cannot be bound to I2P
    • Snark

      protected Snark(I2PSnarkUtil util, String torrent, byte[] ih, String trackerURL, CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet, ConnectionAcceptor connectionAcceptor, boolean ignored, String rootDir)
      Create the torrent in multitorrent or magnet mode, for the snark-rpc plugin.

      Will not start itself. Caller must call startTorrent() if desired.

      Parameters:
      util - the I2PSnarkUtil
      torrent - a placeholder .torrent file name, since the info hash is all that is known
      ih - the info hash
      trackerURL - the tracker URL
      complistener - the complete listener
      peerCoordinatorSet - the peer coordinator set
      connectionAcceptor - the connection acceptor
      ignored - used to be autostart
      rootDir - the root data directory
      Throws:
      RuntimeException - if the storage for the torrent cannot be created
      Snark.RouterException - if the torrent cannot be bound to I2P
    • Snark

      public Snark(I2PSnarkUtil util, String torrent, byte[] ih, String trackerURL, CompleteListener complistener, PeerCoordinatorSet peerCoordinatorSet, ConnectionAcceptor connectionAcceptor, String rootDir)
      Create the torrent in multitorrent or magnet mode.

      Will not start itself. Caller must call startTorrent() if desired.

      Parameters:
      util - the I2PSnarkUtil
      torrent - a fake name for now (not a file name)
      ih - 20-byte info hash
      trackerURL - may be null
      complistener - the complete listener
      peerCoordinatorSet - the peer coordinator set
      connectionAcceptor - the connection acceptor
      rootDir - the root data directory
      Throws:
      RuntimeException - if the storage for the torrent cannot be created
      Snark.RouterException - if the torrent cannot be bound to I2P
  • Method Details

    • generateID

      private byte[] generateID()
      Our peer ID for trackers and peers: the spoofed client identity selected for this torrent's destination when i2psnark.clientId is configured, else the legacy anonymous ID of nine zero bytes, three 0x03 bytes, then eight random bytes.
      Returns:
      20-byte peer ID
      Throws:
      Snark.RouterException - if the router is shutting down
    • startTorrent

      public void startTorrent()
      Start up contacting peers and querying the tracker. Blocks if tunnel is not yet open.

      Does nothing if the storage could not be created for a magnet: that leaves the torrent without metainfo and possibly with a partial data location on disk, and a restart would fail again on the leftover files.

      Throws:
      Snark.RouterException - if the router cannot be reached or no session can be created for the torrent's listening destination
    • x_startTorrent

      private void x_startTorrent()
      Starts the torrent's components.
    • scheduleRetry

      private void scheduleRetry(long delay)
      Schedule a retry of startTorrent() after a per-torrent session creation failure. Rescheduled on each subsequent failure; cancelled by stopTorrent() or a manual start.
      Parameters:
      delay - delay before the retry, in milliseconds
    • resetRetry

      private void resetRetry()
      Reset the session creation retry backoff state, on success and on stop.
    • cancelRetry

      private void cancelRetry()
      Cancel a pending startup retry, if any.
    • stopTorrent

      public void stopTorrent()
      Stop contacting the tracker and talking with peers.
    • stopTorrent

      public void stopTorrent(boolean fast)
      Stop contacting the tracker and talking with peers.
      Parameters:
      fast - if true, limit the life of the unannounce threads
    • stopTorrent

      void stopTorrent(boolean fast, boolean deferTeardown)
      Stop contacting the tracker and talking with peers, optionally leaving the I2P session open so the caller can dispatch unannounces before teardown.
      Parameters:
      fast - if true, limit the life of the unannounce threads
      deferTeardown - if true, keep the session for teardownSession()
    • teardownSession

      void teardownSession()
      Destroy the I2P session for this stopped torrent. Called by stopTorrent() or, after the stop's unannounces have had a chance to dispatch, by stopAllTorrents().

      In single-destination mode the session is shared by every torrent, so it is only dropped once no other torrent still needs it. A transient lookup torrent never owned the shared session, so destroying one must not take it down: trackers such as zzzot submit throwaway lookup magnets and delete them again minutes later, and each delete used to disconnect every running torrent, forcing a fresh destination and a full tunnel rebuild.

    • awaitUnannounces

      void awaitUnannounces(long ms)
      Wait, capped at ms, for this torrent's unannounces to dispatch.
      Parameters:
      ms - the time in milliseconds to wait before giving up, 0 to return immediately
    • getName

      public String getName()
      The file name of the .torrent file.
      Returns:
      file name of .torrent file (should be full absolute path), or a fake name if in magnet mode.
    • getBaseName

      public String getBaseName()
      The base name of the torrent.
      Returns:
      base name of torrent [filtered version of getMetaInfo.getName()], or a fake name if in magnet mode
    • getBaseInfo

      private String getBaseInfo()
      The base name with path and error info, for error logging only.
      Returns:
      base name for torrent [filtered version of getMetaInfo.getName()], or a fake name if in magnet mode, followed by path info and error message, for error logging only
    • getID

      public byte[] getID()
      The peer ID.
      Returns:
      always will be valid even in magnet mode
    • getInfoHash

      public byte[] getInfoHash()
      The info hash.
      Returns:
      always will be valid even in magnet mode
    • getDest

      public TorrentDest getDest()
      The transient multi-dest destination, or null when multi-dest is disabled or the torrent is stopped.
      Returns:
      the destination, or null
    • getMetaInfo

      public MetaInfo getMetaInfo()
      The metainfo.
      Returns:
      may be null if in magnet mode
    • getStorage

      public Storage getStorage()
      The storage.
      Returns:
      may be null if in magnet mode
    • isStopped

      public boolean isStopped()
      Check if the torrent is stopped.
      Returns:
      true if stopped
    • isStarting

      public boolean isStarting()
      Check if startup is in progress.
      Returns:
      true if starting
    • setStarting

      public void setStarting()
      Mark startup in progress.
    • isChecking

      public boolean isChecking()
      Check if file checking is in progress.
      Returns:
      true if checking is in progress, false otherwise
    • getCheckingProgress

      public double getCheckingProgress()
      The progress of file checking as a percentage. If checking is in progress, return completion 0.0 ... 1.0, else return 1.0.
      Returns:
      checking progress as a percentage (0.0 to 1.0)
    • isAllocating

      public boolean isAllocating()
      Check if disk allocation (ballooning) is in progress.
      Returns:
      true if allocating, false otherwise
    • getDownloadRate

      public long getDownloadRate()
      The current download rate in bytes per second.
      Returns:
      download rate in bytes per second, or 0 if not available
    • getUploadRate

      public long getUploadRate()
      The current upload rate in bytes per second.
      Returns:
      upload rate in bytes per second, or 0 if not available
    • getDownloaded

      public long getDownloaded()
      The total number of bytes downloaded.
      Returns:
      total bytes downloaded, or 0 if not available
    • getUploaded

      public long getUploaded()
      The total number of bytes uploaded.
      Returns:
      total bytes uploaded, including saved uploaded if coordinator is not available
    • getPeerCount

      public int getPeerCount()
      The peer count.
      Returns:
      connected peers, 0 if the coordinator has not been created yet
    • getPeerList

      public List<Peer> getPeerList()
      The peer list.
      Returns:
      the live list of peers, not a copy
    • getSwarmPeerCount

      public int getSwarmPeerCount()
      Distinct swarm peers seen recently (via announce, PEX or DHT) or currently connected, for the torrent view peer count.
      Returns:
      the count, 0 if no coordinator
    • getTrackerProblems

      public String getTrackerProblems()
      Not HTML escaped.
      Returns:
      String returned from tracker, or null if no error
    • setTrackerProblems

      public void setTrackerProblems(String p)
      Store the tracker problems string.
      Parameters:
      p - tracker error string or null
    • getLastTrackerResponse

      public long getLastTrackerResponse()
      The wall-clock time of the last valid tracker announce or scrape response, or 0 if none was ever received.
      Returns:
      milliseconds since epoch
    • setLastTrackerResponse

      public void setLastTrackerResponse(long t)
      Record a valid tracker announce or scrape response.
      Parameters:
      t - milliseconds since epoch
    • getTrackerSeenPeers

      public int getTrackerSeenPeers()
      The tracker seen peers count.
      Returns:
      count returned from tracker
    • setTrackerSeenPeers

      public void setTrackerSeenPeers(int p)
      Store the tracker seen peers count.
      Parameters:
      p - peer count from tracker
    • getScrapeSeeders

      public int getScrapeSeeders()
      The best-known seed count from tracker scrapes.
      Returns:
      the seed count, 0 if no scrape yet
    • getScrapeLeechers

      public int getScrapeLeechers()
      The best-known leech count from tracker scrapes.
      Returns:
      the leech count, 0 if no scrape yet
    • getScrapePartialSeeds

      public int getScrapePartialSeeds()
      The best-known partial seed count from tracker scrapes.

      Partial seeds have some files but download nothing more (BEP 21).

      Returns:
      the partial seed count, 0 if no scrape yet or not supported
    • updateScrape

      public void updateScrape(int seeders, int leechers)
      Update the best-known swarm composition from a tracker scrape (BEP 15/48).

      Keeps the maximum of each count, and raises the aggregate tracker seen peers to the sum when larger.

      Parameters:
      seeders - the seed count
      leechers - the leech count
    • updateScrape

      public void updateScrape(int seeders, int leechers, int partials)
      Update the best-known swarm composition from a tracker scrape (BEP 15/48/21).

      Keeps the maximum of each count, and raises the aggregate tracker seen peers to the sum when larger.

      Parameters:
      seeders - the seed count
      leechers - the leech count
      partials - the partial seed count, 0 if unknown
    • updatePiecePriorities

      public void updatePiecePriorities()
      Recalculate piece priorities.
    • getTotalLength

      public long getTotalLength()
      The total length of all torrent files, padding files included.
      Returns:
      total of all torrent files, or total of metainfo file if fetching magnet, or -1
    • getDataLength

      public long getDataLength()
      The total length of all non-padding torrent files; equals getTotalLength() for torrents without BEP 47 padding files. The projected downloaded size.
      Returns:
      total of all non-padding files, or -1 if unavailable
    • getRemainingLength

      public long getRemainingLength()
      Bytes not yet in storage. Does NOT account for skipped files.
      Returns:
      exact value. or -1 if no storage yet. getNeeded() * pieceLength(0) isn't accurate if last piece is still needed.
    • getNeededLength

      public long getNeededLength()
      Bytes still wanted. DOES account for (i.e. does not include) skipped files.
      Returns:
      exact value. or -1 if no storage yet or when not running.
    • getSkippedLength

      public long getSkippedLength()
      Bytes not received and set to skipped. This is not the same as the total of all skipped files, since pieces may span multiple files.
      Returns:
      exact value. or 0 if no storage yet.
    • getNeeded

      public long getNeeded()
      The number of pieces still missing. Does not account for (i.e. includes) skipped files.

      In metadata-only mode (the metainfo is known from a magnet but no Storage exists yet, which is how a zzzot hashlist lookup is answered) no piece has been received, so the whole torrent counts as needed.

      Returns:
      number of pieces still needed, or -1 if neither the metainfo nor the storage is available
    • getPieceLength

      public int getPieceLength(int p)
      The piece length.
      Parameters:
      p - the piece number
      Returns:
      metainfo piece length or 16K if fetching magnet
    • getPieces

      public int getPieces()
      The number of pieces.
      Returns:
      number of pieces, or -1 if the metainfo is not known yet (a magnet that has not resolved), in which case getPieceLength(int) reports the 16 KiB placeholder length
    • restartAcceptor

      public boolean restartAcceptor()
      Restart the connection acceptor.
      Returns:
      true if restarted
    • getTrackerURL

      public String getTrackerURL()
      The tracker URL.
      Returns:
      trackerURL string from magnet-mode constructor, may be null
    • isAutoStoppable

      public boolean isAutoStoppable()
      Check if the torrent is auto-stoppable.
      Returns:
      true if auto-stoppable
    • setAutoStoppable

      public void setAutoStoppable(boolean yes)
      Store whether the torrent is auto-stoppable.
      Parameters:
      yes - true if auto-stoppable
    • fatal

      private void fatal(String s) throws RuntimeException
      Aborts program abnormally.
      Parameters:
      s - the failure text logged and reported to the CompleteListener
      Throws:
      RuntimeException - always, after stopping the torrent and notifying the CompleteListener
    • fatal

      private void fatal(String s, Throwable t) throws RuntimeException
      Aborts program abnormally: logs the failure, stops the torrent, tells the CompleteListener, and throws.
      Parameters:
      s - the failure text logged and reported to the CompleteListener
      t - the cause, may be null
      Throws:
      RuntimeException - always, after stopping the torrent and notifying the CompleteListener
    • fatalRouter

      private void fatalRouter(String s, Throwable t) throws Snark.RouterException
      Throws a unique exception class to blame the router that can be caught by SnarkManager.
      Parameters:
      s - the error message to log and pass to the CompleteListener
      t - the cause, attached to the thrown RouterException
      Throws:
      Snark.RouterException - always, after logging the failure, stopping the torrent and notifying the CompleteListener
    • peerChange

      public void peerChange(PeerCoordinator coordinator, Peer peer)
      CoordinatorListener no-op.
      Specified by:
      peerChange in interface CoordinatorListener
      Parameters:
      coordinator - the PeerCoordinator reporting the change
      peer - the peer whose state changed
    • gotMetaInfo

      public void gotMetaInfo(PeerCoordinator coordinator, MetaInfo metainfo)
      Called when the PeerCoordinator got the MetaInfo via magnet. CoordinatorListener. Create the storage, tell SnarkManager, and give the storage back to the coordinator.
      Specified by:
      gotMetaInfo in interface CoordinatorListener
      Parameters:
      coordinator - the PeerCoordinator that retrieved the metadata
      metainfo - the torrent metadata parsed out of the magnet URI
      Throws:
      RuntimeException - if the data location cannot be created for the metainfo that arrived
    • replaceMetaInfo

      public void replaceMetaInfo(MetaInfo metainfo)
      Call after editing torrent. Caller must ensure infohash, files, etc. did not change.
      Parameters:
      metainfo - the replacement metainfo describing the same torrent under new parameters such as trackers and comment metadata
    • storageCreateFile

      public void storageCreateFile(Storage storage, String name, long length)
      StorageListener no-op.
      Specified by:
      storageCreateFile in interface StorageListener
      Parameters:
      storage - the torrent storage the event came from
      name - the file name
      length - the file length
    • storageAllocated

      public void storageAllocated(Storage storage, long length)
      No-op; deliberately empty.
      Specified by:
      storageAllocated in interface StorageListener
      Parameters:
      storage - the torrent storage the event came from
      length - the number of bytes allocated
    • storageChecked

      public void storageChecked(Storage storage, int num, boolean checked)
      StorageListener callback called when a piece check completes.
      Specified by:
      storageChecked in interface StorageListener
      Parameters:
      storage - the Storage that completed the check
      num - the piece number
      checked - true if the piece hash was correct
    • storageAllChecked

      public void storageAllChecked(Storage storage)
      StorageListener callback called when all pieces have been checked.
      Specified by:
      storageAllChecked in interface StorageListener
      Parameters:
      storage - the Storage that finished checking every piece
    • storageCompleted

      public void storageCompleted(Storage storage)
      StorageListener callback called when the torrent has completed.
      Specified by:
      storageCompleted in interface StorageListener
      Parameters:
      storage - the Storage that reported the download complete
    • isStorageCompleted

      public boolean isStorageCompleted()
      Whether the torrent storage has completed.
      Returns:
      true if the torrent storage has completed
    • setWantedPieces

      public void setWantedPieces(Storage storage)
      StorageListener callback to update the coordinator's wanted piece set.
      Specified by:
      setWantedPieces in interface StorageListener
      Parameters:
      storage - the Storage whose wanted piece set has changed
    • shutdown

      public void shutdown()
      SnarkShutdown callback unused.
      Specified by:
      shutdown in interface ShutdownListener
    • addMessage

      public void addMessage(String message)
      StorageListener and CoordinatorListener callback.
      Specified by:
      addMessage in interface CoordinatorListener
      Specified by:
      addMessage in interface StorageListener
      Parameters:
      message - the message to display
    • overUploadLimit

      public boolean overUploadLimit(int uploaders)
      CoordinatorListener callback to check if the total uploaders across all torrents exceeds the configured limit.
      Specified by:
      overUploadLimit in interface CoordinatorListener
      Parameters:
      uploaders - the number of interested uploaders on this coordinator
      Returns:
      true if the global upload limit is exceeded
    • getRPCID

      public int getRPCID()
      A unique ID for this torrent, useful for RPC.
      Returns:
      positive value unless you wrap around
    • getStartedTime

      public long getStartedTime()
      When did we start this torrent For RPC.
      Returns:
      0 if not started before. Not cleared when stopped.
    • getComments

      public CommentSet getComments()
      The current comment set for this torrent. Not a copy. Caller MUST synch on the returned object for all operations.
      Returns:
      may be null if none
    • addComments

      public boolean addComments(List<Comment> comments)
      Add to the current comment set for this torrent, creating it if it didn't previously exist.
      Parameters:
      comments - the comments to add, becoming the entire set if none existed yet
      Returns:
      true if the set changed
    • isNotificationSent

      public boolean isNotificationSent()
      Whether a completion notification has already been sent for this torrent.
      Returns:
      true if a completion notification has already been sent for this torrent
    • setNotificationSent

      public void setNotificationSent(boolean sent)
      Mark that a completion notification has been sent.
      Parameters:
      sent - true if the notification was sent
    • isBanned

      public boolean isBanned(Hash h)
      Is the given hash banned?
      Specified by:
      isBanned in interface CoordinatorListener
      Parameters:
      h - the peer's destination hash, counted as bad up to the acceptor's limit
      Returns:
      true if banned
    • ban

      public void ban(Hash h)
      Ban the given hash.
      Specified by:
      ban in interface CoordinatorListener
      Parameters:
      h - the peer's destination hash to count against the acceptor's limit