Author: toad
Date: 2008-03-13 00:29:52 +0000 (Thu, 13 Mar 2008)
New Revision: 18498

Modified:
   trunk/freenet/src/freenet/node/PeerNode.java
Log:
Connection race: A can have key 1 as current and key 2 as previous, B can have 
key 2 as current and key 1 as previous.
When a rekey happens, we lose connectivity, get unmatchable packets.
If prev and current are created within a short period of one another, swap them 
over depending on whose key (hashed with both boot IDs and the word "test") is 
numerically larger.
Fixes unmatchable packet messages in simulations. Should improve connectivity.

Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java        2008-03-13 00:26:24 UTC 
(rev 18497)
+++ trunk/freenet/src/freenet/node/PeerNode.java        2008-03-13 00:29:52 UTC 
(rev 18498)
@@ -10,6 +10,7 @@
 import java.math.BigInteger;
 import java.net.MalformedURLException;
 import java.net.UnknownHostException;
+import java.security.MessageDigest;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -252,7 +253,18 @@
        private long connectedTime;
        /** The status of this peer node in terms of Node.PEER_NODE_STATUS_* */
        public int peerNodeStatus = PeerManager.PEER_NODE_STATUS_DISCONNECTED;
+       
+       static final int CHECK_FOR_SWAPPED_TRACKERS_INTERVAL = 
FNPPacketMangler.SESSION_KEY_REKEYING_INTERVAL / 30;

+       static final byte[] TEST_AS_BYTES;
+       static {
+               try {
+                       TEST_AS_BYTES = "test".getBytes("UTF-8");
+               } catch (UnsupportedEncodingException e) {
+                       throw new Error(e);
+               }
+       }
+       
        /** Holds a String-Long pair that shows which message types (as name) 
have been send to this peer. */
        private final Hashtable localNodeSentMessageTypes = new Hashtable();
        /** Holds a String-Long pair that shows which message types (as name) 
have been received by this peer. */
@@ -1832,9 +1844,13 @@
                                prev = currentTracker;
                                previousTracker = prev;
                                currentTracker = newTracker;
-                               unverifiedTracker = null;
+                               // Keep the old unverified tracker.
+                               // In case of a race condition (two setups 
between A and B complete at the same time),
+                               // we might want to keep the unverified tracker 
rather than the previous tracker.
                                neverConnected = false;
                                peerAddedTime = 0;  // don't store anymore
+                               maybeSwapTrackers();
+                               prev = previousTracker;
                        }
                        ctx = null;
                        isRekeying = false;
@@ -1874,6 +1890,48 @@
                return true;
        }

+       /**
+        * Resolve race conditions where two connection setups between two 
peers complete simultaneously.
+        * Swap prev and current if:
+        * - There is a very short period between their respective creations.
+        * - Current's hashcode (including the key, the word "test", and the 
xor of the boot IDs) is
+        * greater than previous's.
+        */
+       private synchronized void maybeSwapTrackers() {
+               if(previousTracker != null && (!previousTracker.isDeprecated()) 
&&
+                               Math.abs(currentTracker.createdTime - 
previousTracker.createdTime) < CHECK_FOR_SWAPPED_TRACKERS_INTERVAL) {
+                       // Swap prev and current iff H(new key) > H(old key).
+                       // To deal with race conditions (node A gets 1 current 
2 prev, node B gets 2 current 1 prev; when we rekey we lose data and cause 
problems).
+                       
+                       // FIXME since this is a key dependancy, it needs to be 
looked at.
+                       // However, an attacker cannot get this far without 
knowing the privkey, so it's unlikely to be an issue.
+                       
+                       MessageDigest md = SHA256.getMessageDigest();
+                       md.update(currentTracker.sessionKey);
+                       md.update(TEST_AS_BYTES);
+                       md.update(Fields.longToBytes(bootID ^ node.bootID));
+                       int curHash = Fields.hashCode(md.digest());
+                       md.reset();
+                       
+                       md.update(previousTracker.sessionKey);
+                       md.update(TEST_AS_BYTES);
+                       md.update(Fields.longToBytes(bootID ^ node.bootID));
+                       int prevHash = Fields.hashCode(md.digest());
+                       
+                       if(prevHash < curHash) {
+                               // Swap over
+                               KeyTracker temp = previousTracker;
+                               previousTracker = currentTracker;
+                               currentTracker = temp;
+                               if(logMINOR) Logger.minor(this, "Swapped 
KeyTracker's on "+this+" cur "+currentTracker+" prev "+previousTracker);
+                       } else {
+                               if(logMINOR) Logger.minor(this, "Not swapping 
KeyTracker's on "+this+" cur "+currentTracker+" prev "+previousTracker);
+                       }
+               } else {
+                       if(logMINOR) Logger.minor(this, "Not swapping 
KeyTracker's, no previousTracker or deprecated or not recent enough");
+               }
+       }
+
        public long getBootID() {
                return bootID;
        }
@@ -1994,14 +2052,15 @@
                                        Logger.minor(this, "Promoting 
unverified tracker " + tracker + " for " + getPeer());
                                completelyDeprecatedTracker = previousTracker;
                                previousTracker = currentTracker;
-                               if(previousTracker != null)
-                                       previousTracker.deprecated();
                                currentTracker = unverifiedTracker;
                                unverifiedTracker = null;
                                isConnected = true;
                                neverConnected = false;
                                peerAddedTime = 0;  // don't store anymore
                                ctx = null;
+                               maybeSwapTrackers();
+                               if(previousTracker != null)
+                                       previousTracker.deprecated();
                        } else
                                return;
                }


Reply via email to