diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
index cb9cbaadaa58ab0983fb3be8fb21f6f260e7910a..3d4422690268d501c5fdde5ab35a5b9d33ecff36 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java
@@ -284,7 +284,7 @@ public class I2PTunnel implements Logging, EventDispatcher {
         l.log("textserver <host> <port> <privkey>");
         l.log("genkeys <privkeyfile> [<pubkeyfile>]");
         l.log("gentextkeys");
-        l.log("client <port> <pubkey>|file:<pubkeyfile>");
+        l.log("client <port> <pubkey>[,<pubkey,...]|file:<pubkeyfile>");
         l.log("httpclient <port>");
         l.log("lookup <name>");
         l.log("quit");
@@ -449,9 +449,11 @@ public class I2PTunnel implements Logging, EventDispatcher {
                 notifyEvent("clientTaskId", new Integer(-1));
             }
         } else {
-            l.log("client <port> <pubkey>|file:<pubkeyfile>");
+            l.log("client <port> <pubkey>[,<pubkey>]|file:<pubkeyfile>");
             l.log("  creates a client that forwards port to the pubkey.\n"
-                  + "  use 0 as port to get a free port assigned.");
+                  + "  use 0 as port to get a free port assigned.  If you specify\n"
+                  + "  a comma delimited list of pubkeys, it will rotate among them\n"
+                  + "  randomlyl");
             notifyEvent("clientTaskId", new Integer(-1));
         }
     }
diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java
index a2163f1f2cd29e6168fc9869dc1a382c7d4c5dd7..4739a07f47bcec19361a09363e841c35967c4898 100644
--- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java
+++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java
@@ -4,7 +4,11 @@
 package net.i2p.i2ptunnel;
 
 import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
 
+import net.i2p.I2PAppContext;
 import net.i2p.client.streaming.I2PSocket;
 import net.i2p.data.DataFormatException;
 import net.i2p.data.Destination;
@@ -15,15 +19,17 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
 
     private static final Log _log = new Log(I2PTunnelClient.class);
 
-    protected Destination dest;
+    /** list of Destination objects that we point at */
+    protected List dests;
     private static final long DEFAULT_READ_TIMEOUT = 5*60*1000; // -1
     protected long readTimeout = DEFAULT_READ_TIMEOUT;
 
     /**
+     * @param destinations comma delimited list of peers we target
      * @throws IllegalArgumentException if the I2PTunnel does not contain
      *                                  valid config to contact the router
      */
