/** Handle an FNPRoutedRejected message. */
 private boolean handleRoutedRejected(Message m) {
   long id = m.getLong(DMT.UID);
   Long lid = Long.valueOf(id);
   RoutedContext rc = routedContexts.get(lid);
   if (rc == null) {
     // Gah
     Logger.error(this, "Unrecognized FNPRoutedRejected");
     return false; // locally originated??
   }
   short htl = rc.lastHtl;
   if (rc.source != null) htl = rc.source.decrementHTL(htl);
   short ohtl = m.getShort(DMT.HTL);
   if (ohtl < htl) htl = ohtl;
   if (htl == 0) {
     // Equivalent to DNF.
     // Relay.
     if (rc.source != null) {
       try {
         rc.source.sendAsync(
             DMT.createFNPRoutedRejected(id, (short) 0), null, nodeStats.routedMessageCtr);
       } catch (NotConnectedException e) {
         // Ouch.
         Logger.error(this, "Unable to relay probe DNF: peer disconnected: " + rc.source);
       }
     }
   } else {
     // Try routing to the next node
     forward(rc.msg, id, rc.source, htl, rc.msg.getDouble(DMT.TARGET_LOCATION), rc, rc.identity);
   }
   return true;
 }
  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;
  }
 /** Prepare a routed-to-node message for forwarding. */
 private Message preForward(Message m, short newHTL) {
   m.set(DMT.HTL, newHTL); // update htl
   if (m.getSpec() == DMT.FNPRoutedPing) {
     int x = m.getInt(DMT.COUNTER);
     x++;
     m.set(DMT.COUNTER, x);
   }
   return m;
 }
 private boolean forward(
     Message m,
     long id,
     PeerNode pn,
     short htl,
     double target,
     RoutedContext ctx,
     byte[] targetIdentity) {
   if (logMINOR) Logger.minor(this, "Should forward");
   // Forward
   m = preForward(m, htl);
   while (true) {
     PeerNode next = node.peers.getByIdentity(targetIdentity);
     if (next != null && !next.isConnected()) {
       Logger.error(this, "Found target but disconnected!: " + next);
       next = null;
     }
     if (next == null)
       next =
           node.peers.closerPeer(
               pn, ctx.routedTo, target, true, node.isAdvancedModeEnabled(), -1, null, null, htl);
     if (logMINOR) Logger.minor(this, "Next: " + next + " message: " + m);
     if (next != null) {
       // next is connected, or at least has been => next.getPeer() CANNOT be null.
       if (logMINOR)
         Logger.minor(this, "Forwarding " + m.getSpec() + " to " + next.getPeer().getPort());
       ctx.addSent(next);
       try {
         next.sendAsync(m, null, nodeStats.routedMessageCtr);
       } catch (NotConnectedException e) {
         continue;
       }
     } else {
       if (logMINOR)
         Logger.minor(
             this, "Reached dead end for " + m.getSpec() + " on " + node.getDarknetPortNumber());
       // Reached a dead end...
       Message reject = DMT.createFNPRoutedRejected(id, htl);
       if (pn != null)
         try {
           pn.sendAsync(reject, null, nodeStats.routedMessageCtr);
         } catch (NotConnectedException e) {
           Logger.error(this, "Cannot send reject message back to source " + pn);
           return true;
         }
     }
     return true;
   }
 }
 public void onRNF(
     short htl,
     double nearest,
     double best,
     short counter,
     short uniqueCounter,
     short linearCounter)
     throws NotConnectedException {
   Message rnf = DMT.createFNPRouteNotFound(uid, htl);
   Message sub =
       DMT.createFNPRHReturnSubMessage(
           nearest, best, counter, uniqueCounter, linearCounter, "rnf");
   rnf.addSubMessage(sub);
   source.sendAsync(rnf, null, sender);
 }
 public void onReceivedRejectOverload(
     double nearest,
     double best,
     short counter,
     short uniqueCounter,
     short linearCounter,
     String reason)
     throws NotConnectedException {
   Message ro = DMT.createFNPRejectedOverload(uid, false, false, false);
   Message sub =
       DMT.createFNPRHReturnSubMessage(
           nearest, best, counter, uniqueCounter, linearCounter, reason);
   ro.addSubMessage(sub);
   source.sendAsync(ro, null, sender);
 }
 /**
  * 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);
     }
   }
 }
 /**
  * Deal with a routed-to-node message that landed on this node. This is where
  * message-type-specific code executes.
  *
  * @param m
  * @return
  */
 private boolean dispatchRoutedMessage(Message m, PeerNode src, long id) {
   if (m.getSpec() == DMT.FNPRoutedPing) {
     if (logMINOR) Logger.minor(this, "RoutedPing reached other side! (" + id + ")");
     int x = m.getInt(DMT.COUNTER);
     Message reply = DMT.createFNPRoutedPong(id, x);
     if (logMINOR) Logger.minor(this, "Replying - counter = " + x + " for " + id);
     try {
       src.sendAsync(reply, null, nodeStats.routedMessageCtr);
     } catch (NotConnectedException e) {
       if (logMINOR)
         Logger.minor(this, "Lost connection replying to " + m + " in dispatchRoutedMessage");
     }
     return true;
   }
   return false;
 }
 RoutedContext(Message msg, PeerNode source, byte[] identity) {
   createdTime = accessTime = System.currentTimeMillis();
   this.source = source;
   routedTo = new HashSet<PeerNode>();
   this.msg = msg;
   lastHtl = msg.getShort(DMT.HTL);
   this.identity = identity;
 }
  /**
   * 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 static long getSecretPingResponse(Node source, PeerNode pathway, long uid)
      throws DisconnectedException {
    // wait for a reject or pong
    MessageFilter mfPong =
        MessageFilter.create()
            .setSource(pathway)
            .setField(DMT.UID, uid)
            .setTimeout(SECRETPONG_TIMEOUT)
            .setType(DMT.FNPSecretPong);
    MessageFilter mfRejectLoop =
        MessageFilter.create()
            .setSource(pathway)
            .setField(DMT.UID, uid)
            .setTimeout(SECRETPONG_TIMEOUT)
            .setType(DMT.FNPRejectedLoop);
    Message msg = source.getUSM().waitFor(mfPong.or(mfRejectLoop), null);

    if (msg == null) {
      Logger.error(
          source, "fatal timeout in waiting for secretpong from " + getPortNumber(pathway));
      return -2;
    }

    if (msg.getSpec() == DMT.FNPSecretPong) {
      int suppliedCounter = msg.getInt(DMT.COUNTER);
      long secret = msg.getLong(DMT.SECRET);
      Logger.normal(source, "got secret, counter=" + suppliedCounter);
      return secret;
    }

    if (msg.getSpec() == DMT.FNPRejectedLoop) {
      Logger.error(
          source,
          "top level secret ping should not reject!: "
              + getPortNumber(source)
              + " -> "
              + getPortNumber(pathway));
      return -1;
    }

    return -3;
  }
 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);
 }
 private boolean handleProbeRequest(Message m, PeerNode source) {
   long id = m.getLong(DMT.UID);
   if (node.recentlyCompleted(id)) {
     Message rejected = DMT.createFNPRejectedLoop(id);
     try {
       source.sendAsync(rejected, null, node.nodeStats.probeRequestCtr);
     } catch (NotConnectedException e) {
       Logger.normal(this, "Rejecting probe request from " + source.getPeer() + ": " + e);
     }
     return true;
   }
   // Lets not bother with full lockUID, just add it to the recently completed list.
   node.completed(id);
   // SSKs don't fix bwlimitDelayTime so shouldn't be accepted when overloaded.
   if (source.shouldRejectProbeRequest()) {
     Logger.normal(this, "Rejecting probe request from " + source.getPeer());
     Message rejected = DMT.createFNPRejectedOverload(id, true);
     try {
       source.sendAsync(rejected, null, node.nodeStats.probeRequestCtr);
     } catch (NotConnectedException e) {
       Logger.normal(
           this, "Rejecting (overload) insert request from " + source.getPeer() + ": " + e);
     }
     return true;
   }
   double target = m.getDouble(DMT.TARGET_LOCATION);
   if (target > 1.0 || target < 0.0) {
     Logger.normal(
         this, "Rejecting invalid (target=" + target + ") probe request from " + source.getPeer());
     Message rejected = DMT.createFNPRejectedOverload(id, true);
     try {
       source.sendAsync(rejected, null, node.nodeStats.probeRequestCtr);
     } catch (NotConnectedException e) {
       Logger.normal(
           this, "Rejecting (invalid) insert request from " + source.getPeer() + ": " + e);
     }
     return true;
   }
   ProbeRequestHandler.start(m, source, node, target);
   return true;
 }
  private static boolean getAck(Node source, PeerNode pathway, long uid)
      throws DisconnectedException {
    // wait for an accepted
    MessageFilter mfAccepted =
        MessageFilter.create()
            .setSource(pathway)
            .setField(DMT.UID, uid)
            .setTimeout(SECRETPONG_TIMEOUT)
            .setType(DMT.FNPAccepted);
    Message msg = source.getUSM().waitFor(mfAccepted, null);

    if (msg == null) {
      return false;
    }

    if (msg.getSpec() == DMT.FNPAccepted) {
      return true;
    }

    Logger.error(source, "got " + msg);
    return false;
  }
 static void start(Message m, PeerNode source, Node n, double target) {
   long uid = m.getLong(DMT.UID);
   double nearestLoc = m.getDouble(DMT.NEAREST_LOCATION);
   double best = m.getDouble(DMT.BEST_LOCATION);
   short htl = m.getShort(DMT.HTL);
   ProbeRequestSender sender =
       new ProbeRequestSender(target, htl, uid, n, nearestLoc, source, best);
   ProbeRequestHandler handler = new ProbeRequestHandler(source, uid, sender);
   sender.addListener(handler);
   PeerNode[] peers = n.peers.connectedPeers;
   Message accepted = DMT.createFNPAccepted(uid);
   Message trace =
       DMT.createFNPRHProbeTrace(
           uid,
           sender.getNearestLoc(),
           sender.getBest(),
           htl,
           (short) 1,
           (short) 1,
           n.getLocation(),
           n.swapIdentifier,
           LocationManager.extractLocs(peers, true),
           LocationManager.extractUIDs(peers),
           (short) 0,
           (short) 1,
           "",
           source.swapIdentifier);
   try {
     source.sendAsync(accepted, null, sender);
     source.sendAsync(trace, null, sender);
   } catch (NotConnectedException e) {
     // We completed(id), rather than locking it, so we don't need to unlock.
     return; // So all we need to do is not start the sender.
   }
   sender.start();
 }
 boolean handleRoutedReply(Message m) {
   long id = m.getLong(DMT.UID);
   if (logMINOR) Logger.minor(this, "Got reply: " + m);
   Long lid = Long.valueOf(id);
   RoutedContext ctx = routedContexts.get(lid);
   if (ctx == null) {
     Logger.error(this, "Unrecognized routed reply: " + m);
     return false;
   }
   PeerNode pn = ctx.source;
   if (pn == null) return false;
   try {
     pn.sendAsync(m, null, nodeStats.routedMessageCtr);
   } catch (NotConnectedException e) {
     if (logMINOR) Logger.minor(this, "Lost connection forwarding " + m + " to " + pn);
   }
   return true;
 }
 private boolean handleAnnounceRequest(Message m, PeerNode source) {
   long uid = m.getLong(DMT.UID);
   OpennetManager om = node.getOpennet();
   if (om == null || !source.canAcceptAnnouncements()) {
     Message msg = DMT.createFNPOpennetDisabled(uid);
     try {
       source.sendAsync(msg, null, node.nodeStats.announceByteCounter);
     } catch (NotConnectedException e) {
       // Ok
     }
     return true;
   }
   if (node.recentlyCompleted(uid)) {
     Message msg = DMT.createFNPRejectedLoop(uid);
     try {
       source.sendAsync(msg, null, node.nodeStats.announceByteCounter);
     } catch (NotConnectedException e) {
       // Ok
     }
     return true;
   }
   boolean success = false;
   // No way to check whether it's actually running atm, so lets report it to the completed list
   // immediately.
   // FIXME we should probably keep a list!
   node.completed(uid);
   try {
     if (!source.shouldAcceptAnnounce(uid)) {
       Message msg = DMT.createFNPRejectedOverload(uid, true);
       try {
         source.sendAsync(msg, null, node.nodeStats.announceByteCounter);
       } catch (NotConnectedException e) {
         // Ok
       }
       return true;
     }
     AnnounceSender sender = new AnnounceSender(m, uid, source, om, node);
     node.executor.execute(sender, "Announcement sender for " + uid);
     success = true;
     return true;
   } finally {
     if (!success) source.completedAnnounce(uid);
   }
 }
 private boolean handleUptime(Message m, PeerNode source) {
   byte uptime = m.getByte(DMT.UPTIME_PERCENT_48H);
   source.setUptime(uptime);
   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;
 }
  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 handleTime(Message m, PeerNode source) {
   long delta = m.getLong(DMT.TIME) - System.currentTimeMillis();
   source.setTimeDelta(delta);
   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 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;
 }