/** * Can't find a published standard for this anywhere. See the libtorrent code. Here we use the * "added" key as a single string of concatenated 32-byte peer hashes. added.f and dropped * unsupported * * @since 0.8.4 */ private static void handlePEX(Peer peer, PeerListener listener, byte[] bs, Log log) { if (log.shouldLog(Log.DEBUG)) log.debug("Got PEX msg from " + peer); try { InputStream is = new ByteArrayInputStream(bs); BDecoder dec = new BDecoder(is); BEValue bev = dec.bdecodeMap(); Map<String, BEValue> map = bev.getMap(); bev = map.get("added"); if (bev == null) return; byte[] ids = bev.getBytes(); if (ids.length < HASH_LENGTH) return; int len = Math.min(ids.length, (I2PSnarkUtil.MAX_CONNECTIONS - 1) * HASH_LENGTH); List<PeerID> peers = new ArrayList<PeerID>(len / HASH_LENGTH); for (int off = 0; off < len; off += HASH_LENGTH) { byte[] hash = new byte[HASH_LENGTH]; System.arraycopy(ids, off, hash, 0, HASH_LENGTH); if (DataHelper.eq(hash, peer.getPeerID().getDestHash())) continue; PeerID pID = new PeerID(hash, listener.getUtil()); peers.add(pID); } // could include ourselves, listener must remove listener.gotPeers(peer, peers); } catch (Exception e) { if (log.shouldLog(Log.INFO)) log.info("PEX msg exception from " + peer, e); // peer.disconnect(false); } }
/** * Creates a new MetaInfo from the given BDecoder. The BDecoder must have a complete dictionary * describing the torrent. */ private MetaInfo(BDecoder be) throws IOException { // Note that evaluation order matters here... this(be.bdecodeMap().getMap()); byte[] origInfohash = be.get_special_map_digest(); // shouldn't ever happen if (!DataHelper.eq(origInfohash, info_hash)) throw new InvalidBEncodingException("Infohash mismatch, please report"); }
/** * Efficiently returns the name and the 20 byte SHA1 hash of the info dictionary in a torrent file * Caller must close stream. * * @param infoHashOut 20-byte out parameter * @since 0.8.5 */ public static String getNameAndInfoHash(InputStream in, byte[] infoHashOut) throws IOException { BDecoder bd = new BDecoder(in); Map<String, BEValue> m = bd.bdecodeMap().getMap(); BEValue ibev = m.get("info"); if (ibev == null) throw new InvalidBEncodingException("Missing info map"); Map<String, BEValue> i = ibev.getMap(); BEValue rvbev = i.get("name"); if (rvbev == null) throw new InvalidBEncodingException("Missing name"); byte[] h = bd.get_special_map_digest(); System.arraycopy(h, 0, infoHashOut, 0, 20); return rvbev.getString(); }
/** * Receive the DHT port numbers * * @since DHT */ private static void handleDHT(Peer peer, PeerListener listener, byte[] bs, Log log) { if (log.shouldLog(Log.DEBUG)) log.debug("Got DHT msg from " + peer); try { InputStream is = new ByteArrayInputStream(bs); BDecoder dec = new BDecoder(is); BEValue bev = dec.bdecodeMap(); Map<String, BEValue> map = bev.getMap(); int qport = map.get("port").getInt(); int rport = map.get("rport").getInt(); listener.gotPort(peer, qport, rport); } catch (Exception e) { if (log.shouldLog(Log.INFO)) log.info("DHT msg exception from " + peer, e); // peer.disconnect(false); } }
private static void handleHandshake(Peer peer, PeerListener listener, byte[] bs, Log log) { if (log.shouldLog(Log.DEBUG)) log.debug("Got handshake msg from " + peer); try { // this throws NPE on missing keys InputStream is = new ByteArrayInputStream(bs); BDecoder dec = new BDecoder(is); BEValue bev = dec.bdecodeMap(); Map<String, BEValue> map = bev.getMap(); peer.setHandshakeMap(map); Map<String, BEValue> msgmap = map.get("m").getMap(); if (log.shouldLog(Log.DEBUG)) log.debug("Peer " + peer + " supports extensions: " + msgmap.keySet()); // if (msgmap.get(TYPE_PEX) != null) { // if (log.shouldLog(Log.DEBUG)) // log.debug("Peer supports PEX extension: " + peer); // // peer state calls peer listener calls sendPEX() // } // if (msgmap.get(TYPE_DHT) != null) { // if (log.shouldLog(Log.DEBUG)) // log.debug("Peer supports DHT extension: " + peer); // // peer state calls peer listener calls sendDHT() // } MagnetState state = peer.getMagnetState(); if (msgmap.get(TYPE_METADATA) == null) { if (log.shouldLog(Log.DEBUG)) log.debug("Peer does not support metadata extension: " + peer); // drop if we need metainfo and we haven't found anybody yet synchronized (state) { if (!state.isInitialized()) { if (log.shouldLog(Log.DEBUG)) log.debug("Dropping peer, we need metadata! " + peer); peer.disconnect(); } } return; } BEValue msize = map.get("metadata_size"); if (msize == null) { if (log.shouldLog(Log.DEBUG)) log.debug("Peer does not have the metainfo size yet: " + peer); // drop if we need metainfo and we haven't found anybody yet synchronized (state) { if (!state.isInitialized()) { if (log.shouldLog(Log.DEBUG)) log.debug("Dropping peer, we need metadata! " + peer); peer.disconnect(); } } return; } int metaSize = msize.getInt(); if (log.shouldLog(Log.DEBUG)) log.debug("Got the metainfo size: " + metaSize); int remaining; synchronized (state) { if (state.isComplete()) return; if (state.isInitialized()) { if (state.getSize() != metaSize) { if (log.shouldLog(Log.DEBUG)) log.debug("Wrong metainfo size " + metaSize + " from: " + peer); peer.disconnect(); return; } } else { // initialize it if (metaSize > MAX_METADATA_SIZE) { if (log.shouldLog(Log.DEBUG)) log.debug("Huge metainfo size " + metaSize + " from: " + peer); peer.disconnect(false); return; } if (log.shouldLog(Log.INFO)) log.info("Initialized state, metadata size = " + metaSize + " from " + peer); state.initialize(metaSize); } remaining = state.chunksRemaining(); } // send requests for chunks int count = Math.min(remaining, PARALLEL_REQUESTS); for (int i = 0; i < count; i++) { int chk; synchronized (state) { chk = state.getNextRequest(); } if (log.shouldLog(Log.INFO)) log.info("Request chunk " + chk + " from " + peer); sendRequest(peer, chk); } } catch (Exception e) { if (log.shouldLog(Log.WARN)) log.warn("Handshake exception from " + peer, e); } }
/** * REF: BEP 9 * * @since 0.8.4 */ private static void handleMetadata(Peer peer, PeerListener listener, byte[] bs, Log log) { if (log.shouldLog(Log.DEBUG)) log.debug("Got metadata msg from " + peer); try { InputStream is = new ByteArrayInputStream(bs); BDecoder dec = new BDecoder(is); BEValue bev = dec.bdecodeMap(); Map<String, BEValue> map = bev.getMap(); int type = map.get("msg_type").getInt(); int piece = map.get("piece").getInt(); MagnetState state = peer.getMagnetState(); if (type == TYPE_REQUEST) { if (log.shouldLog(Log.DEBUG)) log.debug("Got request for " + piece + " from: " + peer); byte[] pc; synchronized (state) { pc = state.getChunk(piece); } sendPiece(peer, piece, pc); // Do this here because PeerConnectionOut only reports for PIECE messages peer.uploaded(pc.length); listener.uploaded(peer, pc.length); } else if (type == TYPE_DATA) { int size = map.get("total_size").getInt(); if (log.shouldLog(Log.DEBUG)) log.debug("Got data for " + piece + " length " + size + " from: " + peer); boolean done; int chk = -1; synchronized (state) { if (state.isComplete()) return; int len = is.available(); if (len != size) { // probably fatal if (log.shouldLog(Log.WARN)) log.warn("total_size " + size + " but avail data " + len); } peer.downloaded(len); listener.downloaded(peer, len); done = state.saveChunk(piece, bs, bs.length - len, len); if (log.shouldLog(Log.INFO)) log.info("Got chunk " + piece + " from " + peer); if (!done) chk = state.getNextRequest(); } // out of the lock if (done) { // Done! // PeerState will call the listener (peer coord), who will // check to see if the MagnetState has it if (log.shouldLog(Log.WARN)) log.warn("Got last chunk from " + peer); } else { // get the next chunk if (log.shouldLog(Log.INFO)) log.info("Request chunk " + chk + " from " + peer); sendRequest(peer, chk); } } else if (type == TYPE_REJECT) { if (log.shouldLog(Log.WARN)) log.warn("Got reject msg from " + peer); peer.disconnect(false); } else { if (log.shouldLog(Log.WARN)) log.warn("Got unknown metadata msg from " + peer); peer.disconnect(false); } } catch (Exception e) { if (log.shouldLog(Log.INFO)) log.info("Metadata ext. msg. exception from " + peer, e); // fatal ? peer.disconnect(false); } }