// Commented for the Learning branch package com.limegroup.bittorrent.handshaking; import java.nio.ByteBuffer; import java.util.Arrays; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.limegroup.bittorrent.TorrentManager; import com.limegroup.gnutella.io.NIOSocket; /** * Make an IncomingBTHandshaker object to perform the BitTorrent handshake with a remote computer that connected to us. * * A BitTorrent handshake looks like this: * * S * BitTorrent protocol * EEEEEEEE * HHHHHHHHHHHHHHHHHHHH * PPPPPPPPPPPPPPPPPPPP * * S is 19, the length of the text that comes next. * "BitTorrent protocol" is 19 bytes of ASCII text. * E is the remote computer's 8 extension bytes. * H is the 20-byte SHA1 hash of the info section of the .torrent file. * P is the remote computer's peer ID. * * The acceptor already read S and "BitTorrent " from the channel, so verifyIncoming() just checks for "protocol". * The remote computer wants the torrent with the hash H, verifyIncoming() makes sure we have it. */ public class IncomingBTHandshaker extends BTHandshaker { /** A debugging log we can write lines of text to as the program runs. */ private static final Log LOG = LogFactory.getLog(IncomingBTHandshaker.class); /** * "protocol" as ASCII text in an 8-byte array. * This is a part of the BitTorrent handshake, which begins "[19]BitTorrent protocol". */ private static final byte[] PROTOCOL = new byte[] { 'p', 'r', 'o', 't', 'o', 'c', 'o', 'l' }; /** A link back up to the program's single TorrentManager object, which keeps a list of the torrents we're downloading and sharing. */ private final TorrentManager manager; /** * Make a new IncomingBTHandshaker object to do the BitTorrent handshake with a remote computer that connected to us. * Only TorrentManager.acceptConnection() calls this constructor. * This constructor just saves the 2 given objects, it doesn't do anything else. * * @param sock The NIOSocket object that connects us to the remote computer * @param manager A reference to the program's single TorrentManager object, which keeps the list of all the torrents we're downloading and sharing */ public IncomingBTHandshaker(NIOSocket sock, TorrentManager manager) { // Save the given objects in this new one this.sock = sock; this.manager = manager; } /** * Get ready to start the handshake with the remote computer. * * Only TorrentManager.acceptConnection() calls this method. * Makes buffers to hold the remote computer's handshake data, and registers this object with NIO so it will call our handleRead() method. */ public void startHandshaking() { // Make the empty incomingHandshake buffers, where we'll put the data of the remote computer's handshake initIncomingHandshake(); // Register this object with NIO so it will call our handleRead() method when the remote computer send us data setReadInterest(); } /** * Check the handshake the remote computer sent us when it connected to us. * * Looks for the text "protocol", the Acceptor already read the 19 length byte and the text "BitTorrent ". * Makes sure we have the torrent the remote computer wants. * * @return True if a complete part of the remote computer's handshake looks valid. * True if we don't have another new complete part yet. * False if there's a mistake in the handshake data the remote computer sent us. */ protected boolean verifyIncoming() { // Loop for each buffer we've filled in the incomingHandshake array of them since the last time handleRead() called verifyIncoming() for ( ; currentBufIndex < incomingHandshake.length && !incomingHandshake[currentBufIndex].hasRemaining(); currentBufIndex++) { ByteBuffer current = incomingHandshake[currentBufIndex]; // Verify the filled contents of incomingHandshake[0], an 8-byte buffer for the text "protocol" switch(currentBufIndex) { case 0: // Make sure the text is "protocol" if (!Arrays.equals(current.array(), PROTOCOL)) return false; // It's valid break; // Verify the filled contents of incomingHandshake[1], an 8-byte buffer for the remote computer's extension bytes case 1: // It's valid break; // Verify the filled contents of incomingHandshake[2], a 20-byte buffer for the hash of the info section of the .torrent file case 2: // Have the program's TorrentManager object look up the hash torrent = manager.getTorrentForHash(current.array()); // Get the ManagedTorrent object we have to represent it // The TorrentManager doesn't have a ManagedTorrent for that hash, we aren't downloading this file at all if (torrent == null) { // Return false to refuse the remote computer's connection to us if (LOG.isDebugEnabled()) LOG.debug("incoming connection for unknown torrent"); return false; // Yes, we have this file } else { // Compose the handshake we'll send the remote computer initOutgoingHandshake(); // Register this IncomingBTHandshaker with NIO so it will call our handleWrite() method when we can write setWriteInterest(); // Have the BTConnectionFetcher add this IncomingBTHandshaker object to its fetchers list torrent.getFetcher().handshakerStarted(this); } // It's valid break; // Verify the filled contents of incomingHandshake[3], a 20-byte buffer for the remote computer's peer ID case 3: // No way to check it, it's valid break; } } // Return true, we found no problems with the remote computer's handshake data return true; } /** * Make the empty incomingHandshake buffers, where we'll put the data of the remote computer's handshake. * * incomingHandshake[1] is a ByteBuffer object made by wrapping the extBytes byte array. * This means that when handleRead() writes data from the remote computer into the incomingHandshake[2] ByteBuffer, it will also go in the extBytes byte array. * incomingHandshake[3] does this for the peerID byte array the same way. */ protected void initIncomingHandshake() { // Make incomingHandshake an array of 4 ByteBuffer objects incomingHandshake = new ByteBuffer[4]; byte[] tmp = new byte[8]; incomingHandshake[0] = ByteBuffer.wrap(tmp); // Make incomingHandshake[0] to hold 8 bytes extBytes = new byte[8]; incomingHandshake[1] = ByteBuffer.wrap(extBytes); // Make incomingHandshake[1] to hold 8 bytes, linked to the extBytes byte array tmp = new byte[20]; incomingHandshake[2] = ByteBuffer.wrap(tmp); // Make incomingHandshake[2] to hold 20 bytes peerId = new byte[20]; incomingHandshake[3] = ByteBuffer.wrap(peerId); // Make incomingHandshake[3] to hold 20 bytes, linked to the peerID byte array } }