diff --git a/history.txt b/history.txt
index ef65d315c57b546814f6642abdda20b1360c0868..4da6a73704e3208360404b750c8ce006350622ae 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,10 @@
-$Id: history.txt,v 1.330 2005/11/26 00:05:54 jrandom Exp $
+$Id: history.txt,v 1.331 2005/11/26 04:16:25 jrandom Exp $
+
+2005-11-26  jrandom 
+    * Be more explicit about what messages we will handle through a client
+      tunnel, and how we will handle them.  This cuts off a set of attacks
+      that an active adversary could mount, though they're probably nonobvious
+      and would require at least some sophistication.
 
 2005-11-26  Raccoon23
     * Added support for 'dynamic keys' mode, where the router creates a new
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index 9fc26a3286c004ac7cd4e8bc22662f9e92460a43..18e17bdcae84dd6d665d7af0f5e781172a433a69 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.298 $ $Date: 2005/11/26 00:05:53 $";
+    public final static String ID = "$Revision: 1.299 $ $Date: 2005/11/26 04:16:13 $";
     public final static String VERSION = "0.6.1.5";
-    public final static long BUILD = 9;
+    public final static long BUILD = 10;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
         System.out.println("Router ID: " + RouterVersion.ID);
diff --git a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
index 23915ff5fbf5a48710d467aa863e14fbfbda2c98..ad994ab3434cc4ae08a9c392b1f1f741651faac2 100644
--- a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
+++ b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
@@ -5,6 +5,7 @@ import net.i2p.data.TunnelId;
 import net.i2p.data.Payload;
 import net.i2p.data.i2np.DataMessage;
 import net.i2p.data.i2np.DatabaseStoreMessage;
+import net.i2p.data.i2np.DeliveryStatusMessage;
 import net.i2p.data.i2np.DeliveryInstructions;
 import net.i2p.data.i2np.I2NPMessage;
 import net.i2p.data.i2np.GarlicMessage;
@@ -32,6 +33,7 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec
         _client = client;
         _log = ctx.logManager().getLog(InboundMessageDistributor.class);
         _receiver = new GarlicMessageReceiver(ctx, this, client);
+        _context.statManager().createRateStat("tunnel.dropDangerousClientTunnelMessage", "How many tunnel messages come down a client tunnel that we shouldn't expect (lifetime is the 'I2NP type')", "Tunnels", new long[] { 10*60*1000, 60*60*1000 });
     }
     
     public void distribute(I2NPMessage msg, Hash target) {
@@ -49,6 +51,16 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec
         }
         */
         
+        if ( (_client != null) && 
+             (msg.getType() != DeliveryStatusMessage.MESSAGE_TYPE) &&
+             (msg.getType() != GarlicMessage.MESSAGE_TYPE) ) {
+            // drop it, since we should only get tunnel test messages and garlic messages down
+            // client tunnels
+            _context.statManager().addRateData("tunnel.dropDangerousClientTunnelMessage", 1, msg.getType());
+            _log.error("Dropped dangerous message down a tunnel for " + _client.toBase64() + ": " + msg, new Exception("cause"));
+            return;
+        }
+        
         if ( (target == null) || ( (tunnel == null) && (_context.routerHash().equals(target) ) ) ) {
             // targetting us either implicitly (no target) or explicitly (no tunnel)
             // make sure we don't honor any remote requests directly (garlic instructions, etc)
@@ -115,16 +127,39 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec
                         // unnecessarily
                         DatabaseStoreMessage dsm = (DatabaseStoreMessage)data;
                         try {
-                            if (dsm.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET)
+                            if (dsm.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) {
+                                // dont tell anyone else about it if we got it through a client tunnel
+                                // (though this is the default, but it doesn't hurt to make it explicit)
+                                if (_client != null)
+                                    dsm.getLeaseSet().setReceivedAsPublished(false);
                                 _context.netDb().store(dsm.getKey(), dsm.getLeaseSet());
-                            else
+                            } else {                                        
+                                if (_client != null) {
+                                    // drop it, since the data we receive shouldn't include router 
+                                    // references, as that might get us to talk to them (and therefore
+                                    // open an attack vector)
+                                    _context.statManager().addRateData("tunnel.dropDangerousClientTunnelMessage", 1, 
+                                                                       DatabaseStoreMessage.MESSAGE_TYPE);
+                                    _log.error("Dropped dangerous message down a tunnel for " + _client.toBase64() + ": " + dsm, new Exception("cause"));
+                                    return;
+                                }
                                 _context.netDb().store(dsm.getKey(), dsm.getRouterInfo());
+                            }
                         } catch (IllegalArgumentException iae) {
                             if (_log.shouldLog(Log.WARN))
                                 _log.warn("Bad store attempt", iae);
                         }
                     } else {
-                        _context.inNetMessagePool().add(data, null, null);
+                        if ( (_client != null) && (data.getType() != DeliveryStatusMessage.MESSAGE_TYPE) ) {
+                            // drop it, since the data we receive shouldn't include other stuff, 
+                            // as that might open an attack vector
+                            _context.statManager().addRateData("tunnel.dropDangerousClientTunnelMessage", 1, 
+                                                               data.getType());
+                            _log.error("Dropped dangerous message down a tunnel for " + _client.toBase64() + ": " + data, new Exception("cause"));
+                            return;
+                        } else {
+                            _context.inNetMessagePool().add(data, null, null);
+                        }
                     }
                     return;
                 }