java.lang.Object
org.klomp.snark.UDPTrackerClient
- All Implemented Interfaces:
I2PSessionListener, I2PSessionMuxedListener
One of these for all trackers and info hashes. Ref: BEP 15, proposal 160.
The main difference from BEP 15 is that the announce response contains a 32-byte hash instead of a 4-byte IP and a 2-byte port.
We send both repliable and raw datagrams, but we only receive raw datagrams, as follows:
client tracker type
----------+----------------+--------------------
conn req | --> | repliable Datagram2
| <-- conn resp | raw
announce | --> | repliable Datagram3
| <-- ann resp | raw
| <-- error | raw
----------+-----------------+-------------------
Nested Class Summary
Modifier and TypeClassDescriptionprivate static enumState of the connection id for one tracker.private static classA tracker host and port.private classCallback for replies.private classOne UDP tracker and its connection state.static classRepresents a response from a UDP tracker.
This class encapsulates the tracker's response including peer lists, interval information, and any error messages.private static enumState of one outstanding tracker request.Field Summary
Modifier and TypeFieldDescriptionprivate final I2PAppContextThe app context.private booleanTrue while the client thread is running.private final LogLogger for UDP tracker traffic.private final HashHash of this client's destination.private final intUnsigned datagrams.private final Map<Integer, UDPTrackerClient.ReplyWaiter> Our TID to tracker.private final I2PSessionHook to inject and receive datagrams.private final ConcurrentHashMap<UDPTrackerClient.HostPort, UDPTrackerClient.Tracker> Dest and port to tracker data.private final I2PSnarkUtilThe webapp's configuration helper.private static final intAction value for an announce request.private static final intAction value for a connection request.private static final intAction value marking an error reply.private static final intAction value for a scrape request.private static final longHow long a connection ID stays valid, in milliseconds (BEP 15 connection lifetime).private static final intAnnounce interval used until a tracker reports one, in seconds.private static final longQuery timeout before any backoff, in milliseconds.static final intTorrent download completed.static final intNo event (periodic announce).static final intPartial seed (BEP 21), HTTP only; not defined in the UDP protocol.static final intTorrent download started.static final intTorrent stopped.private static final longInitial connection id sent in the connect request.private static final intLongest announce interval accepted from a tracker, in seconds.private static final intShortest announce interval accepted from a tracker, in seconds.Constructor Summary
ConstructorsConstructorDescriptionUDPTrackerClient(I2PAppContext ctx, I2PSession session, I2PSnarkUtil util) Create a client bound to one torrent's session, listening on the port just belowTrackerClient.PORTso it never collides with the HTTP tracker client.Method Summary
Modifier and TypeMethodDescriptionannounce(byte[] ih, byte[] peerID, int max, long maxWait, String toHost, int toPort, long downloaded, long left, long uploaded, int event, boolean fast) Announce and get peers for a torrent.voiddisconnected(I2PSession session) Handle a session disconnect.voiderrorOccurred(I2PSession session, String message, Throwable error) Handle a session error.private LonggetConnection(UDPTrackerClient.Tracker tr, long untilTime) Establish a connection to the tracker.private UDPTrackerClient.TrackergetTracker(String host, int port) Create or find a tracker entry.voidmessageAvailable(I2PSession session, int msgId, long size) For non-muxed.voidmessageAvailable(I2PSession session, int msgId, long size, int proto, int fromPort, int toPort) Instruct the client that the given session has received a message.private voidreceiveAnnounce(UDPTrackerClient.ReplyWaiter waiter, byte[] payload) Parses an announce reply and hands it to the waiter.private voidreceiveConnection(UDPTrackerClient.ReplyWaiter waiter, byte[] payload) Receive a connection reply.private voidreceiveError(UDPTrackerClient.ReplyWaiter waiter, byte[] payload, int expected) Parses an error reply and fails the waiter.private voidreceiveMessage(Destination from, int fromPort, byte[] payload) Receive a message.private voidreceiveScrape(UDPTrackerClient.ReplyWaiter waiter, byte[] payload) Parse a BEP 15 scrape response, single info hash: seeders, completed, leechers.voidreportAbuse(I2PSession session, int severity) Handle a reportAbuse message from the session.private booleanresend(UDPTrackerClient.ReplyWaiter w, long toWait) Resend the stored payload.Scrape for swarm composition for a torrent.private byte[]sendAnnounce(UDPTrackerClient.Tracker tr, int tid, long connID, byte[] ih, byte[] id, long downloaded, long left, long uploaded, int event, int numWant) Send one time with given tid.private UDPTrackerClient.ReplyWaitersendAnnounce(UDPTrackerClient.Tracker tr, long connID, byte[] ih, byte[] id, long downloaded, long left, long uploaded, int event, int numWant, long toWait) Send one time with a new tid.private byte[]sendConnReq(UDPTrackerClient.Tracker tr, int tid) Send one time with given tid.private UDPTrackerClient.ReplyWaitersendConnReq(UDPTrackerClient.Tracker tr, long toWait) Send one time with a new tid.private booleansendMessage(Destination dest, int toPort, byte[] payload, boolean repliable) Lowest-level send message call.private byte[]sendScrape(UDPTrackerClient.Tracker tr, int tid, long connID, byte[] ih) Send one time with given tid.private UDPTrackerClient.ReplyWaitersendScrape(UDPTrackerClient.Tracker tr, long connID, byte[] ih, long toWait) Send one time with a new tid.voidstart()Can't be restarted after stopping?voidstop()Stop everything.private booleanwaitAndRetransmit(UDPTrackerClient.ReplyWaiter w, long untilTime) Wait after initial send, resend if necessary.
Field Details
_context
The app context._log
Logger for UDP tracker traffic._session
Hook to inject and receive datagrams._util
The webapp's configuration helper._myHash
Hash of this client's destination._rPort
private final int _rPortUnsigned datagrams._trackers
Dest and port to tracker data._sentQueries
Our TID to tracker._isRunning
private boolean _isRunningTrue while the client thread is running.INIT_CONN_ID
private static final long INIT_CONN_IDInitial connection id sent in the connect request.- See Also:
EVENT_NONE
EVENT_COMPLETED
EVENT_STARTED
EVENT_STOPPED
EVENT_PAUSED
public static final int EVENT_PAUSEDPartial seed (BEP 21), HTTP only; not defined in the UDP protocol.- See Also:
ACTION_CONNECT
private static final int ACTION_CONNECTAction value for a connection request.- See Also:
ACTION_ANNOUNCE
private static final int ACTION_ANNOUNCEAction value for an announce request.- See Also:
ACTION_SCRAPE
private static final int ACTION_SCRAPEAction value for a scrape request.- See Also:
ACTION_ERROR
private static final int ACTION_ERRORAction value marking an error reply.- See Also:
CONN_EXPIRATION
private static final long CONN_EXPIRATIONHow long a connection ID stays valid, in milliseconds (BEP 15 connection lifetime).- See Also:
DEFAULT_TIMEOUT
private static final long DEFAULT_TIMEOUTQuery timeout before any backoff, in milliseconds.- See Also:
DEFAULT_INTERVAL
private static final int DEFAULT_INTERVALAnnounce interval used until a tracker reports one, in seconds.- See Also:
MIN_INTERVAL
private static final int MIN_INTERVALShortest announce interval accepted from a tracker, in seconds.- See Also:
MAX_INTERVAL
private static final int MAX_INTERVALLongest announce interval accepted from a tracker, in seconds.- See Also:
Constructor Details
UDPTrackerClient
Create a client bound to one torrent's session, listening on the port just belowTrackerClient.PORTso it never collides with the HTTP tracker client.- Parameters:
ctx- the application context supplying the log and timer servicessession- the torrent's session, used to send datagrams and to derive our own hashutil- the utility instance supplying the enabled-tracker configuration
Method Details
start
public void start()Can't be restarted after stopping?stop
public void stop()Stop everything.announce
public UDPTrackerClient.TrackerResponse announce(byte[] ih, byte[] peerID, int max, long maxWait, String toHost, int toPort, long downloaded, long left, long uploaded, int event, boolean fast) Announce and get peers for a torrent. Blocking! Caller should run in a thread.- Parameters:
ih- the Info Hash (torrent)peerID- the peer ID this client announces itself undermax- maximum number of peers to returnmaxWait- the maximum time to wait (ms) must be > 0toHost- the tracker hostnametoPort- the tracker portdownloaded- bytes downloaded so far for this torrentleft- bytes still to download for this torrentuploaded- bytes uploaded so far for this torrentevent- one of the EVENT_* codes describing why this announce is being madefast- if true, don't wait for dest, no retx, ...- Returns:
- null on fail or if fast is true
scrape
Scrape for swarm composition for a torrent. Blocking! Caller should run in a thread.BEP 15 scrape request, single info hash.
- Parameters:
ih- the Info Hash (torrent)maxWait- the maximum time to wait (ms) must be > 0toHost- the tracker hostnametoPort- the tracker port- Returns:
- the response, or null on fail or timeout
getConnection
Establish a connection to the tracker.- Parameters:
tr- the tracker to connect tountilTime- the deadline time in milliseconds- Returns:
- the connection ID, or null on fail
getTracker
Create or find a tracker entry.- Parameters:
host- the tracker hostnameport- the tracker port- Returns:
- non-null tracker entry
sendConnReq
Send one time with a new tid.- Parameters:
tr- the tracker to send the connect request totoWait- how long to wait for the reply, in ms, greater than zero- Returns:
- the waiter to poll for the reply, or null if the request could not be sent
- Throws:
IllegalArgumentException- if toWait is zero or negative
sendConnReq
Send one time with given tid.- Parameters:
tr- the tracker to send the connect request totid- the transaction ID to send, and to match the reply against- Returns:
- the payload or null on failure
sendAnnounce
private UDPTrackerClient.ReplyWaiter sendAnnounce(UDPTrackerClient.Tracker tr, long connID, byte[] ih, byte[] id, long downloaded, long left, long uploaded, int event, int numWant, long toWait) Send one time with a new tid.- Parameters:
tr- the tracker to announce toconnID- the connection ID from the connect responseih- the 20-byte SHA-1 info hash of the torrentid- the 20-byte peer ID to advertisedownloaded- bytes downloaded so far for this torrentleft- bytes still to download for this torrentuploaded- bytes uploaded so far for this torrentevent- one of the EVENT_* codes describing why this announce is madenumWant- maximum number of peers to returntoWait- how long to wait for the reply, in ms, 0 or less to skip registering a waiter- Returns:
- null on failure or if toWait <= 0
sendAnnounce
private byte[] sendAnnounce(UDPTrackerClient.Tracker tr, int tid, long connID, byte[] ih, byte[] id, long downloaded, long left, long uploaded, int event, int numWant) Send one time with given tid.- Parameters:
tr- the tracker to announce totid- the transaction ID to send, and to match the reply againstconnID- the connection ID from the connect responseih- the 20-byte SHA-1 info hash of the torrentid- the 20-byte peer ID to advertisedownloaded- bytes downloaded so far for this torrentleft- bytes still to download for this torrentuploaded- bytes uploaded so far for this torrentevent- one of the EVENT_* codes describing why this announce is madenumWant- maximum number of peers to return- Returns:
- the payload or null on failure
sendScrape
private UDPTrackerClient.ReplyWaiter sendScrape(UDPTrackerClient.Tracker tr, long connID, byte[] ih, long toWait) Send one time with a new tid.- Parameters:
tr- the tracker to scrape fromconnID- the connection ID from the connect responseih- the 20-byte SHA-1 info hash to scrapetoWait- if <= 0 does not register- Returns:
- null on failure or if toWait <= 0
sendScrape
Send one time with given tid.- Parameters:
tr- the tracker to scrape fromtid- the transaction ID to send, and to match the reply againstconnID- the connection ID from the connect responseih- the 20-byte SHA-1 info hash to scrape- Returns:
- the payload or null on failure
waitAndRetransmit
Wait after initial send, resend if necessary.- Parameters:
w- the outstanding query to wait for a reply on, locked throughoutuntilTime- deadline in ms since epoch to keep retransmitting until- Returns:
- true if a valid reply was received, false on timeout or resend failure
resend
Resend the stored payload.- Parameters:
w- the outstanding query whose stored payload is sent againtoWait- the retransmit timeout in ms, used only for logging- Returns:
- true if the datagram was handed to the session for sending
sendMessage
Lowest-level send message call.- Parameters:
dest- may be null, returns falsetoPort- the tracker's UDP port to send the datagram topayload- the BEP 15 request bytes, wrapped before sendingrepliable- true to wrap in a repliable Datagram2, false for a raw Datagram3 that does not carry our lease set- Returns:
- true if the session accepted the datagram; false when not running, when the payload cannot be wrapped in a Datagram2 or Datagram3, or when the send is rejected
receiveMessage
Receive a message.- Parameters:
from- dest or null if it didn't come in on signed portfromPort- the local port the datagram arrived on, unusedpayload- the raw tracker response, ignored if shorter than 8 bytes
receiveConnection
Receive a connection reply.- Parameters:
waiter- the outstanding request this reply answerspayload- the connect response: 8-byte action and tid header, an 8-byte connection ID at offset 8, and an optional 2-byte lifetime in seconds at offset 16
receiveAnnounce
Parses an announce reply and hands it to the waiter.- Parameters:
waiter- the request the reply answerspayload- the reply body
receiveScrape
Parse a BEP 15 scrape response, single info hash: seeders, completed, leechers.- Parameters:
waiter- the waiter for the scrape requestpayload- the raw response
receiveError
Parses an error reply and fails the waiter.- Parameters:
waiter- the request the reply answerspayload- the reply bodyexpected- the action the reply should have carried
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:
messageAvailablein interfaceI2PSessionMuxedListener- Parameters:
session- session to notifymsgId- message number availablesize- size of the message - why it's a long and not an int is a mysteryproto- 1-254 or 0 for unspecifiedfromPort- 1-65535 or 0 for unspecifiedtoPort- 1-65535 or 0 for unspecified
messageAvailable
For non-muxed.- Specified by:
messageAvailablein interfaceI2PSessionListener- Specified by:
messageAvailablein interfaceI2PSessionMuxedListener- Parameters:
session- session to notifymsgId- message number availablesize- size of the message payload in bytes. The I2CP payload size is an int internally and is widened to long here, so the value is never negative
reportAbuse
Handle a reportAbuse message from the session.- Specified by:
reportAbusein interfaceI2PSessionListener- Specified by:
reportAbusein interfaceI2PSessionMuxedListener- Parameters:
session- the session that reported the abuseseverity- the abuse severity
disconnected
Handle a session disconnect.- Specified by:
disconnectedin interfaceI2PSessionListener- Specified by:
disconnectedin interfaceI2PSessionMuxedListener- Parameters:
session- the session that dropped
errorOccurred
Handle a session error.- Specified by:
errorOccurredin interfaceI2PSessionListener- Specified by:
errorOccurredin interfaceI2PSessionMuxedListener- Parameters:
session- the session that reported the errormessage- the error messageerror- the cause of the error, non-null