/** * Validate an expected handshake on a connection. * * <p>Reads an expected handshake message from the given connected socket, parses it and validates * that the torrent hash_info corresponds to the torrent we're sharing, and that the peerId * matches the peer ID we expect to see coming from the remote peer. * * @param socket The connected socket to the remote peer. * @param peerId The peer ID we expect in the handshake. If <em>null</em>, any peer ID is accepted * (this is the case for incoming connections). * @return The validated handshake message object. */ private Handshake validateHandshake(Socket socket, byte[] peerId) throws IOException, ParseException { InputStream is = socket.getInputStream(); // Read the handshake from the wire int pstrlen = is.read(); byte[] data = new byte[Handshake.BASE_HANDSHAKE_LENGTH + pstrlen]; data[0] = (byte) pstrlen; is.read(data, 1, data.length - 1); // Parse and check the handshake Handshake hs = Handshake.parse(ByteBuffer.wrap(data)); if (!Arrays.equals(hs.getInfoHash(), this.torrent.getInfoHash())) { throw new ParseException( "Handshake for unknow torrent " + Torrent.byteArrayToHexString(hs.getInfoHash()) + " from " + this.socketRepr(socket) + ".", pstrlen + 9); } if (peerId != null && !Arrays.equals(hs.getPeerId(), peerId)) { throw new ParseException( "Announced peer ID " + Torrent.byteArrayToHexString(hs.getPeerId()) + " did not match expected peer ID " + Torrent.byteArrayToHexString(peerId) + ".", pstrlen + 29); } return hs; }
/** * Creates the torrent file for the specified <code>srcFile</code> and announce URI. If such * torrent already exists, loads and returns it. */ @Nullable public static File getOrCreateTorrent( @NotNull final File srcFile, @NotNull final String relativePath, @NotNull final File torrentsStore, @NotNull final URI announceURI) { setHashingThreadsCount(); File torrentFile = new File(torrentsStore, relativePath + TORRENT_FILE_SUFFIX); if (torrentFile.isFile()) { try { Torrent t = loadTorrent(torrentFile); for (List<URI> uris : t.getAnnounceList()) { if (uris.contains(announceURI)) return torrentFile; } } catch (IOException e) { LOG.warn( "Failed to load existing torrent file: " + torrentFile.getAbsolutePath() + ", error: " + e.toString() + ". Will create new torrent file instead."); } } final Torrent torrent = createTorrent(srcFile, torrentFile, announceURI); return torrent != null ? torrentFile : null; }
@Override public void run() { Socket socket = new Socket(); InetSocketAddress address = new InetSocketAddress(this.peer.getIp(), this.peer.getPort()); try { logger.info("Connecting to {}...", this.peer); socket.connect(address, 3 * 1000); this.handler.sendHandshake(socket); Handshake hs = this.handler.validateHandshake( socket, (this.peer.hasPeerId() ? this.peer.getPeerId().array() : null)); logger.info( "Handshaked with {}, peer ID is {}.", this.peer, Torrent.byteArrayToHexString(hs.getPeerId())); this.handler.fireNewPeerConnection(socket, hs.getPeerId()); } catch (IOException ioe) { try { socket.close(); } catch (IOException e) { } this.handler.fireFailedConnection(this.peer, ioe); } catch (ParseException pe) { try { socket.close(); } catch (IOException e) { } this.handler.fireFailedConnection(this.peer, pe); } }
/** Creates the torrent file for the specified <code>srcFile</code> and announce URI. */ @Nullable public static Torrent createTorrent( @NotNull File srcFile, @NotNull File torrentFile, @NotNull URI announceURI) { setHashingThreadsCount(); try { Torrent t = Torrent.create(srcFile, announceURI, "TeamCity"); t.save(torrentFile); return t; } catch (Exception e) { LOG.warnAndDebugDetails( String.format( "Unable to create torrent file from %s: %s", srcFile.getPath(), e.toString()), e); } return null; }
/** Loads torrent from torrent file */ @NotNull public static Torrent loadTorrent(@NotNull File torrentFile) throws IOException { setHashingThreadsCount(); try { return Torrent.load(torrentFile); } catch (NoSuchAlgorithmException e) { ExceptionUtil.rethrowAsRuntimeException(e); } return null; }
private static void setHashingThreadsCount() { Torrent.setHashingThreadsCount(2); // limit number of threads generating hashes for a file }