Author: robert
Date: 2008-02-06 20:04:55 +0000 (Wed, 06 Feb 2008)
New Revision: 17609

Added:
   trunk/freenet/src/freenet/node/NetworkIDManager.java
Modified:
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/node/NodeDispatcher.java
Log:
initial SecretPing implementation (for discussion; untested & disabled)


Added: trunk/freenet/src/freenet/node/NetworkIDManager.java
===================================================================
--- trunk/freenet/src/freenet/node/NetworkIDManager.java                        
        (rev 0)
+++ trunk/freenet/src/freenet/node/NetworkIDManager.java        2008-02-06 
20:04:55 UTC (rev 17609)
@@ -0,0 +1,223 @@
+
+package freenet.node;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+import freenet.io.comm.DMT;
+import freenet.io.comm.DisconnectedException;
+import freenet.io.comm.Message;
+import freenet.io.comm.MessageFilter;
+import freenet.io.comm.NotConnectedException;
+
+import freenet.support.Logger;
+
+/**
+ * Handles the processing of challenge/response pings as well as the storage 
of the secrets pertaining thereto.
+ * It may (eventually) also handle the separation of peers into network peer 
groups.
+ * @author robert
+ * @created 2008-02-06
+ */
+class NetworkIDManager {
+       private static boolean disableSecretPings=true;
+       
+       private static final int SECRETPONG_TIMEOUT=10000;
+       private final boolean logMINOR;
+       
+       //Atomic: Locking for both via secretsByPeer
+       private final HashMap secretsByPeer=new HashMap();
+       private final HashMap secretsByUID=new HashMap();
+       
+       private final Node node;
+       
+       NetworkIDManager(Node node) {
+               this.node=node;
+               this.logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       }
+       
+       /**
+        * Stores the secret&uid contained in the message associated with the 
peer it comes from.
+        * "FNPStoreSecret" messages are *never* forwarded, they are only 
between peers as an alert
+        * that they may be asked for the secret from a third party.
+        */
+       public boolean handleStoreSecret(Message m) {
+               PeerNode pn=(PeerNode)m.getSource();
+               long uid = m.getLong(DMT.UID);
+               long secret = m.getLong(DMT.SECRET);
+               StoredSecret s=new StoredSecret(pn, uid, secret);
+               Logger.error(this, "Storing secret: "+s);
+               addOrReplaceSecret(s);
+               return true;
+       }
+       
+       public boolean handleSecretPing(Message m) {
+               try {
+                       return _handleSecretPing(m);
+               } catch (NotConnectedException e) {
+                       Logger.normal(this, "secretPing/not connected: "+e);
+                       return false;
+               }
+       }
+       
+       /*
+        @throws NotConnectedException if the *source* goes away
+        */
+       private boolean _handleSecretPing(Message m) throws 
NotConnectedException {
+               PeerNode source=(PeerNode)m.getSource();
+               long uid = m.getLong(DMT.UID);
+               short htl = m.getShort(DMT.HTL);
+               short dawnHtl=m.getShort(DMT.DAWN_HTL);
+               int counter=m.getInt(DMT.COUNTER);
+               
+               if (disableSecretPings || node.recentlyCompleted(uid)) {
+                       source.sendAsync(DMT.createFNPRejectedLoop(uid), null, 
0, null);
+               } else {
+                       StoredSecret match;
+                       //Yes, I know... it looks really weird sync.ing on a 
separate map...
+                       synchronized (secretsByPeer) {
+                               match=(StoredSecret)secretsByUID.get(new 
Long(uid));
+                       }
+                       if (match!=null) {
+                               //This is the node that the ping intends to 
reach, we will *not* forward it; but we might not respond positively either.
+                               //don't set the completed flag, we might reject 
it from one peer (too short a path) and accept it from another.
+                               if (htl > dawnHtl) {
+                                       
source.sendAsync(DMT.createFNPRejectedLoop(uid), null, 0, null);
+                               } else {
+                                       Logger.error(this, "Responding to 
"+source+" with "+match+" from "+match.peer);
+                                       
source.sendAsync(match.getSecretPong(counter+1), null, 0, null);
+                               }
+                       } else {
+                               //Set the completed flag immediately for 
determining reject loops rather than locking the uid.
+                               node.completed(uid);
+                               
+                               //Not a local match... forward
+                               double target=m.getDouble(DMT.TARGET_LOCATION);
+                               HashSet routedTo=new HashSet();
+                               HashSet notIgnored=new HashSet();
+                               while (true) {
+                                       PeerNode next;
+                                       
+                                       if (htl > dawnHtl && 
routedTo.isEmpty()) {
+                                               
next=node.peers.getRandomPeer(source);
+                                       } else {
+                                               
next=node.peers.closerPeer(source, routedTo, notIgnored, target, true, 
node.isAdvancedModeEnabled(), -1, null);
+                                       }
+                                       
+                                       if (next==null) {
+                                               //would be rnf... but this is a 
more exhaustive and lightweight search I suppose.
+                                               
source.sendAsync(DMT.createFNPRejectedLoop(uid), null, 0, null);
+                                               break;
+                                       }
+                                       
+                                       htl=next.decrementHTL(htl);
+                                       
+                                       if (htl<=0) {
+                                               //would be dnf if we were 
looking for data.
+                                               
source.sendAsync(DMT.createFNPRejectedLoop(uid), null, 0, null);
+                                               break;
+                                       }
+                                       
+                                       if (!source.isConnected()) {
+                                               throw new 
NotConnectedException("source gone away while forwarding");
+                                       }
+                                       
+                                       counter++;
+                                       routedTo.add(next);
+                                       try {
+                                               
next.sendAsync(DMT.createFNPSecretPing(uid, target, htl, dawnHtl, counter), 
null, 0, null);
+                                       } catch (NotConnectedException e) {
+                                               Logger.normal(this, next+" 
disconnected before secret-ping-forward");
+                                               continue;
+                                       }
+                                       
+                                       //wait for a reject or pong
+                                       MessageFilter mfPong = 
MessageFilter.create().setSource(next).setField(DMT.UID, 
uid).setTimeout(SECRETPONG_TIMEOUT).setType(DMT.FNPSecretPong);
+                                       MessageFilter mfRejectLoop = 
MessageFilter.create().setSource(next).setField(DMT.UID, 
uid).setTimeout(SECRETPONG_TIMEOUT).setType(DMT.FNPRejectedLoop);
+                                       Message msg;
+                                       
+                                       try {
+                                               msg = 
node.usm.waitFor(mfPong.or(mfRejectLoop), null);
+                                       } catch (DisconnectedException e) {
+                                               Logger.normal(this, next+" 
disconnected while waiting for a secret-pong");
+                                               continue;
+                                       }
+                                       
+                                       if (msg==null) {
+                                               Logger.error(this, "fatal 
timeout in waiting for secretpong from "+next);
+                                               //backoff?
+                                               break;
+                                       }
+                                       
+                                       if (msg.getSpec() == DMT.FNPSecretPong) 
{
+                                               int 
suppliedCounter=msg.getInt(DMT.COUNTER);
+                                               if (suppliedCounter>counter)
+                                                       counter=suppliedCounter;
+                                               long 
secret=msg.getLong(DMT.SECRET);
+                                               Logger.error(this, node+" 
forwarding apparently-successful secretpong response: "+counter+"/"+secret+" 
from "+next+" to "+source);
+                                               
source.sendAsync(DMT.createFNPSecretPong(uid, counter, secret), null, 0, null);
+                                               break;
+                                       }
+                                       
+                                       if (msg.getSpec() == 
DMT.FNPRejectedLoop) {
+                                               if (logMINOR) 
Logger.minor(this, "secret ping (reject/loop): "+source+" -> "+next);
+                                               continue;
+                                       }
+                                       
+                                       Logger.error(this, "unexpected message 
type: "+msg);
+                                       break;
+                               }
+                       }
+                       //unlockUID()
+               }
+               return true;
+       }
+       
+       //FIXME: This needs to be wired in.
+       public void onDisconnect(PeerNode pn) {
+               synchronized (secretsByPeer) {
+                       StoredSecret s=(StoredSecret)secretsByPeer.get(pn);
+                       if (s!=null) {
+                               //???: Might it still be valid to respond to 
secret pings when the neighbor requesting it has disconnected? (super-secret 
ping?)
+                               Logger.error(this, "Removing on disconnect: 
"+s);
+                               removeSecret(s);
+                       }
+               }
+       }
+       
+       private void addOrReplaceSecret(StoredSecret s) {
+               synchronized (secretsByPeer) {
+                       StoredSecret 
prev=(StoredSecret)secretsByPeer.get(s.peer);
+                       if (prev!=null) {
+                               Logger.normal(this, "Removing on replacement: 
"+s);
+                               removeSecret(prev);
+                       }
+                       //Need to remember by peer (so we can remove it on 
disconnect)
+                       //Need to remember by uid (so we can respond quickly to 
arbitrary requests).
+                       secretsByPeer.put(s.peer, s);
+                       secretsByUID.put(new Long(s.uid), s);
+               }
+       }
+       
+       private void removeSecret(StoredSecret s) {
+               //synchronized (secretsByPeer) in calling functions
+               secretsByPeer.remove(s);
+               secretsByUID.remove(s);
+       }
+       
+       private static final class StoredSecret {
+               PeerNode peer;
+               long uid;
+               long secret;
+               StoredSecret(PeerNode peer, long uid, long secret) {
+                       this.peer=peer;
+                       this.uid=uid;
+                       this.secret=secret;
+               }
+               public String toString() {
+                       return "Secret("+uid+"/"+secret+")";
+               }
+               Message getSecretPong(int counter) {
+                       return DMT.createFNPSecretPong(uid, counter, secret);
+               }
+       }
+}
\ No newline at end of file

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2008-02-06 19:43:39 UTC (rev 
17608)
+++ trunk/freenet/src/freenet/node/Node.java    2008-02-06 20:04:55 UTC (rev 
17609)
@@ -189,6 +189,7 @@

        /** Stats */
        public final NodeStats nodeStats;
+       public final NetworkIDManager netid;

        /** Config object for the whole node. */
        public final PersistentConfig config;
@@ -1445,6 +1446,8 @@

                clientCore = new NodeClientCore(this, config, nodeConfig, 
nodeDir, getDarknetPortNumber(), sortOrder, oldThrottleFS == null ? null : 
oldThrottleFS.subset("RequestStarters"), oldConfig, fproxyConfig, toadlets);

+               netid = new NetworkIDManager(this);
+                
                nodeConfig.register("disableHangCheckers", false, sortOrder++, 
true, false, "Node.disableHangCheckers", "Node.disableHangCheckersLong", new 
BooleanCallback() {

                        public boolean get() {

Modified: trunk/freenet/src/freenet/node/NodeDispatcher.java
===================================================================
--- trunk/freenet/src/freenet/node/NodeDispatcher.java  2008-02-06 19:43:39 UTC 
(rev 17608)
+++ trunk/freenet/src/freenet/node/NodeDispatcher.java  2008-02-06 20:04:55 UTC 
(rev 17609)
@@ -74,6 +74,10 @@
                                // Ignore
                        }
                        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);
@@ -441,6 +445,7 @@
                }
        }

+       //FIXME: PLEASE! When are these contexts ever cleaned up!!! 
Memory-leak-per-ping!
        final Hashtable routedContexts = new Hashtable();

        static class RoutedContext {


Reply via email to