-    public I2PTunnelClient(int localPort, String destination, Logging l, 
+    public I2PTunnelClient(int localPort, String destinations, Logging l, 
                            boolean ownDest, EventDispatcher notifyThis, 
                            I2PTunnel tunnel) throws IllegalArgumentException {
         super(localPort, ownDest, l, notifyThis, "SynSender", tunnel);
@@ -33,19 +39,28 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
             return;
         }
 
-        try {
-            dest = I2PTunnel.destFromName(destination);
-            if (dest == null) {
-                l.log("Could not resolve " + destination + ".");
-                return;
+        StringTokenizer tok = new StringTokenizer(destinations, ",");
+        dests = new ArrayList(1);
+        while (tok.hasMoreTokens()) {
+            String destination = tok.nextToken();
+            try {
+                Destination dest = I2PTunnel.destFromName(destination);
+                if (dest == null)
+                    l.log("Could not resolve " + destination);
+                else
+                    dests.add(dest);
+            } catch (DataFormatException dfe) {
+                l.log("Bad format parsing \"" + destination + "\"");
             }
-        } catch (DataFormatException e) {
-            l.log("Bad format in destination \"" + destination + "\".");
+        }
+
+        if (dests.size() <= 0) {
+            l.log("No target destinations found");
             notifyEvent("openClientResult", "error");
             return;
         }
 
-        setName(getLocalPort() + " -> " + destination);
+        setName(getLocalPort() + " -> " + destinations);
 
         startRunning();
 
@@ -56,6 +71,7 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
     public long getReadTimeout() { return readTimeout; }
     
     protected void clientConnectionRun(Socket s) {
+        Destination dest = pickDestination();
         I2PSocket i2ps = null;
         try {
             i2ps = createI2PSocket(dest);
@@ -72,4 +88,17 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
             }
         }
     }
+    
+    private final Destination pickDestination() {
+        int size = dests.size();
+        if (size <= 0) {
+            if (_log.shouldLog(Log.ERROR))
+                _log.error("No client targets?!");
+            return null;
+        }
+        if (size == 1) // skip the rand in the most common case
+            return (Destination)dests.get(0);
+        int index = I2PAppContext.getGlobalContext().random().nextInt(size);
+        return (Destination)dests.get(index);
+    }
 }
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..246ecb8e7d2b9561e7409f26d8a3f756b2a17c92
--- /dev/null
+++ b/apps/routerconsole/java/src/net/i2p/router/web/ReseedHandler.java
@@ -0,0 +1,129 @@
+package net.i2p.router.web;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import net.i2p.I2PAppContext;
+import net.i2p.util.I2PThread;
+import net.i2p.util.Log;
+
+/**
+ * Handler to deal with reseed requests.  This reseed from the URL
+ * http://dev.i2p.net/i2pdb/ unless the java env property "i2p.reseedURL" is
+ * set.  It always writes to ./netDb/, so don't mess with that.
+ *
+ */
+public class ReseedHandler {
+    private static ReseedRunner _reseedRunner = new ReseedRunner();
+    
+    public void setReseedNonce(String nonce) { 
+        if (nonce == null) return;
+        if (nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.nonce")) ||
+            nonce.equals(System.getProperty("net.i2p.router.web.ReseedHandler.noncePrev"))) {
+            synchronized (_reseedRunner) {
+                if (_reseedRunner.isRunning()) {
+                    return;
+                } else {
+                    System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "true");
+                    I2PThread reseed = new I2PThread(_reseedRunner, "Reseed");
+                    reseed.start();
+                }
+            }
+        }
+    }
+
+    public static class ReseedRunner implements Runnable {
+        private boolean _isRunning;
+        public ReseedRunner() { _isRunning = false; }
+        public boolean isRunning() { return _isRunning; }
+        public void run() {
+            _isRunning = true;
+            reseed();
+            System.setProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false");
+            _isRunning = false;
+        }
+    }
+    
+    private static final String DEFAULT_SEED_URL = "http://dev.i2p.net/i2pdb/";
+    /**
+     * Reseed has been requested, so lets go ahead and do it.  Fetch all of
+     * the routerInfo-*.dat files from the specified URL (or the default) and
+     * save them into this router's netDb dir.
+     *
+     */
+    private static void reseed() {
+        String seedURL = System.getProperty("i2p.reseedURL", DEFAULT_SEED_URL);
+        if ( (seedURL == null) || (seedURL.trim().length() <= 0) ) 
+            seedURL = DEFAULT_SEED_URL;
+        try {
+            URL dir = new URL(seedURL);
+            String content = new String(readURL(dir));
+            Set urls = new HashSet();
+            int cur = 0;
+            while (true) {
+                int start = content.indexOf("href=\"routerInfo-", cur);
+                if (start < 0)
+                    break;
+
+                int end = content.indexOf(".dat\">", start);
+                String name = content.substring(start+"href=\"routerInfo-".length(), end);
+                urls.add(name);
+                cur = end + 1;
+            }
+
+            int fetched = 0;
+            int errors = 0;
+            for (Iterator iter = urls.iterator(); iter.hasNext(); ) {
+                try {
+                    fetchSeed(seedURL, (String)iter.next());
+                    fetched++;
+                } catch (Exception e) {
+                    errors++;
+                }
+            }
+        } catch (Throwable t) {
+            I2PAppContext.getGlobalContext().logManager().getLog(ReseedHandler.class).error("Error reseeding", t);
+        }
+    }
+    
+    private static void fetchSeed(String seedURL, String peer) throws Exception {
+        URL url = new URL(seedURL + (seedURL.endsWith("/") ? "" : "/") + "routerInfo-" + peer + ".dat");
+
+        byte data[] = readURL(url);
+        writeSeed(peer, data);
+    }
+    
+    private static byte[] readURL(URL url) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
+        URLConnection con = url.openConnection();
+        InputStream in = con.getInputStream();
+        byte buf[] = new byte[1024];
+        while (true) {
+            int read = in.read(buf);
+            if (read < 0)
+                break;
+            baos.write(buf, 0, read);
+        }
+        in.close();
+        return baos.toByteArray();
+    }
+    
+    private static void writeSeed(String name, byte data[]) throws Exception {
+        String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb");
+        File netDbDir = new File(dirName);
+        if (!netDbDir.exists()) {
+            boolean ok = netDbDir.mkdirs();
+        }
+        FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat"));
+        fos.write(data);
+        fos.close();
+    }
+}
diff --git a/apps/routerconsole/jsp/config.jsp b/apps/routerconsole/jsp/config.jsp
index b195f551597c3225f5c048ae3e27207a8fce462d..93315d16ecd9b2a9393b84f3230ec250c3332178 100644
--- a/apps/routerconsole/jsp/config.jsp
+++ b/apps/routerconsole/jsp/config.jsp
@@ -28,44 +28,58 @@
  <input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigNetHandler.nonce")%>" />
  <input type="hidden" name="action" value="blah" />
 
