From 7d4e093b584df47eea9e00479265b6100f541c85 Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Sat, 19 Feb 2005 23:20:56 +0000
Subject: [PATCH] 2005-02-19  jrandom     * Only build new extra tunnels on
 failure if we don't have enough     * Fix a fencepost in the tunnel building
 so that e.g. a variance of       2 means +/- 2, not +/- 1 (thanks dm!)     *
 Avoid an NPE on client disconnect     * Never select a shitlisted peer to
 participate in a tunnel     * Have netDb store messages timeout after 10s,
 not the full 60s (duh)     * Keep session tags around for a little longer,
 just in case (grr)     * Cleaned up some closing event issues on the
 streaming lib     * Stop bundling the jetty 5.1.2 and updated wrapper.config
 in the update       so that 0.4.* users will need to do a clean install, but
 we don't need       to shove an additional 2MB in each update to those
 already on 0.5.     * Imported the susimail css (oops, thanks susi!)

---
 .../net/i2p/client/streaming/Connection.java  | 29 ++++--
 .../i2p/client/streaming/PacketHandler.java   |  8 +-
 .../i2p/client/streaming/SchedulerDead.java   | 12 +--
 .../streaming/SchedulerHardDisconnected.java  |  2 +-
 apps/susimail/src/css.css                     | 96 +++++++++++++++++++
 build.xml                                     |  4 +
 .../crypto/TransientSessionKeyManager.java    |  2 +-
 .../src/net/i2p/stat/BufferedStatLog.java     | 16 ++--
 history.txt                                   | 16 +++-
 .../src/net/i2p/router/RouterVersion.java     |  4 +-
 .../kademlia/RepublishLeaseSetJob.java        |  5 +-
 .../router/networkdb/kademlia/StoreJob.java   |  4 +-
 .../router/peermanager/ProfileOrganizer.java  |  3 +
 .../tunnel/InboundMessageDistributor.java     |  5 +
 .../tunnel/pool/TunnelPeerSelector.java       | 10 +-
 .../i2p/router/tunnel/pool/TunnelPool.java    |  8 +-
 16 files changed, 184 insertions(+), 40 deletions(-)
 create mode 100644 apps/susimail/src/css.css

diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
index fc8bad8389..a7a8da680d 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java
@@ -57,7 +57,7 @@ public class Connection {
     private I2PSocketFull _socket;
     /** set to an error cause if the connection could not be established */
     private String _connectionError;
-    private boolean _disconnectScheduled;
+    private long _disconnectScheduledOn;
     private long _lastReceivedOn;
     private ActivityTimer _activityTimer;
     /** window size when we last saw congestion */
@@ -113,7 +113,7 @@ public class Connection {
         _connectionManager = manager;
         _resetReceived = false;
         _connected = true;
-        _disconnectScheduled = false;
+        _disconnectScheduledOn = -1;
         _lastReceivedOn = -1;
         _activityTimer = new ActivityTimer();
         _ackSinceCongestion = true;
@@ -191,6 +191,10 @@ public class Connection {
      *
      */
     void sendReset() {
+        if (_disconnectScheduledOn < 0) {
+            _disconnectScheduledOn = _context.clock().now();
+            SimpleTimer.getInstance().addEvent(new DisconnectEvent(), DISCONNECT_TIMEOUT);
+        }
         _resetSent = true;
         if (_resetSentOn <= 0)
             _resetSentOn = _context.clock().now();
@@ -382,6 +386,10 @@ public class Connection {
     }
     
     void resetReceived() {
+        if (_disconnectScheduledOn < 0) {
+            _disconnectScheduledOn = _context.clock().now();
+            SimpleTimer.getInstance().addEvent(new DisconnectEvent(), DISCONNECT_TIMEOUT);
+        }
         _resetReceived = true;
         MessageOutputStream mos = _outputStream;
         MessageInputStream mis = _inputStream;
@@ -398,6 +406,7 @@ public class Connection {
     public boolean getHardDisconnected() { return _hardDisconnected; }
     public boolean getResetSent() { return _resetSent; }
     public long getResetSentOn() { return _resetSentOn; }
+    public long getDisconnectScheduledOn() { return _disconnectScheduledOn; }
 
     void disconnect(boolean cleanDisconnect) {
         disconnect(cleanDisconnect, true);
@@ -424,8 +433,8 @@ public class Connection {
             killOutstandingPackets();
         }
         if (removeFromConMgr) {
-            if (!_disconnectScheduled) {
-                _disconnectScheduled = true;
+            if (_disconnectScheduledOn < 0) {
+                _disconnectScheduledOn = _context.clock().now();
                 SimpleTimer.getInstance().addEvent(new DisconnectEvent(), DISCONNECT_TIMEOUT);
             }
         }
@@ -445,8 +454,8 @@ public class Connection {
             SimpleTimer.getInstance().removeEvent(_activityTimer);
         _activityTimer = null;
         
-        if (!_disconnectScheduled) {
-            _disconnectScheduled = true;
+        if (_disconnectScheduledOn < 0) {
+            _disconnectScheduledOn = _context.clock().now();
             
             if (_log.shouldLog(Log.INFO))
                 _log.info("Connection disconnect complete from dead, drop the con "
@@ -576,7 +585,13 @@ public class Connection {
     public long getAckedPackets() { return _ackedPackets; }
     public long getCreatedOn() { return _createdOn; }
     public long getCloseSentOn() { return _closeSentOn; }
-    public void setCloseSentOn(long when) { _closeSentOn = when; }
+    public void setCloseSentOn(long when) { 
+        _closeSentOn = when;
+        if (_disconnectScheduledOn < 0) {
+            _disconnectScheduledOn = _context.clock().now();
+            SimpleTimer.getInstance().addEvent(new DisconnectEvent(), DISCONNECT_TIMEOUT);
+        }
+    }
     public long getCloseReceivedOn() { return _closeReceivedOn; }
     public void setCloseReceivedOn(long when) { _closeReceivedOn = when; }
     
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java
index a66996d82e..93fb4ce559 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketHandler.java
@@ -156,9 +156,11 @@ public class PacketHandler {
                     sendReset(packet);
                 }
             } else {
-                // someone is sending us a packet on the wrong stream 
-                if (_log.shouldLog(Log.WARN))
-                    _log.warn("Received a packet on the wrong stream: " + packet + " connection: " + con);
+                if (!con.getResetSent()) {
+                    // someone is sending us a packet on the wrong stream 
+                    if (_log.shouldLog(Log.WARN))
+                        _log.warn("Received a packet on the wrong stream: " + packet + " connection: " + con);
+                }
             }
         }
     }
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerDead.java b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerDead.java
index 34652e27b9..3c0d4bda8e 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerDead.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerDead.java
@@ -32,19 +32,13 @@ class SchedulerDead extends SchedulerImpl {
     
     public boolean accept(Connection con) {
         if (con == null) return false;
-        long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
-        if (con.getResetSent())
-            timeSinceClose = _context.clock().now() - con.getResetSentOn();
-        boolean nothingLeftToDo = (con.getCloseSentOn() > 0) &&
-                                  (con.getCloseReceivedOn() > 0) &&
-                                  (con.getUnackedPacketsReceived() <= 0) &&
-                                  (con.getUnackedPacketsSent() <= 0) &&
-                                  (con.getResetSent()) &&
+        long timeSinceClose = _context.clock().now() - con.getDisconnectScheduledOn();
+        boolean nothingLeftToDo = (con.getDisconnectScheduledOn() > 0) && 
                                   (timeSinceClose >= Connection.DISCONNECT_TIMEOUT);
         boolean timedOut = (con.getOptions().getConnectTimeout() < con.getLifetime()) && 
                            con.getSendStreamId() == null &&
                            con.getLifetime() >= Connection.DISCONNECT_TIMEOUT;
-        return con.getResetReceived() || nothingLeftToDo || timedOut;
+        return nothingLeftToDo || timedOut;
     }
     
     public void eventOccurred(Connection con) {
diff --git a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerHardDisconnected.java b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerHardDisconnected.java
index 04d4d0c891..7488e77b94 100644
--- a/apps/streaming/java/src/net/i2p/client/streaming/SchedulerHardDisconnected.java
+++ b/apps/streaming/java/src/net/i2p/client/streaming/SchedulerHardDisconnected.java
@@ -36,7 +36,7 @@ class SchedulerHardDisconnected extends SchedulerImpl {
         long timeSinceClose = _context.clock().now() - con.getCloseSentOn();
         if (con.getResetSent())
             timeSinceClose = _context.clock().now() - con.getResetSentOn();
-        boolean ok = (con.getHardDisconnected() || con.getResetSent()) && 
+        boolean ok = (con.getHardDisconnected() || con.getResetSent() || con.getResetReceived()) && 
                      (timeSinceClose < Connection.DISCONNECT_TIMEOUT);
         return ok;
     }
diff --git a/apps/susimail/src/css.css b/apps/susimail/src/css.css
new file mode 100644
index 0000000000..59b6e0c46b
--- /dev/null
+++ b/apps/susimail/src/css.css
@@ -0,0 +1,96 @@
+body {
+	background-color:white;
+}
+
+li {
+	font-family:Verdana,Tahoma,Arial,Helvetica;
+	color:black;
+	line-height:12pt;
+	font-size:10pt;
+	margin-left:5mm;
+	margin-right:5mm;
+}
+
+p {
+	font-family:Verdana,Tahoma,Arial,Helvetica;
+	color:black;
+	line-height:12pt;
+	margin-left:5mm;
+	margin-right:5mm;
+	font-size:10pt;
+}
+
+p.hl {
+	font-size:12pt;
+	letter-spacing:2pt;
+	line-height:18pt;
+	font-weight:bold;
+}
+
+p.text {
+	margin-left:10mm;
+	margin-right:10mm;
+}
+
+p.error {
+	color:#ff0000;
+}
+
+p.info {
+	color:#327BBF;
+}
+
+span.coloured {
+	color:#327BBF;
+}
+
+p.footer {
+	margin-left:10mm;
+	margin-right:10mm;
+    	font-size:8pt;
+    	line-height:10pt;
+}
+
+p.mailbody {
+	font-family:Courier-Fixed;
+	margin-left:1cm;
+	margin-right:1cm;
+}
+
+a {
+	color:#327BBF;
+	text-decoration:none;
+}
+
+a:hover {
+	text-decoration:underline;
+}
+
+td {
+	font-family:Verdana,Tahoma,Arial,Helvetica;
+	color:black;
+	line-height:12pt;
+	margin-left:5mm;
+	margin-right:5mm;
+	font-size:10pt;
+}
+
+tr.list0 {
+	background-color:#e0e0e0;
+}
+
+tr.list1 {
+	background-color:#ffffff;
+}
+
+table.noborder {
+	margin-left:0mm;
+	margin-top:0mm;
+	margin-right:0mm;
+}
+
+pre {
+	font-family:Courier-Fixed;
+	margin-left:1cm;
+	margin-right:1cm;
+}
\ No newline at end of file
diff --git a/build.xml b/build.xml
index ce50f5fd49..6700cf019d 100644
--- a/build.xml
+++ b/build.xml
@@ -250,15 +250,19 @@
         <copy file="build/routerconsole.jar" todir="pkg-temp/lib/" />
         
         <!-- for the i2p 0.5 release, push jetty 5.2.1 -->
+        <!--
         <copy file="build/jasper-compiler.jar" todir="pkg-temp/lib/" />
         <copy file="build/jasper-runtime.jar" todir="pkg-temp/lib/" />
         <copy file="build/commons-logging.jar" todir="pkg-temp/lib/" />
         <copy file="build/commons-el.jar" todir="pkg-temp/lib/" />
         <copy file="build/org.mortbay.jetty.jar" todir="pkg-temp/lib/" />
         <copy file="build/javax.servlet.jar" todir="pkg-temp/lib/" />
+        -->
         <!-- requires commons-* to be added to the classpath (boo, hiss) -->
+        <!--
         <copy file="installer/resources/wrapper.config" todir="pkg-temp/" />
         <touch file="pkg-temp/wrapper.config.updated" />
+        -->
         
         <copy file="build/i2ptunnel.war" todir="pkg-temp/webapps/" />
         <copy file="build/routerconsole.war" todir="pkg-temp/webapps/" />
diff --git a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java
index a09479a1e2..3e9df9a6ef 100644
--- a/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java
+++ b/core/java/src/net/i2p/crypto/TransientSessionKeyManager.java
@@ -53,7 +53,7 @@ class TransientSessionKeyManager extends SessionKeyManager {
      * can cause failed decrypts)
      *
      */
-    public final static long SESSION_LIFETIME_MAX_MS = SESSION_TAG_DURATION_MS + 2 * 60 * 1000;
+    public final static long SESSION_LIFETIME_MAX_MS = SESSION_TAG_DURATION_MS + 5 * 60 * 1000;
     public final static int MAX_INBOUND_SESSION_TAGS = 500 * 1000; // this will consume at most a few MB
 
     /** 
diff --git a/core/java/src/net/i2p/stat/BufferedStatLog.java b/core/java/src/net/i2p/stat/BufferedStatLog.java
index 039ca1709c..2dcd932224 100644
--- a/core/java/src/net/i2p/stat/BufferedStatLog.java
+++ b/core/java/src/net/i2p/stat/BufferedStatLog.java
@@ -99,7 +99,7 @@ public class BufferedStatLog implements StatLog {
             if (_out != null) try { _out.close(); } catch (IOException ioe) {}
             _outFile = filename;
             try {
-                _out = new BufferedWriter(new FileWriter(_outFile, true));
+                _out = new BufferedWriter(new FileWriter(_outFile, true), 32*1024);
             } catch (IOException ioe) { ioe.printStackTrace(); }
         }
     }
@@ -147,12 +147,16 @@ public class BufferedStatLog implements StatLog {
                         _out.write(when);
                         _out.write(" ");
                         if (_events[cur].getScope() == null)
-                            _out.write("noScope ");
+                            _out.write("noScope");
                         else
-                            _out.write(_events[cur].getScope() + " ");
-                        _out.write(_events[cur].getStat()+" ");
-                        _out.write(_events[cur].getValue()+" ");
-                        _out.write(_events[cur].getDuration()+"\n");
+                            _out.write(_events[cur].getScope());
+                        _out.write(" ");
+                        _out.write(_events[cur].getStat());
+                        _out.write(" ");
+                        _out.write(Long.toString(_events[cur].getValue()));
+                        _out.write(" ");
+                        _out.write(Long.toString(_events[cur].getDuration()));
+                        _out.write("\n");
                     }
                     cur = (cur + 1) % _events.length;
                 }
diff --git a/history.txt b/history.txt
index 5d0bd5f15a..c2f678eb18 100644
--- a/history.txt
+++ b/history.txt
@@ -1,4 +1,18 @@
-$Id: history.txt,v 1.146 2005/02/17 17:57:53 jrandom Exp $
+$Id: history.txt,v 1.147 2005/02/18 10:58:20 jrandom Exp $
+
+2005-02-19  jrandom
+    * Only build new extra tunnels on failure if we don't have enough
+    * Fix a fencepost in the tunnel building so that e.g. a variance of
+      2 means +/- 2, not +/- 1 (thanks dm!)
+    * Avoid an NPE on client disconnect
+    * Never select a shitlisted peer to participate in a tunnel
+    * Have netDb store messages timeout after 10s, not the full 60s (duh)
+    * Keep session tags around for a little longer, just in case (grr)
+    * Cleaned up some closing event issues on the streaming lib
+    * Stop bundling the jetty 5.1.2 and updated wrapper.config in the update
+      so that 0.4.* users will need to do a clean install, but we don't need 
+      to shove an additional 2MB in each update to those already on 0.5.
+    * Imported the susimail css (oops, thanks susi!)
 
 * 2005-02-18  0.5 released
 
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index e4faa8626d..493e10015a 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.141 $ $Date: 2005/02/17 12:59:28 $";
+    public final static String ID = "$Revision: 1.142 $ $Date: 2005/02/17 17:57:53 $";
     public final static String VERSION = "0.5";
-    public final static long BUILD = 0;
+    public final static long BUILD = 1;
     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/networkdb/kademlia/RepublishLeaseSetJob.java b/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java
index cf8e7299bd..2678ee6816 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/RepublishLeaseSetJob.java
@@ -22,7 +22,8 @@ import net.i2p.util.Log;
  */
 public class RepublishLeaseSetJob extends JobImpl {
     private Log _log;
-    private final static long REPUBLISH_LEASESET_DELAY = 60*1000; // 5 mins
+    private final static long REPUBLISH_LEASESET_DELAY = 5*60*1000; // 5 mins
+    private final static long REPUBLISH_LEASESET_TIMEOUT = 60*1000;
     private Hash _dest;
     private KademliaNetworkDatabaseFacade _facade;
     
@@ -43,7 +44,7 @@ public class RepublishLeaseSetJob extends JobImpl {
                     if (!ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) {
                         _log.warn("Not publishing a LOCAL lease that isn't current - " + _dest, new Exception("Publish expired LOCAL lease?"));
                     } else {
-                        getContext().jobQueue().addJob(new StoreJob(getContext(), _facade, _dest, ls, new OnSuccess(getContext()), new OnFailure(getContext()), REPUBLISH_LEASESET_DELAY));
+                        getContext().jobQueue().addJob(new StoreJob(getContext(), _facade, _dest, ls, new OnSuccess(getContext()), new OnFailure(getContext()), REPUBLISH_LEASESET_TIMEOUT));
                     }
                 } else {
                     _log.warn("Client " + _dest + " is local, but we can't find a valid LeaseSet?  perhaps its being rebuilt?");
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 26e78c5df4..706d1420f4 100644
--- a/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
+++ b/router/java/src/net/i2p/router/networkdb/kademlia/StoreJob.java
@@ -199,7 +199,7 @@ class StoreJob extends JobImpl {
             //    _log.debug(getJobId() + ": Send store to " + router.getIdentity().getHash().toBase64());
         }
 
-        sendStore(msg, router, _expiration);
+        sendStore(msg, router, getContext().clock().now() + STORE_TIMEOUT_MS);
     }
     
     private void sendStore(DatabaseStoreMessage msg, RouterInfo peer, long expiration) {
@@ -315,7 +315,7 @@ class StoreJob extends JobImpl {
             
             sendNext();
         }
-        public String getName() { return "Kademlia Store Failed"; }
+        public String getName() { return "Kademlia Store Peer Failed"; }
     }
 
     /**
diff --git a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
index 318c81fc8d..c224a0ce18 100644
--- a/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
+++ b/router/java/src/net/i2p/router/peermanager/ProfileOrganizer.java
@@ -690,6 +690,9 @@ public class ProfileOrganizer {
         // the CLI shouldn't depend upon the netDb
         if (netDb == null) return true;
         if (_context.router() == null) return true;
+        if ( (_context.shitlist() != null) && (_context.shitlist().isShitlisted(peer)) ) 
+            return false; // never select a shitlisted peer
+            
         if (null != netDb.lookupRouterInfoLocally(peer)) {
             if (_log.shouldLog(Log.INFO))
                 _log.info("Peer " + peer.toBase64() + " is locally known, allowing its use");
diff --git a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
index d4abda61b9..53713dcb3b 100644
--- a/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
+++ b/router/java/src/net/i2p/router/tunnel/InboundMessageDistributor.java
@@ -80,6 +80,11 @@ public class InboundMessageDistributor implements GarlicMessageReceiver.CloveRec
             // ok, they want us to send it remotely, but that'd bust our anonymity,
             // so we send it out a tunnel first
             TunnelInfo out = _context.tunnelManager().selectOutboundTunnel(_client);
+            if (out == null) {
+                if (_log.shouldLog(Log.ERROR))
+                    _log.error("no outbound tunnel to send the client message for " + _client + ": " + msg);
+                return;
+            }
             if (_log.shouldLog(Log.INFO))
                 _log.info("distributing inbound tunnel message back out " + out
                           + " targetting " + target.toBase64().substring(0,4));
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java
index c54aab48f0..d224306a2b 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java
@@ -26,10 +26,14 @@ abstract class TunnelPeerSelector {
         if (settings.getLengthVariance() != 0) {
             int skew = settings.getLengthVariance();
             if (skew > 0)
-                length += ctx.random().nextInt(skew);
+                length += ctx.random().nextInt(skew+1);
             else {
-                skew = 0 - skew;
-                length += ctx.random().nextInt(2*skew) - skew;
+                skew = 1 - skew;
+                int off = ctx.random().nextInt(skew);
+                if (ctx.random().nextBoolean())
+                    length += off;
+                else
+                    length -= off;
             }
             if (length < 0)
                 length = 0;
diff --git a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
index d7e2ac024a..8c002c2e63 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPool.java
@@ -298,7 +298,8 @@ public class TunnelPool {
                 if (_log.shouldLog(Log.WARN))
                     _log.warn(toString() + ": unable to build a new leaseSet on failure (" + remaining 
                               + " remaining), request a new tunnel");
-                buildFake(false);
+                if (remaining < _settings.getBackupQuantity() + _settings.getQuantity())
+                    buildFake(false);
             }
         }
         refreshBuilders();
@@ -320,8 +321,9 @@ public class TunnelPool {
                 if (_log.shouldLog(Log.WARN))
                     _log.warn(toString() + ": unable to build a new leaseSet on expire (" + remaining 
                               + " remaining), request a new tunnel");
-                if (_settings.getAllowZeroHop())
-                    buildFake();
+                if ( (remaining < _settings.getBackupQuantity() + _settings.getQuantity()) 
+                   && (_settings.getAllowZeroHop()) )
+                        buildFake();
             }
         }
     }
-- 
GitLab