void bitfieldMessage(byte[] bitmap) { synchronized (this) { if (_log.shouldLog(Log.DEBUG)) _log.debug(peer + " rcv bitfield"); if (bitfield != null) { // XXX - Be liberal in what you accept? if (_log.shouldLog(Log.WARN)) _log.warn("Got unexpected bitfield message from " + peer); return; } // XXX - Check for weird bitfield and disconnect? // FIXME will have to regenerate the bitfield after we know exactly // how many pieces there are, as we don't know how many spare bits there are. if (metainfo == null) bitfield = new BitField(bitmap, bitmap.length * 8); else bitfield = new BitField(bitmap, metainfo.getPieces()); } if (metainfo == null) return; boolean interest = listener.gotBitField(peer, bitfield); setInteresting(interest); if (bitfield.complete() && !interest) { // They are seeding and we are seeding, // why did they contact us? (robert) // Dump them quick before we send our whole bitmap if (_log.shouldLog(Log.WARN)) _log.warn("Disconnecting seed that connects to seeds: " + peer); peer.disconnect(true); } }
/** * Disconnects this peer if it was connected. If deregister is true, PeerListener.disconnected() * will be called when the connection is completely terminated. Otherwise the connection is * silently terminated. */ public void disconnect(boolean deregister) { // Both in and out connection will call this. this.deregister = deregister; disconnect(); }
/** * Runs the connection to the other peer. This method does not return until the connection is * terminated. * * <p>When the connection is correctly started the connected() method of the given PeerListener is * called. If the connection ends or the connection could not be setup correctly the * disconnected() method is called. * * <p>If the given BitField is non-null it is send to the peer as first message. */ public void runConnection( I2PSnarkUtil util, PeerListener listener, BitField bitfield, MagnetState mState) { if (state != null) throw new IllegalStateException("Peer already started"); if (_log.shouldLog(Log.DEBUG)) _log.debug("Running connection to " + peerID.toString(), new Exception("connecting")); try { // Do we need to handshake? if (din == null) { // Outgoing connection sock = util.connect(peerID); if (_log.shouldLog(Log.DEBUG)) _log.debug("Connected to " + peerID + ": " + sock); if ((sock == null) || (sock.isClosed())) { throw new IOException("Unable to reach " + peerID); } InputStream in = sock.getInputStream(); OutputStream out = sock.getOutputStream(); byte[] id = handshake(in, out); byte[] expected_id = peerID.getID(); if (expected_id == null) { peerID.setID(id); } else if (Arrays.equals(expected_id, id)) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Handshake got matching IDs with " + toString()); } else { throw new IOException( "Unexpected peerID '" + PeerID.idencode(id) + "' expected '" + PeerID.idencode(expected_id) + "'"); } } else { // Incoming connection if (_log.shouldLog(Log.DEBUG)) _log.debug("Already have din [" + sock + "] with " + toString()); } // bad idea? if (metainfo == null && (options & OPTION_EXTENSION) == 0) { if (_log.shouldLog(Log.INFO)) _log.info("Peer does not support extensions and we need metainfo, dropping"); throw new IOException("Peer does not support extensions and we need metainfo, dropping"); } PeerConnectionIn in = new PeerConnectionIn(this, din); PeerConnectionOut out = new PeerConnectionOut(this, dout); PeerState s = new PeerState(this, listener, metainfo, in, out); if ((options & OPTION_EXTENSION) != 0) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Peer supports extensions, sending reply message"); int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1; out.sendExtension(0, ExtensionHandler.getHandshake(metasize)); } if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Peer supports DHT, sending PORT message"); int port = util.getDHT().getPort(); out.sendPort(port); } // Send our bitmap if (bitfield != null) s.out.sendBitfield(bitfield); // We are up and running! state = s; magnetState = mState; listener.connected(this); if (_log.shouldLog(Log.DEBUG)) _log.debug("Start running the reader with " + toString()); // Use this thread for running the incomming connection. // The outgoing connection creates its own Thread. out.startup(); Thread.currentThread().setName("Snark reader from " + peerID); s.in.run(); } catch (IOException eofe) { // Ignore, probably just the other side closing the connection. // Or refusing the connection, timing out, etc. if (_log.shouldLog(Log.DEBUG)) _log.debug(this.toString(), eofe); } catch (Throwable t) { _log.error(this + ": " + t.getMessage(), t); if (t instanceof OutOfMemoryError) throw (OutOfMemoryError) t; } finally { if (deregister) listener.disconnected(this); disconnect(); } }
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); } }