diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
index 608d36ba89b9c5b8abd7e4d3fc121ea27cd10f08..5e0a8702e410b5c43d30ef18aaffbd6ebdb47faf 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java
@@ -159,9 +159,28 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable {
         }
     }
 
+    private static final String PROP_HANDLER_COUNT = "i2ptunnel.blockingHandlerCount";
+    private static final int DEFAULT_HANDLER_COUNT = 10;
+    
+    protected int getHandlerCount() { 
+        int rv = DEFAULT_HANDLER_COUNT;
+        String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT);
+        if (cnt != null) {
+            try {
+                rv = Integer.parseInt(cnt);
+                if (rv <= 0)
+                    rv = DEFAULT_HANDLER_COUNT;
+            } catch (NumberFormatException nfe) {
+                rv = DEFAULT_HANDLER_COUNT;
+            }
+        }
+        return rv;
+    }
+    
     public void run() {
             I2PServerSocket i2pss = sockMgr.getServerSocket();
-            for (int i = 0; i < 5; i++) {
+            int handlers = getHandlerCount();
+            for (int i = 0; i < handlers; i++) {
                 I2PThread handler = new I2PThread(new Handler(i2pss), "Handle Server " + i);
                 handler.start();
             }
diff --git a/core/java/src/net/i2p/CoreVersion.java b/core/java/src/net/i2p/CoreVersion.java
index 17d8502055b12e554733fa44634e32b48e20bf04..7d72508d3545653739966d0c9c67f4154f18e6a0 100644
--- a/core/java/src/net/i2p/CoreVersion.java
+++ b/core/java/src/net/i2p/CoreVersion.java
@@ -14,8 +14,8 @@ package net.i2p;
  *
  */
 public class CoreVersion {
-    public final static String ID = "$Revision: 1.35 $ $Date: 2005/04/20 15:14:20 $";
-    public final static String VERSION = "0.6";
+    public final static String ID = "$Revision: 1.36 $ $Date: 2005/07/27 14:04:49 $";
+    public final static String VERSION = "0.6.0.1";
 
     public static void main(String args[]) {
         System.out.println("I2P Core version: " + VERSION);
diff --git a/history.txt b/history.txt
index 76b8cda67c315e57ad5c033ba464b8b8e3b26204..6e75abf083608e696fdc60010c5351afa3003435 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,11 @@
-$Id: history.txt,v 1.220 2005/08/01 08:35:11 duck Exp $
+$Id: history.txt,v 1.221 2005/08/01 22:26:51 duck Exp $
+
+* 2005-08-03  0.6.0.1 released
+
+2005-08-03  jrandom
+    * Backed out an inadvertant change to the netDb store redundancy factor.
+    * Verify tunnel participant caching.
+    * Logging cleanup
 
 2005-08-01  duck
     * Update IzPack to 3.7.2 (build 2005.04.22). This fixes bug #82.
diff --git a/initialNews.xml b/initialNews.xml
index b6c585ea781083215c5a93f4150d9e4ca10da9f6..b15b7694b5c5330bb523a92bafd806e3af891fb9 100644
--- a/initialNews.xml
+++ b/initialNews.xml
@@ -1,5 +1,5 @@
-<i2p.news date="$Date: 2005/07/27 14:06:11 $">
- <i2p.release version="0.6" date="2005/07/27" minVersion="0.6"
+<i2p.news date="$Date: 2005/07/27 15:16:44 $">
+ <i2p.release version="0.6.0.1" date="2005/07/27" minVersion="0.6"
               anonurl="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/i2pupdate.sud"
               publicurl="http://dev.i2p.net/i2p/i2pupdate.sud"
               anonannouncement="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/pipermail/i2p/2005-July/000824.html" 
diff --git a/installer/install.xml b/installer/install.xml
index 4f4e9670fcfca86f7b669b30decdd9aea3b3a7af..cc023f472c61fad375caca5c6e15e9bde8d66854 100644
--- a/installer/install.xml
+++ b/installer/install.xml
@@ -4,7 +4,7 @@
 
     <info>
         <appname>i2p</appname>
-        <appversion>0.6</appversion>
+        <appversion>0.6.0.1</appversion>
         <authors>
             <author name="I2P" email="support@i2p.net"/>
         </authors>
diff --git a/news.xml b/news.xml
index 87bc3e5b7311bc7bedeadf90cd4b0a75dbdfc67a..193f4ad4f5f04c9066edb3318320630eab053f83 100644
--- a/news.xml
+++ b/news.xml
@@ -1,5 +1,5 @@
-<i2p.news date="$Date: 2005/07/27 15:16:44 $">
- <i2p.release version="0.6" date="2005/07/27" minVersion="0.6"
+<i2p.news date="$Date: 2005/07/28 15:33:27 $">
+ <i2p.release version="0.6.0.1" date="2005/07/27" minVersion="0.6"
               anonurl="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/i2p/i2pupdate.sud"
               publicurl="http://dev.i2p.net/i2p/i2pupdate.sud"
               anonannouncement="http://i2p/NF2RLVUxVulR3IqK0sGJR0dHQcGXAzwa6rEO4WAWYXOHw-DoZhKnlbf1nzHXwMEJoex5nFTyiNMqxJMWlY54cvU~UenZdkyQQeUSBZXyuSweflUXFqKN-y8xIoK2w9Ylq1k8IcrAFDsITyOzjUKoOPfVq34rKNDo7fYyis4kT5bAHy~2N1EVMs34pi2RFabATIOBk38Qhab57Umpa6yEoE~rbyR~suDRvD7gjBvBiIKFqhFueXsR2uSrPB-yzwAGofTXuklofK3DdKspciclTVzqbDjsk5UXfu2nTrC1agkhLyqlOfjhyqC~t1IXm-Vs2o7911k7KKLGjB4lmH508YJ7G9fLAUyjuB-wwwhejoWqvg7oWvqo4oIok8LG6ECR71C3dzCvIjY2QcrhoaazA9G4zcGMm6NKND-H4XY6tUWhpB~5GefB3YczOqMbHq4wi0O9MzBFrOJEOs3X4hwboKWANf7DT5PZKJZ5KorQPsYRSq0E3wSOsFCSsdVCKUGsAAAA/pipermail/i2p/2005-April/000824.html" 
diff --git a/router/doc/udp.html b/router/doc/udp.html
index bafe3411d2a1df7e0924e2d5bffc5a24e83c2bbe..10c0c30eef19e55f18a0b45fda85e90cef63fbce 100644
--- a/router/doc/udp.html
+++ b/router/doc/udp.html
@@ -1,4 +1,4 @@
-<code>$Id: udp.html,v 1.13 2005/05/01 15:08:08 jrandom Exp $</code>
+<code>$Id: udp.html,v 1.14 2005/07/27 14:04:07 jrandom Exp $</code>
 
 <h1>Secure Semireliable UDP (SSU)</h1>
 <b>DRAFT</b>
@@ -21,8 +21,8 @@ indirect address, for using a third party to introduce the peer.
 There is no restriction on the number of addresses a peer may have.</p>
 
 <pre>
-    Direct: udp://host:port/introKey
-  Indirect: udp://tag@relayhost:port/relayIntroKey/targetIntroKey
+    Direct: ssu://host:port/introKey[?opts=[A-Z]*]
+  Indirect: ssu://tag@relayhost:port/relayIntroKey/targetIntroKey[?opts=[A-Z]*]
 </pre>
 
 <p>These introduction keys are delivered through an external channel 
@@ -36,6 +36,10 @@ located.  In addition, the peer establishing the connection must
 already know the public keys of the peer they are connecting to (but
 not necessary to any intermediary relay peer).</p>
 
+<p>Each of the addresses may also expose a series of options - special
+capabilities of that particular peer.  For a list of available
+capabilities, see <a href="#capabilities">below</a>.</p>
+
 <h2><a name="header">Header</a></h2>
 
 <p>All UDP datagrams begin with a MAC and an IV, followed by a variable
@@ -433,6 +437,41 @@ bits 10-23: fragment size</pre></li>
  +----+----+----+----+----+----+----+----+
 </pre>
 
+<h3><a name="peerTest">PeerTest (type 7)</a></h3>
+<table border="1">
+<tr><td align="right" valign="top"><b>Peer:</b></td>
+    <td><a href="#peerTesting">Any</a></td></tr>
+<tr><td align="right" valign="top"><b>Data:</b></td>
+    <td><ul>
+        <li>4 byte nonce</li>
+        <li>1 byte IP address size</li>
+        <li>that many byte representation of Alice's IP address</li>
+        <li>2 byte port number</li>
+        <li>Alice's introduction key</li>
+        <li>N bytes, currently uninterpreted</li>
+	</ul></td></tr>
+<tr><td align="right" valign="top"><b>Key used:</b></td>
+    <td>introKey (or sessionKey if the connection has already been established)</td></tr>
+</table>
+
+<pre>
+ +----+----+----+----+----+----+----+----+
+ |    test nonce     |size| that many    |
+ +----+----+----+----+----+              |
+ |bytes making up Alice's IP address     |
+ |----+----+----+----+----+----+----+----+
+ | Port (A)| Alice or Charlie's          |
+ +----+----+                             |
+ | introduction key (Alice's is sent to  |
+ | Bob and Charlie, while Charlie's is   |                                      |
+ | sent to Alice)                        |
+ |         +----+----+----+----+----+----+
+ |         | arbitrary amount of         |
+ |----+----+                             |
+ | uninterpreted data                    |
+ +----+----+----+----+----+----+----+----+
+</pre>
+
 <h2><a name="congestioncontrol">Congestion control</a></h2>
 
 <p>SSU's need for only semireliable delivery, TCP-friendly operation,
@@ -524,6 +563,74 @@ are not in any particular order - in fact, they are likely to be
 entirely random.  The SSU layer makes no attempt at messageId 
 replay prevention - higher layers should take that into account.</p>
 
+<h2><a name="peerTesting">Peer testing</a></h2>
+
+<p>The automation of collaborative reachability testing for peers is
+enabled by a sequence of PeerTest messages.  With its proper 
+execution, a peer will be able to determine their own reachability
+and may update its behavior accordingly.  The testing process is 
+quite simple:</p>
+
+<pre>
+        Alice                  Bob                  Charlie
+    PeerTest ------------------&gt;
+         &lt;-------------PeerTest  PeerTest-------------&gt;
+         &lt;------------------------------------------PeerTest
+    PeerTest------------------------------------------&gt;
+         &lt;------------------------------------------PeerTest
+</pre>
+
+<p>Each of the PeerTest messages carry a nonce identifying the
+test series itself, as initialized by Alice.  If Alice doesn't 
+get a particular message that she expects, she will retransmit
+accordingly, and based upon the data received or the messages
+missing, she will know her reachability.  The various end states
+that may be reached are as follows:</p>
+
+<ul>
+<li>If she doesn't receive a response from Bob, she will retransmit
+up to a certain number of times, but if no response ever arrives,
+she will know that her firewall or NAT is somehow misconfigured, 
+rejecting all inbound UDP packets even in direct response to an
+outbound packet.  Alternately, Bob may be down.</li>
+
+<li>If Alice doesn't receive a PeerTest message with the 
+expected nonce from a third party (Charlie), she will retransmit
+her initial request to Bob up to a certain number of times, even
+if she has received Bob's reply already.  If Charlie's first message 
+still doesn't get through but Bob's does, she knows that she is
+behind a NAT or firewall that is rejecting unsolicited connection
+attempts and that port forwarding is not operating properly (the
+IP and port that Bob offered up should be forwarded).</li>
+
+<li>If Alice receives Bob's PeerTest message and both of Charlie's
+PeerTest messages but the enclosed IP and port numbers in Bob's 
+and Charlie's second messages don't match, she knows that she is 
+behind a symmetric NAT, rewriting all of her outbound packets with
+different 'from' ports for each peer contacted.  She will need to
+explicitly forward a port and always have that port exposed for 
+remote connectivity, ignoring further port discovery.</li>
+
+<li>If Alice receives Charlie's first message but not his second,
+she will retransmit her PeerTest message to Charlie up to a 
+certain number of times, but if no response is received she knows
+that Charlie is either confused or no longer online.</li>
+</ul>
+
+<p>Alice should choose Bob arbitrarily from known peers who seem
+to be capable of participating in peer tests.  Bob in turn should
+choose Charlie arbitrarily from peers that he knows who seem to be
+capable of participating in peer tests and who are on a different
+IP from both Bob and Alice.  If the first error condition occurs
+(Alice doesn't get PeerTest messages from Bob), Alice may decide
+to designate a new peer as Bob and try again with a different nonce.</p>
+
+<p>Alice's introduction key is included in all of the PeerTest 
+messages so that she doesn't need to already have an established
+session with Bob and so that Charlie can contact her without knowing
+any additional information.  Alice may go on to establish a session
+with either Bob or Charlie, but it is not required.</p>
+
 <h2><a name="messageSequences">Message sequences</a></h2>
 
 <h3><a name="establishDirect">Connection establishment (direct)</a></h3>
@@ -594,3 +701,16 @@ replay prevention - higher layers should take that into account.</p>
  |                                       |
  +----+----+----+----+----+----+----+----+
 </pre> 
+
+<h2><a name="capabilities">Peer capabilities</a></h2>
+
+<dl>
+ <dt>A</dt>
+ <dd>If the peer address contains the 'A' capability, that means 
+     they are willing and able to participate in peer tests as
+     a 'Bob' or 'Charlie'.</dd>
+ <dt>B</dt>
+ <dd>If the peer address contains the 'B' capability, that means
+     they are willing and able to serve as an introducer - serving
+     as a Bob for an otherwise unreachable Alice.</dd>
+</dl>
\ No newline at end of file
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index e42bdcc7439daf525838fbac6961c9c36faeb5a0..54046c1f292792c90d3119cf97a6b728a408ed48 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
  *
  */
 public class RouterVersion {
-    public final static String ID = "$Revision: 1.209 $ $Date: 2005/07/27 14:03:43 $";
-    public final static String VERSION = "0.6";
-    public final static long BUILD = 1;
+    public final static String ID = "$Revision: 1.210 $ $Date: 2005/07/31 16:35:27 $";
+    public final static String VERSION = "0.6.0.1";
+    public final static long BUILD = 0;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION);
         System.out.println("Router ID: " + RouterVersion.ID);
diff --git a/router/java/src/net/i2p/router/message/SendMessageDirectJob.java b/router/java/src/net/i2p/router/message/SendMessageDirectJob.java
index 5ab565ad706c6cefc1bc8c28df85bc8fb7195425..10a9bf13b9ba03d37f82b7694d06ee1f0cff9ddc 100644
--- a/router/java/src/net/i2p/router/message/SendMessageDirectJob.java
+++ b/router/java/src/net/i2p/router/message/SendMessageDirectJob.java
@@ -74,8 +74,8 @@ public class SendMessageDirectJob extends JobImpl {
         long now = getContext().clock().now();
 
         if (_expiration < now) {
-            if (_log.shouldLog(Log.ERROR))
-                _log.error("Timed out sending message " + _message + " directly (expiration = " 
+            if (_log.shouldLog(Log.WARN))
+                _log.warn("Timed out sending message " + _message + " directly (expiration = " 
                            + new Date(_expiration) + ") to " + _targetHash.toBase64(), getAddedBy());
             if (_onFail != null)
                 getContext().jobQueue().addJob(_onFail);
diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
index 3d7c41d3a75714ddc4cec2161ccab324185f278e..ffdf5e90451cba5937a92224cecb70be0b0ad303 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
@@ -39,7 +39,7 @@ class StoreJob extends JobImpl {
     private PeerSelector _peerSelector;
 
     private final static int PARALLELIZATION = 3; // how many sent at a time
-    private final static int REDUNDANCY = 3; // we want the data sent to 6 peers
+    private final static int REDUNDANCY = 10; // we want the data sent to 10 peers
     /**
      * additionally send to 1 outlier(s), in case all of the routers chosen in our
      * REDUNDANCY set are attacking us by accepting DbStore messages but dropping
diff --git a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
index 743718b96ab36be10e6e073d863ab36441a0fac6..df137136d693e39ae3333a22f3962e60237ef863 100644
--- a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
+++ b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java
@@ -179,8 +179,8 @@ public class UDPReceiver {
                         if (_log.shouldLog(Log.INFO))
                             _log.info("Changing ports...");
                     } else {
-                        if (_log.shouldLog(Log.ERROR))
-                            _log.error("Error receiving", ioe);
+                        if (_log.shouldLog(Log.WARN))
+                            _log.warn("Error receiving", ioe);
                     }
                     packet.release();
                 }
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java
index 484cab14ec2328fa26088b3a4253df3046d3e041..aa7792f6b5d99e6c28397e0900b314d8110c65a1 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelParticipant.java
@@ -142,14 +142,18 @@ public class TunnelParticipant {
         }
         public String getName() { return "forward a tunnel message"; }
         public void runJob() {
-            RouterInfo ri = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
-            if (ri != null) {
-                _nextHopCache = ri;
-                send(_config, _msg, ri);
+            if (_nextHopCache != null) {
+                send(_config, _msg, _nextHopCache);
             } else {
-                if (_log.shouldLog(Log.ERROR))
-                    _log.error("Lookup the nextHop (" + _config.getSendTo().toBase64().substring(0,4) 
-                              + " failed!  where do we go for " + _config + "?  msg dropped: " + _msg);
+                RouterInfo ri = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
+                if (ri != null) {
+                    _nextHopCache = ri;
+                    send(_config, _msg, ri);
+                } else {
+                    if (_log.shouldLog(Log.ERROR))
+                        _log.error("Lookup the nextHop (" + _config.getSendTo().toBase64().substring(0,4) 
+                                  + " failed!  where do we go for " + _config + "?  msg dropped: " + _msg);
+                }
             }
         }
     }
@@ -162,6 +166,9 @@ public class TunnelParticipant {
         }
         public String getName() { return "timeout looking for next hop info"; }
         public void runJob() {
+            if (_nextHopCache != null)
+                return;
+            
             RouterInfo ri = _context.netDb().lookupRouterInfoLocally(_config.getSendTo());
             if (ri != null) {
                 _nextHopCache = ri;