- <b>External hostname/IP address:</b> 
-    <input name="hostname" type="text" size="32" value="<jsp:getProperty name="nethelper" property="hostname" />" />
-    <input type="submit" name="guesshost" value="Guess" /><br />
- <b>Externally reachable TCP port:</b>
+ TCP port:
      <input name="port" type="text" size="4" value="<jsp:getProperty name="nethelper" property="port" />" /> <br />
- <i>The hostname/IP address and TCP port must be reachable from the outside world.  If
- you are behind a firewall or NAT, this means you must poke a hole for this port.  If
- you are using DHCP and do not have a static IP address, you should either use a service like
- <a href="http://dyndns.org/">dyndns</a> or leave the hostname blank.  If you leave it blank,
- your router will autodetect the 'correct' IP address by asking a peer (and unconditionally 
- believing them if the address is routable and you don't have any established connections yet).
- The "guess" functionality makes an HTTP request 
- to <a href="http://www.whatismyip.com/">www.whatismyip.com</a>.</i>
- <hr />
- <b>Enable internal time synchronization?</b> <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
- <i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
-    be within a few seconds of "correct".</i>
+ <b>You must poke a hole in your firewall or NAT (if applicable) so that you can receive inbound TCP
+ connections on it.</b>  Nothing will work if you don't.  Sorry.  We know how to make it so
+ this restriction won't be necessary, but its later on in the 
+ <a href="http://www.i2p.net/roadmap">roadmap</a> and we only have so many coder-hours (but if you want
+ to help, please <a href="http://www.i2p.net/getinvolved">get involved!</a>)
  <hr />
+ 
  <b>Bandwidth limiter</b><br />
- <b>Inbound rate</b>: 
+ Inbound rate: 
     <input name="inboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="inboundRate" />" /> KBytes per second<br />
- <b>Inbound burst duration:</b>
+ Inbound burst duration:
     <jsp:getProperty name="nethelper" property="inboundBurstFactorBox" /><br />
- <b>Outbound rate:</b>
+ Outbound rate:
     <input name="outboundrate" type="text" size="2" value="<jsp:getProperty name="nethelper" property="outboundRate" />" /> KBytes per second<br />
- <b>Outbound burst duration:</b> 
+ Outbound burst duration:
   <jsp:getProperty name="nethelper" property="outboundBurstFactorBox" /><br />
  <i>A negative rate means there is no limit</i><br />
  <hr />
- <b>Reseed</b> (from <input name="reseedfrom" type="text" size="40" value="http://dev.i2p.net/i2pdb/" />): 
-               <input type="submit" name="reseed" value="now" /><br />
- <i>May take some time to download the peer references</i>
+ Enable internal time synchronization? <input type="checkbox" <jsp:getProperty name="nethelper" property="enableTimeSyncChecked" /> name="enabletimesync" /><br />
+ <i>If disabled, your machine <b>must</b> be NTP synchronized - your clock must always
+    be within a few seconds of "correct".  You will need to be able to send outbound UDP
+    packets on port 123 to one of the pool.ntp.org machines (or some other SNTP server).</i>
  <hr />
  <input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
- <i>Changing the hostname or TCP port will force a 'soft restart' - dropping your connections 
-    and clients as if the router was stopped and restarted.  <b>Please be patient</b> - it may take
+ <i>Changing the TCP port will force a 'soft restart' - dropping your connections and clients as 
+    if the router was stopped and restarted.  <b>Please be patient</b> - it may take
     a few seconds to complete.</i>
  </form>
+ <hr />
+ <b>Advanced network config:</b>
+ <p>
+ There are two other network settings, but no one reads this text so there's no reason
+ to tell you about them.  In case you actually do read this, here's the deal: by default,
+ I2P will attempt to guess your IP address by having whomever it talks to tell it what 
+ address they think you are.  If and only if you have no working TCP connections and you
+ have not overridden the IP address, your router will believe them.  If that doesn't sound
+ ok to you, thats fine - go to the <a href="/configadvanced.jsp">advanced config</a> page
+ and add "i2np.tcp.hostname=yourHostname", then go to the 
+ <a href="/configservice.jsp">service</a> page and do a graceful restart.  We used to make
+ people enter a hostname/IP address on this page, but too many people got it wrong ;)</p>
+ 
+ <p>The other advanced network option has to do with reseeding - you should never need to 
+ reseed your router as long as you can find at least one other peer on the network.  However,
+ when you do need to reseed, a link will show up on the left hand side which will
+ fetch all of the routerInfo-* files from http://dev.i2p.net/i2pdb/.  That URL is just an
+ apache folder pointing at the netDb/ directory of a router - anyone can run one, and you can
+ configure your router to seed off an alternate URL by adding the java environmental property
+ "i2p.reseedURL=someURL" (e.g. java -Di2p.reseedURL=http://dev.i2p.net/i2pdb/ ...).  You can
+ also do it manually by getting routerInfo-*.dat files from someone (a friend, someone on IRC,
+ whatever) and saving them to your netDb/ directory.</p>
 </div>
 
 </body>
diff --git a/apps/routerconsole/jsp/summary.jsp b/apps/routerconsole/jsp/summary.jsp
index 144f35dacbd747c68ee9d1c9fed1743ea4347e3a..aedbf44fe1026624d0ba42854aff6ca94075f6fc 100644
--- a/apps/routerconsole/jsp/summary.jsp
+++ b/apps/routerconsole/jsp/summary.jsp
@@ -2,6 +2,9 @@
 <jsp:useBean class="net.i2p.router.web.SummaryHelper" id="helper" scope="request" />
 <jsp:setProperty name="helper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
 
+<jsp:useBean class="net.i2p.router.web.ReseedHandler" id="reseed" scope="request" />
+<jsp:setProperty name="reseed" property="*" />
+
 <div class="routersummary">
  <u><b>General</b></u><br />
  <b>Ident:</b> <jsp:getProperty name="helper" property="ident" /><br />
@@ -16,8 +19,27 @@
  <b>High capacity:</b> <jsp:getProperty name="helper" property="highCapacityPeers" /><br />
  <b>Well integrated:</b> <jsp:getProperty name="helper" property="wellIntegratedPeers" /><br />
  <b>Failing:</b> <jsp:getProperty name="helper" property="failingPeers" /><br />
- <b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br />
- <hr />
+ <b>Shitlisted:</b> <jsp:getProperty name="helper" property="shitlistedPeers" /><br /><%
+     if (helper.getActivePeers() <= 0) {
+        %><b><a href="config.jsp">check your NAT/firewall</a></b><br /><%
+     }
+    if (helper.getActiveProfiles() <= 4) { // 4 is the min fallback
+        if ("true".equals(System.getProperty("net.i2p.router.web.ReseedHandler.reseedInProgress", "false"))) {
+            out.print(" <i>reseeding</i>");
+        } else {
+            long nonce = new java.util.Random().nextLong();
+            String prev = System.getProperty("net.i2p.router.web.ReseedHandler.nonce");
+            if (prev != null) System.setProperty("net.i2p.router.web.ReseedHandler.noncePrev", prev);
+            System.setProperty("net.i2p.router.web.ReseedHandler.nonce", nonce+"");
+            String uri = request.getRequestURI();
+            if (uri.indexOf('?') > 0)
+                uri = uri + "&reseedNonce=" + nonce;
+            else
+                uri = uri + "?reseedNonce=" + nonce;
+            out.print(" <a href=\"" + uri + "\">reseed</a>");
+        }
+    }
+ %><hr />
  
  <u><b>Bandwidth in/out</b></u><br />
  <b>1m:</b> <jsp:getProperty name="helper" property="inboundMinuteKBps" />/<jsp:getProperty name="helper" property="outboundMinuteKBps" />KBps<br />
diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java
index 6a32f57a31e86a0ad37a717562a673d13ab7e0d4..dc7e92e374152ac2692af567414e44a452e47e4a 100644
--- a/core/java/src/net/i2p/data/DataHelper.java
+++ b/core/java/src/net/i2p/data/DataHelper.java
@@ -154,6 +154,9 @@ public class DataHelper {
             while ( (line = in.readLine()) != null) {
                 if (line.trim().length() <= 0) continue;
                 if (line.charAt(0) == '#') continue;
+                if (line.charAt(0) == ';') continue;
+                if (line.indexOf('#') > 0)  // trim off any end of line comment
+                    line = line.substring(0, line.indexOf('#')).trim();
                 int split = line.indexOf('=');
                 if (split <= 0) continue;
                 String key = line.substring(0, split);
@@ -311,7 +314,7 @@ public class DataHelper {
             throw new IllegalArgumentException("wtf, fromLong got a negative? " + rv + ": offset="+ offset +" numBytes="+numBytes);
         return rv;
     }
-
+    
     public static void main(String args[]) {
         for (int i = 0; i <= 0xFF; i++)
             testLong(1, i);
diff --git a/history.txt b/history.txt
index 8cdc5b6d5d732dbd236117911b862b87eeda7e4e..49e9ce30bb16209dc53e4cc33b45bbf87a684b27 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,13 @@
-$Id: history.txt,v 1.76 2004/11/19 18:04:27 jrandom Exp $
+$Id: history.txt,v 1.77 2004/11/20 23:08:14 jrandom Exp $
+
+2004-11-21  jrandom
+    * Allow end of line comments in the hosts.txt and other config files,
+      using '#' to begin the comments (thanks susi!)
+    * Add support to I2PTunnel's 'client' feature for picking between multiple
+      target destinations (e.g. 'client 6668 irc.duck.i2p,irc.baffled.i2p')
+    * Add a quick link on the left hand nav to reseed if there aren't enough
+      known peers, as well as link to the config page if there are no active 
+      peers.  Revised config page accordingly.
 
 2004-11-21  jrandom
     * Destroy ElGamal/AES+SessionTag keys after 15 minutes of inactivity 
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index d88cdf641a51963c1f19f32f334d74b8c176eb11..bf30d4d53be7f07bd8f3cb418255eae703f6f344 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.81 $ $Date: 2004/11/19 18:04:27 $";
+    public final static String ID = "$Revision: 1.82 $ $Date: 2004/11/20 23:08:14 $";
     public final static String VERSION = "0.4.1.4";
-    public final static long BUILD = 10;
+    public final static long BUILD = 11;
     public static void main(String args[]) {
         System.out.println("I2P Router version: " + VERSION);
         System.out.println("Router ID: " + RouterVersion.ID);