private boolean handleGetOfferedKey(Message m, PeerNode source) { Key key = (Key) m.getObject(DMT.KEY); byte[] authenticator = ((ShortBuffer) m.getObject(DMT.OFFER_AUTHENTICATOR)).getData(); long uid = m.getLong(DMT.UID); if (!HMAC.verifyWithSHA256( node.failureTable.offerAuthenticatorKey, key.getFullKey(), authenticator)) { Logger.error( this, "Invalid offer request from " + source + " : authenticator did not verify"); try { source.sendAsync( DMT.createFNPGetOfferedKeyInvalid(uid, DMT.GET_OFFERED_KEY_REJECTED_BAD_AUTHENTICATOR), null, node.failureTable.senderCounter); } catch (NotConnectedException e) { // Too bad. } return true; } if (logMINOR) Logger.minor(this, "Valid GetOfferedKey for " + key + " from " + source); // Do we want it? We can RejectOverload if we don't have the bandwidth... boolean isSSK = key instanceof NodeSSK; OfferReplyTag tag = new OfferReplyTag(isSSK); node.lockUID(uid, isSSK, false, true, false, tag); boolean needPubKey; try { needPubKey = m.getBoolean(DMT.NEED_PUB_KEY); String reject = nodeStats.shouldRejectRequest(true, false, isSSK, false, true, source, false); if (reject != null) { Logger.normal( this, "Rejecting FNPGetOfferedKey from " + source + " for " + key + " : " + reject); Message rejected = DMT.createFNPRejectedOverload(uid, true); try { source.sendAsync(rejected, null, node.failureTable.senderCounter); } catch (NotConnectedException e) { Logger.normal( this, "Rejecting (overload) data request from " + source.getPeer() + ": " + e); } node.unlockUID(uid, isSSK, false, false, true, false, tag); return true; } } catch (Error e) { node.unlockUID(uid, isSSK, false, false, true, false, tag); throw e; } catch (RuntimeException e) { node.unlockUID(uid, isSSK, false, false, true, false, tag); throw e; } // Otherwise, sendOfferedKey is responsible for unlocking. // Accept it. try { node.failureTable.sendOfferedKey(key, isSSK, needPubKey, uid, source, tag); } catch (NotConnectedException e) { // Too bad. } return true; }
/** * Receive the file. * * @return True if the whole file was received, false otherwise. */ public boolean receive() { while (true) { MessageFilter mfSendKilled = MessageFilter.create() .setSource(peer) .setType(DMT.FNPBulkSendAborted) .setField(DMT.UID, uid) .setTimeout(TIMEOUT); MessageFilter mfPacket = MessageFilter.create() .setSource(peer) .setType(DMT.FNPBulkPacketSend) .setField(DMT.UID, uid) .setTimeout(TIMEOUT); if (prb.hasWholeFile()) { try { peer.sendAsync(DMT.createFNPBulkReceivedAll(uid), null, ctr); } catch (NotConnectedException e) { // Ignore, we have the data. } return true; } Message m; try { m = prb.usm.waitFor(mfSendKilled.or(mfPacket), ctr); } catch (DisconnectedException e) { prb.abort(RetrievalException.SENDER_DISCONNECTED, "Sender disconnected"); return false; } if (peer.getBootID() != peerBootID) { prb.abort(RetrievalException.SENDER_DIED, "Sender restarted"); return false; } if (m == null) { prb.abort(RetrievalException.TIMED_OUT, "Sender timeout"); return false; } if (m.getSpec() == DMT.FNPBulkSendAborted) { prb.abort(RetrievalException.SENDER_DIED, "Sender cancelled send"); return false; } if (m.getSpec() == DMT.FNPBulkPacketSend) { int packetNo = m.getInt(DMT.PACKET_NO); byte[] data = ((ShortBuffer) m.getObject(DMT.DATA)).getData(); prb.received(packetNo, data, 0, data.length); } } }
/** * Handle a routed-to-a-specific-node message. * * @param m * @return False if we want the message put back on the queue. */ boolean handleRouted(Message m, PeerNode source) { if (logMINOR) Logger.minor(this, "handleRouted(" + m + ')'); long id = m.getLong(DMT.UID); Long lid = Long.valueOf(id); short htl = m.getShort(DMT.HTL); byte[] identity = ((ShortBuffer) m.getObject(DMT.NODE_IDENTITY)).getData(); if (source != null) htl = source.decrementHTL(htl); RoutedContext ctx; ctx = routedContexts.get(lid); if (ctx != null) { try { source.sendAsync(DMT.createFNPRoutedRejected(id, htl), null, nodeStats.routedMessageCtr); } catch (NotConnectedException e) { if (logMINOR) Logger.minor(this, "Lost connection rejecting " + m); } return true; } ctx = new RoutedContext(m, source, identity); synchronized (routedContexts) { routedContexts.put(lid, ctx); } // source == null => originated locally, keep full htl double target = m.getDouble(DMT.TARGET_LOCATION); if (logMINOR) Logger.minor(this, "id " + id + " from " + source + " htl " + htl + " target " + target); if (Math.abs(node.lm.getLocation() - target) <= Double.MIN_VALUE) { if (logMINOR) Logger.minor(this, "Dispatching " + m.getSpec() + " on " + node.getDarknetPortNumber()); // Handle locally // Message type specific processing dispatchRoutedMessage(m, source, id); return true; } else if (htl == 0) { Message reject = DMT.createFNPRoutedRejected(id, (short) 0); if (source != null) try { source.sendAsync(reject, null, nodeStats.routedMessageCtr); } catch (NotConnectedException e) { if (logMINOR) Logger.minor(this, "Lost connection rejecting " + m); } return true; } else { return forward(m, id, source, htl, target, ctx, identity); } }
private void finishDisconnect(final Message m, final PeerNode source) { source.disconnected(true, true); // If true, remove from active routing table, likely to be down for a while. // Otherwise just dump all current connection state and keep trying to connect. boolean remove = m.getBoolean(DMT.REMOVE); if (remove) node.peers.disconnect(source, false, false, false); // If true, purge all references to this node. Otherwise, we can keep the node // around in secondary tables etc in order to more easily reconnect later. // (Mostly used on opennet) boolean purge = m.getBoolean(DMT.PURGE); if (purge) { OpennetManager om = node.getOpennet(); if (om != null) om.purgeOldOpennetPeer(source); } // Process parting message int type = m.getInt(DMT.NODE_TO_NODE_MESSAGE_TYPE); ShortBuffer messageData = (ShortBuffer) m.getObject(DMT.NODE_TO_NODE_MESSAGE_DATA); if (messageData.getLength() == 0) return; node.receivedNodeToNodeMessage(source, type, messageData, true); }
public boolean handleMessage(Message m) { PeerNode source = (PeerNode) m.getSource(); if (source == null) { // Node has been disconnected and garbage collected already! Ouch. return true; } if (logMINOR) Logger.minor(this, "Dispatching " + m + " from " + source); if (callback != null) { try { callback.snoop(m, node); } catch (Throwable t) { Logger.error(this, "Callback threw " + t, t); } } MessageType spec = m.getSpec(); if (spec == DMT.FNPPing) { // Send an FNPPong Message reply = DMT.createFNPPong(m.getInt(DMT.PING_SEQNO)); try { source.sendAsync(reply, null, pingCounter); // nothing we can do if can't contact source } catch (NotConnectedException e) { if (logMINOR) Logger.minor(this, "Lost connection replying to " + m); } return true; } else if (spec == DMT.FNPStoreSecret) { return node.netid.handleStoreSecret(m); } else if (spec == DMT.FNPSecretPing) { return node.netid.handleSecretPing(m); } else if (spec == DMT.FNPDetectedIPAddress) { Peer p = (Peer) m.getObject(DMT.EXTERNAL_ADDRESS); source.setRemoteDetectedPeer(p); node.ipDetector.redetectAddress(); return true; } else if (spec == DMT.FNPTime) { return handleTime(m, source); } else if (spec == DMT.FNPUptime) { return handleUptime(m, source); } else if (spec == DMT.FNPSentPackets) { source.handleSentPackets(m); return true; } else if (spec == DMT.FNPVoid) { return true; } else if (spec == DMT.FNPDisconnect) { handleDisconnect(m, source); return true; } else if (spec == DMT.nodeToNodeMessage) { node.receivedNodeToNodeMessage(m, source); return true; } else if (spec == DMT.UOMAnnounce && source.isRealConnection()) { return node.nodeUpdater.uom.handleAnnounce(m, source); } else if (spec == DMT.UOMRequestRevocation && source.isRealConnection()) { return node.nodeUpdater.uom.handleRequestRevocation(m, source); } else if (spec == DMT.UOMSendingRevocation && source.isRealConnection()) { return node.nodeUpdater.uom.handleSendingRevocation(m, source); } else if (spec == DMT.UOMRequestMain && source.isRealConnection()) { return node.nodeUpdater.uom.handleRequestJar(m, source, false); } else if (spec == DMT.UOMRequestExtra && source.isRealConnection()) { return node.nodeUpdater.uom.handleRequestJar(m, source, true); } else if (spec == DMT.UOMSendingMain && source.isRealConnection()) { return node.nodeUpdater.uom.handleSendingMain(m, source); } else if (spec == DMT.UOMSendingExtra && source.isRealConnection()) { return node.nodeUpdater.uom.handleSendingExt(m, source); } else if (spec == DMT.FNPOpennetAnnounceRequest) { return handleAnnounceRequest(m, source); } else if (spec == DMT.FNPRoutingStatus) { if (source instanceof DarknetPeerNode) { boolean value = m.getBoolean(DMT.ROUTING_ENABLED); if (logMINOR) Logger.minor(this, "The peer (" + source + ") asked us to set routing=" + value); ((DarknetPeerNode) source).setRoutingStatus(value, false); } // We claim it in any case return true; } else if (source.isRealConnection() && spec == DMT.FNPLocChangeNotificationNew) { double newLoc = m.getDouble(DMT.LOCATION); ShortBuffer buffer = ((ShortBuffer) m.getObject(DMT.PEER_LOCATIONS)); double[] locs = Fields.bytesToDoubles(buffer.getData()); /** * Do *NOT* remove the sanity check below! * * @see http://archives.freenetproject.org/message/20080718.144240.359e16d3.en.html */ if ((OpennetManager.MAX_PEERS_FOR_SCALING < locs.length) && (source.isOpennet())) { if (locs.length > OpennetManager.PANIC_MAX_PEERS) { // This can't happen by accident Logger.error( this, "We received " + locs.length + " locations from " + source.toString() + "! That should *NOT* happen! Possible attack!"); source.forceDisconnect(true); return true; } else { // A few extra can happen by accident. Just use the first 20. Logger.normal( this, "Too many locations from " + source.toString() + " : " + locs.length + " could be an accident, using the first " + OpennetManager.MAX_PEERS_FOR_SCALING); double[] firstLocs = new double[OpennetManager.MAX_PEERS_FOR_SCALING]; System.arraycopy(locs, 0, firstLocs, 0, OpennetManager.MAX_PEERS_FOR_SCALING); locs = firstLocs; } } // We are on darknet and we trust our peers OR we are on opennet // and the amount of locations sent to us seems reasonable source.updateLocation(newLoc, locs); return true; } if (!source.isRoutable()) return false; if (logDEBUG) Logger.debug(this, "Not routable"); if (spec == DMT.FNPNetworkID) { source.handleFNPNetworkID(m); return true; } else if (spec == DMT.FNPSwapRequest) { return node.lm.handleSwapRequest(m, source); } else if (spec == DMT.FNPSwapReply) { return node.lm.handleSwapReply(m, source); } else if (spec == DMT.FNPSwapRejected) { return node.lm.handleSwapRejected(m, source); } else if (spec == DMT.FNPSwapCommit) { return node.lm.handleSwapCommit(m, source); } else if (spec == DMT.FNPSwapComplete) { return node.lm.handleSwapComplete(m, source); } else if (spec == DMT.FNPCHKDataRequest) { return handleDataRequest(m, source, false); } else if (spec == DMT.FNPSSKDataRequest) { return handleDataRequest(m, source, true); } else if (spec == DMT.FNPInsertRequest) { return handleInsertRequest(m, source, false); } else if (spec == DMT.FNPSSKInsertRequest) { return handleInsertRequest(m, source, true); } else if (spec == DMT.FNPSSKInsertRequestNew) { return handleInsertRequest(m, source, true); } else if (spec == DMT.FNPRHProbeRequest) { return handleProbeRequest(m, source); } else if (spec == DMT.FNPRoutedPing) { return handleRouted(m, source); } else if (spec == DMT.FNPRoutedPong) { return handleRoutedReply(m); } else if (spec == DMT.FNPRoutedRejected) { return handleRoutedRejected(m); // FIXME implement threaded probe requests of various kinds. // Old probe request code was a major pain, never really worked. // We should have threaded probe requests (for simple code), // and one for each routing strategy. // } else if(spec == DMT.FNPProbeRequest) { // return handleProbeRequest(m, source); // } else if(spec == DMT.FNPProbeReply) { // return handleProbeReply(m, source); // } else if(spec == DMT.FNPProbeRejected) { // return handleProbeRejected(m, source); // } else if(spec == DMT.FNPProbeTrace) { // return handleProbeTrace(m, source); } else if (spec == DMT.FNPOfferKey) { return handleOfferKey(m, source); } else if (spec == DMT.FNPGetOfferedKey) { return handleGetOfferedKey(m, source); } return false; }
private boolean handleInsertRequest(Message m, PeerNode source, boolean isSSK) { ByteCounter ctr = isSSK ? node.nodeStats.sskInsertCtr : node.nodeStats.chkInsertCtr; long id = m.getLong(DMT.UID); if (node.recentlyCompleted(id)) { Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting insert request from " + source.getPeer() + ": " + e); } return true; } InsertTag tag = new InsertTag(isSSK, InsertTag.START.REMOTE); if (!node.lockUID(id, isSSK, true, false, false, tag)) { if (logMINOR) Logger.minor(this, "Could not lock ID " + id + " -> rejecting (already running)"); Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting insert request from " + source.getPeer() + ": " + e); } return true; } // SSKs don't fix bwlimitDelayTime so shouldn't be accepted when overloaded. String rejectReason = nodeStats.shouldRejectRequest(!isSSK, true, isSSK, false, false, source, false); if (rejectReason != null) { Logger.normal( this, "Rejecting insert from " + source.getPeer() + " preemptively because " + rejectReason); Message rejected = DMT.createFNPRejectedOverload(id, true); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal( this, "Rejecting (overload) insert request from " + source.getPeer() + ": " + e); } node.unlockUID(id, isSSK, true, false, false, false, tag); return true; } boolean forkOnCacheable = Node.FORK_ON_CACHEABLE_DEFAULT; Message forkControl = m.getSubMessage(DMT.FNPSubInsertForkControl); if (forkControl != null) forkOnCacheable = forkControl.getBoolean(DMT.ENABLE_INSERT_FORK_WHEN_CACHEABLE); long now = System.currentTimeMillis(); if (m.getSpec().equals(DMT.FNPSSKInsertRequest)) { NodeSSK key = (NodeSSK) m.getObject(DMT.FREENET_ROUTING_KEY); byte[] data = ((ShortBuffer) m.getObject(DMT.DATA)).getData(); byte[] headers = ((ShortBuffer) m.getObject(DMT.BLOCK_HEADERS)).getData(); short htl = m.getShort(DMT.HTL); SSKInsertHandler rh = new SSKInsertHandler( key, data, headers, htl, source, id, node, now, tag, node.canWriteDatastoreInsert(htl), forkOnCacheable); rh.receivedBytes(m.receivedByteCount()); node.executor.execute( rh, "SSKInsertHandler for " + id + " on " + node.getDarknetPortNumber()); } else if (m.getSpec().equals(DMT.FNPSSKInsertRequestNew)) { NodeSSK key = (NodeSSK) m.getObject(DMT.FREENET_ROUTING_KEY); short htl = m.getShort(DMT.HTL); SSKInsertHandler rh = new SSKInsertHandler( key, null, null, htl, source, id, node, now, tag, node.canWriteDatastoreInsert(htl), forkOnCacheable); rh.receivedBytes(m.receivedByteCount()); node.executor.execute( rh, "SSKInsertHandler for " + id + " on " + node.getDarknetPortNumber()); } else { CHKInsertHandler rh = new CHKInsertHandler(m, source, id, node, now, tag, forkOnCacheable); node.executor.execute( rh, "CHKInsertHandler for " + id + " on " + node.getDarknetPortNumber()); } if (logMINOR) Logger.minor(this, "Started InsertHandler for " + id); return true; }
/** Handle an incoming FNPDataRequest. */ private boolean handleDataRequest(Message m, PeerNode source, boolean isSSK) { long id = m.getLong(DMT.UID); ByteCounter ctr = isSSK ? node.nodeStats.sskRequestCtr : node.nodeStats.chkRequestCtr; if (node.recentlyCompleted(id)) { Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting data request (loop, finished): " + e); } return true; } short htl = m.getShort(DMT.HTL); Key key = (Key) m.getObject(DMT.FREENET_ROUTING_KEY); final RequestTag tag = new RequestTag(isSSK, RequestTag.START.REMOTE); if (!node.lockUID(id, isSSK, false, false, false, tag)) { if (logMINOR) Logger.minor(this, "Could not lock ID " + id + " -> rejecting (already running)"); Message rejected = DMT.createFNPRejectedLoop(id); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal(this, "Rejecting request from " + source.getPeer() + ": " + e); } node.failureTable.onFinalFailure(key, null, htl, htl, -1, source); return true; } else { if (logMINOR) Logger.minor(this, "Locked " + id); } // There are at least 2 threads that call this function. // DO NOT reuse the meta object, unless on a per-thread basis. // Object allocation is pretty cheap in modern Java anyway... // If we do reuse it, call reset(). BlockMetadata meta = new BlockMetadata(); KeyBlock block = node.fetch(key, false, false, false, false, meta); String rejectReason = nodeStats.shouldRejectRequest( !isSSK, false, isSSK, false, false, source, block != null && !meta.isOldBlock()); if (rejectReason != null) { // can accept 1 CHK request every so often, but not with SSKs because they aren't throttled so // won't sort out bwlimitDelayTime, which was the whole reason for accepting them when // overloaded... Logger.normal( this, "Rejecting " + (isSSK ? "SSK" : "CHK") + " request from " + source.getPeer() + " preemptively because " + rejectReason); Message rejected = DMT.createFNPRejectedOverload(id, true); try { source.sendAsync(rejected, null, ctr); } catch (NotConnectedException e) { Logger.normal( this, "Rejecting (overload) data request from " + source.getPeer() + ": " + e); } tag.setRejected(); node.unlockUID(id, isSSK, false, false, false, false, tag); // Do not tell failure table. // Otherwise an attacker can flood us with requests very cheaply and purge our // failure table even though we didn't accept any of them. return true; } nodeStats.reportIncomingRequestLocation(key.toNormalizedDouble()); // if(!node.lockUID(id)) return false; RequestHandler rh = new RequestHandler(m, source, id, node, htl, key, tag, block); node.executor.execute( rh, "RequestHandler for UID " + id + " on " + node.getDarknetPortNumber()); return true; }
private boolean handleOfferKey(Message m, PeerNode source) { Key key = (Key) m.getObject(DMT.KEY); byte[] authenticator = ((ShortBuffer) m.getObject(DMT.OFFER_AUTHENTICATOR)).getData(); node.failureTable.onOffer(key, source, authenticator); return true; }