diff --git a/apps/susidns/src/jsp/index.jsp b/apps/susidns/src/jsp/index.jsp
index 67c96c6801507d2ebd87bd20d69ffc151bc65832..f4b57eba3c2a4531f2afa89a76f5a6caaa95bf34 100644
--- a/apps/susidns/src/jsp/index.jsp
+++ b/apps/susidns/src/jsp/index.jsp
@@ -87,7 +87,7 @@
 <%=intl._t("Hosts in the private address book can be accessed by you but their addresses are never distributed to others.")%>
 <%=intl._t("The private address book can also be used for aliases of hosts in your other address books.")%>
 </p>
-<center><img src="/themes/susidns/images/how.png" border="0" alt="address book working scheme" title="How the address book works" class="illustrate" /></center>
+<center><img src="/themes/susidns/images/how.svg" height="367" width="600" border="0" alt="address book working scheme" title="How the address book works" class="illustrate" /></center>
 </div>
 <div id="footer">
 <hr>
diff --git a/history.txt b/history.txt
index 155cfb80ba9cc6c132a2e8fd10900a675ef7eba4..f7153a703b6a186e6a91c6157ee1e0bc377fd313 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,16 @@
+2016-01-20 zzz
+ * SusiDNS: Replace image (ticket #1301)
+
+2016-01-17 zzz
+ * BuildHandler: Disable tunnel removal on next-hop timeout
+ * Console:
+   - Fix mime type for svg in themes directory
+   - Add zh_TW translation
+ * Fortuna: Add getByte() method
+ * i2psnark: add opendocument mime types
+ * i2ptunnel: Remove unused stats
+ * Utils: Move CachedIteratorArrayList from core to router
+
 2016-01-13 zzz
  * BuildHandler: More early-disconnect cases
  * Family: Add i2p-dev cert
@@ -29,7 +42,7 @@
 2015-12-20 zzz
  * BuildHandler: Additional fixes (ticket #1738)
  * CertUtil: Add methods to export private keys
- * Console: Sybil tool enhancementsrivate keys
+ * Console: Sybil tool enhancements
  * Transports:
    - Disconnect faster when first message is a
      tunnel build request which we reject
diff --git a/installer/resources/themes/susidns/images/how.png b/installer/resources/themes/susidns/images/how.png
deleted file mode 100644
index 8d1e26192b6e0005b5e94c35a712faf98fc68084..0000000000000000000000000000000000000000
Binary files a/installer/resources/themes/susidns/images/how.png and /dev/null differ
diff --git a/installer/resources/themes/susidns/images/how.svg b/installer/resources/themes/susidns/images/how.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1ec27ee9950e65feeb110af307bd92eeb9c24b49
--- /dev/null
+++ b/installer/resources/themes/susidns/images/how.svg
@@ -0,0 +1,389 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="900"
+   height="550"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="I2P_Addressbooks_2v2.svg"
+   inkscape:export-filename="/home/z3r0fox/Documents/I2P/I2P_Addressbooks.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="TriangleOutS"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="TriangleOutS"
+       style="overflow:visible">
+      <path
+         id="path4373"
+         d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+         transform="scale(0.2)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow1Send"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow1Send"
+       style="overflow:visible;">
+      <path
+         id="path4240"
+         d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
+         transform="scale(0.2) rotate(180) translate(6,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="TriangleOutM"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="TriangleOutM"
+       style="overflow:visible">
+      <path
+         id="path4370"
+         d="M 5.77,0.0 L -2.88,5.0 L -2.88,-5.0 L 5.77,0.0 z "
+         style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt"
+         transform="scale(0.4)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Mend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow2Mend"
+       style="overflow:visible;">
+      <path
+         id="path4252"
+         style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+         d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+         transform="scale(0.6) rotate(180) translate(0,0)" />
+    </marker>
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow2Lend"
+       style="overflow:visible;">
+      <path
+         id="path4246"
+         style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+         d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+         transform="scale(1.1) rotate(180) translate(1,0)" />
+    </marker>
+    <linearGradient
+       id="linearGradient3833"
+       osb:paint="solid">
+      <stop
+         style="stop-color:#000000;stop-opacity:1;"
+         offset="0"
+         id="stop3835" />
+    </linearGradient>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="1"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.70710678"
+     inkscape:cx="458.28588"
+     inkscape:cy="264.46843"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer3"
+     showgrid="false"
+     inkscape:window-width="1920"
+     inkscape:window-height="1014"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:showpageshadow="false"
+     showborder="false" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     inkscape:label="Local Records Bound">
+    <g
+       inkscape:groupmode="layer"
+       id="layer3"
+       inkscape:label="Background">
+      <rect
+         style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.0422982;stroke-opacity:1"
+         id="rect6550"
+         width="931.9245"
+         height="579.78534"
+         x="-19.777842"
+         y="-12.83585"
+         ry="0" />
+    </g>
+    <path
+       style="fill:#dcdcdc;fill-opacity:1;stroke:#86887a;stroke-width:2.01356006;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 30.890714,69.164214 c -8.224961,0 -14.823986,6.541695 -14.823986,14.65625 l 0,216.781246 c 0,8.11456 0,21.16669 0,29.28125 l 0,78.1875 c 0,8.11456 6.599025,14.65625 14.823986,14.65625 l 401.989756,0 c 6.88999,0 21.77869,0 28.66604,0 l 411.99911,0 c 8.22497,0 14.85566,-6.54169 14.85566,-14.65625 l 0,-78.1875 c 0,-8.11456 -6.63069,-14.65625 -14.85566,-14.65625 l -411.99911,0 c -6.88735,0 -16.43942,-6.51044 -16.43942,-14.625 l 0,-216.781246 c 0,-8.114555 -6.6307,-14.65625 -14.85566,-14.65625 z"
+       id="rect4048"
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="sssssssssssssssss" />
+    <path
+       style="fill:none;stroke:#000000;stroke-width:7.75368643;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:7.75368654, 7.75368654;stroke-dashoffset:0;marker-end:url(#TriangleOutS)"
+       d="m 451.13412,399.89047 0,46.87194"
+       id="path4219"
+       inkscape:connector-curvature="0" />
+  </g>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-502.36218)">
+    <rect
+       style="fill:#dcdcdc;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:8, 8;stroke-dashoffset:0"
+       id="rect4217"
+       width="288.49957"
+       height="128.69344"
+       x="29.698484"
+       y="680.42401"
+       ry="14.647212" />
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.48403537;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985-0-1-1-8-0-4"
+       width="392.53958"
+       height="61.619305"
+       x="476.89587"
+       y="716.08234"
+       ry="14.647212" />
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985"
+       width="401.03055"
+       height="61.619305"
+       x="248.85085"
+       y="960.64032"
+       ry="14.647212"
+       inkscape:export-xdpi="90"
+       inkscape:export-ydpi="90"
+       inkscape:export-filename="/home/z3r0fox/Documents/I2P/I2P_Addressbooks.png" />
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+       x="318.22424"
+       y="999.0943"
+       id="text3759"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3761"
+         x="318.22424"
+         y="999.0943"
+         style="font-size:20px">PUBLISHED ADDRESSBOOK</tspan></text>
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985-0"
+       width="401.03055"
+       height="61.619305"
+       x="250.2148"
+       y="839.92706"
+       ry="14.647212" />
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.21147585;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985-0-1-3"
+       width="261.59198"
+       height="61.619305"
+       x="39.99683"
+       y="714.26422"
+       ry="14.647212" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+       x="123.64268"
+       y="548.29602"
+       id="text3910"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3912"
+         x="123.64268"
+         y="548.29602"
+         style="font-size:24px;font-style:normal;font-weight:bold;-inkscape-font-specification:Arial Bold">LOCAL RECORDS</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+       x="592.61475"
+       y="548.38812"
+       id="text3804-7"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806-0"
+         x="592.61475"
+         y="548.38812"
+         style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-family:Arial;-inkscape-font-specification:Arial Bold">SUBSCRIPTIONS</tspan></text>
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.48403537;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985-0-1-1-8-0"
+       width="392.53958"
+       height="61.619305"
+       x="476.89584"
+       y="692.04077"
+       ry="14.647212" />
+    <rect
+       style="fill:#ffffff;stroke:#000000;stroke-width:1.45869457999999996;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"
+       id="rect2985-0-1-1-8-09"
+       width="392.53958"
+       height="59.532894"
+       x="34.247021"
+       y="588.80316"
+       ry="14.151262" />
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.48403537;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985-0-1-1-8-0-3"
+       width="392.53958"
+       height="61.619305"
+       x="476.89587"
+       y="667.99915"
+       ry="14.647212" />
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.48403537;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985-0-1-1-8-0-3-8"
+       width="392.53958"
+       height="61.619305"
+       x="476.8959"
+       y="641.12903"
+       ry="14.647212" />
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.48403537;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985-0-1-1-8-0-3-8-6"
+       width="392.53958"
+       height="61.619305"
+       x="476.8959"
+       y="614.25903"
+       ry="14.647212" />
+    <rect
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1.44015396;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2985-0-1-1-8-0-3-8-6-2"
+       width="392.53958"
+       height="58.02914"
+       x="476.89584"
+       y="589.55286"
+       ry="13.793812" />
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="523.25903"
+       y="625.26965"
+       id="text3058"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3060"
+         x="523.25903"
+         y="625.26965">http://i2p-projekt.i2p/hosts.txt</tspan><tspan
+         sodipodi:role="line"
+         x="523.25903"
+         y="650.26965"
+         id="tspan3062" /></text>
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+       x="111.58013"
+       y="625.36176"
+       id="text3804-4"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806-6"
+         x="111.58013"
+         y="625.36176"
+         style="font-size:20px">MASTER ADDRESSBOOK</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+       x="48.546604"
+       y="752.23694"
+       id="text3804-0"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806-7"
+         x="48.546604"
+         y="752.23694"
+         style="font-size:20px">PRIVATE ADDRESSBOOK</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+       x="334.5625"
+       y="876.79767"
+       id="text3804"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan3806"
+         x="334.5625"
+         y="876.79767"
+         style="font-size:20px">ROUTER ADDRESSBOOK</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="668.92303"
+       y="867.10022"
+       id="text4096"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         id="tspan4098"
+         x="668.92303"
+         y="867.10022"
+         style="font-weight:bold">SEARCHABLE BY</tspan><tspan
+         sodipodi:role="line"
+         x="668.92303"
+         y="892.10022"
+         id="tspan4100"
+         style="font-weight:bold">I2P APPLICATIONS</tspan></text>
+    <text
+       xml:space="preserve"
+       style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+       x="669.99786"
+       y="984.91705"
+       id="text4096-4"
+       sodipodi:linespacing="125%"><tspan
+         sodipodi:role="line"
+         x="669.99786"
+         y="984.91705"
+         id="tspan4100-2"
+         style="font-weight:bold">OPTIONAL, FOR</tspan><tspan
+         sodipodi:role="line"
+         x="669.99786"
+         y="1009.9171"
+         id="tspan4128"
+         style="font-weight:bold">EEPSITES</tspan></text>
+    <path
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.09225798;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 360.61885,654.35563 0,124.26272 -19.78125,-0.0723 20.75,29.19795 20.71875,29.19795 21.0625,-29.05357 21.03125,-29.0536 -17.9375,-0.0723 0,-124.40709 -45.84375,0 z"
+       id="rect4130"
+       inkscape:connector-curvature="0" />
+    <path
+       inkscape:connector-curvature="0"
+       style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:2.0965755;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       d="m 521.05801,654.02458 0,124.77607 -19.78125,-0.0725 20.75,29.31856 20.71875,29.31858 21.0625,-29.17362 21.03125,-29.1736 -17.9375,-0.0723 0,-124.92103 -45.84375,0 z"
+       id="rect4130-3" />
+  </g>
+</svg>
diff --git a/installer/resources/themes/susidns/images/how_fr.png b/installer/resources/themes/susidns/images/how_fr.png
deleted file mode 100644
index 43da5d091e98e8a034b523799d89976080c4a981..0000000000000000000000000000000000000000
Binary files a/installer/resources/themes/susidns/images/how_fr.png and /dev/null differ
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index f7d70ed0aa6b54fa9329f93adb9baf354b141a81..0e04b1445cf7ad279d778bf1dbbb2a045fdd813e 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
     /** deprecated */
     public final static String ID = "Monotone";
     public final static String VERSION = CoreVersion.VERSION;
-    public final static long BUILD = 24;
+    public final static long BUILD = 26;
 
     /** for example "-test" */
     public final static String EXTRA = "-rc";
diff --git a/router/java/src/net/i2p/router/tunnel/HopProcessor.java b/router/java/src/net/i2p/router/tunnel/HopProcessor.java
index f45ec234b4fa3eff131bde6bf08a14bd8d0baf11..9ba6218fd90dd5e9bec5b3093d65426977f6be05 100644
--- a/router/java/src/net/i2p/router/tunnel/HopProcessor.java
+++ b/router/java/src/net/i2p/router/tunnel/HopProcessor.java
@@ -30,14 +30,28 @@ class HopProcessor {
      */
     //static final boolean USE_DOUBLE_IV_ENCRYPTION = true;
     static final int IV_LENGTH = 16;
-    
 
+    /**
+     *  @deprecated used only by unit tests
+     */
+    HopProcessor(I2PAppContext ctx, HopConfig config) {
+        this(ctx, config, createValidator());
+    }
+    
     public HopProcessor(I2PAppContext ctx, HopConfig config, IVValidator validator) {
         _context = ctx;
         _log = ctx.logManager().getLog(HopProcessor.class);
         _config = config;
         _validator = validator;
     }
+
+    /**
+     *  @deprecated used only by unit test constructor
+     */
+    private static IVValidator createValidator() { 
+        // yeah, we'll use an O(1) validator later (e.g. bloom filter)
+        return new HashSetIVValidator();
+    }
     
     /**
      * Process the data for the current hop, overwriting the original data with
diff --git a/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java b/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java
index 88aa6da71ad9b68947bff3fd893346b632f83106..4a3c5eccbec7b95e2eafb12210483e7b3f426cd7 100644
--- a/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java
+++ b/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java
@@ -21,8 +21,10 @@ class InboundEndpointProcessor {
     
     //static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION;
     
-    /** @deprecated unused */
-    public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg) {
+    /**
+     *  @deprecated used only by unit tests
+     */
+    InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg) {
         this(ctx, cfg, DummyValidator.getInstance());
     }
 
diff --git a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
index 28056f88035704a0d7c8a0d952e26f1a7eaec91d..134c76802e439815853f8f6f3df33002cfa97bda 100644
--- a/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
+++ b/router/java/src/net/i2p/router/tunnel/TunnelDispatcher.java
@@ -451,8 +451,8 @@ public class TunnelDispatcher implements Service {
     public void remove(TunnelCreatorConfig cfg) {
         if (cfg.isInbound()) {
             TunnelId recvId = cfg.getConfig(cfg.getLength()-1).getReceiveTunnel();
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("removing our own inbound " + cfg);
+            if (_log.shouldLog(Log.INFO))
+                _log.info("removing our own inbound " + cfg);
             TunnelParticipant participant = _participants.remove(recvId);
             if (participant == null) {
                 _inboundGateways.remove(recvId);
@@ -470,8 +470,8 @@ public class TunnelDispatcher implements Service {
                 }
             }
         } else {
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("removing our own outbound " + cfg);
+            if (_log.shouldLog(Log.INFO))
+                _log.info("removing our own outbound " + cfg);
             TunnelId outId = cfg.getConfig(0).getSendTunnel();
             TunnelGateway gw = _outboundGateways.remove(outId);
             if (gw != null) {
@@ -498,8 +498,8 @@ public class TunnelDispatcher implements Service {
         
         boolean removed = (null != _participatingConfig.remove(recvId));
         if (removed) {
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("removing " + cfg /* , new Exception() */ );
+            if (_log.shouldLog(Log.INFO))
+                _log.info("removing " + cfg /* , new Exception() */ );
         } else {
             // this is normal, this can get called twice
             if (_log.shouldLog(Log.DEBUG))
diff --git a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
index f36f449b74ce3ffe3f6d9921eaa1840f5caabf53..c28a531cf0e748bcae9abbc2a2d29d7b0c03c2b2 100644
--- a/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
+++ b/router/java/src/net/i2p/router/tunnel/pool/BuildHandler.java
@@ -448,8 +448,8 @@ class BuildHandler implements Runnable {
      */
     private long handleRequest(BuildMessageState state) {
         long timeSinceReceived = _context.clock().now()-state.recvTime;
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug(state.msg.getUniqueId() + ": handling request after " + timeSinceReceived);
+        //if (_log.shouldLog(Log.DEBUG))
+        //    _log.debug(state.msg.getUniqueId() + ": handling request after " + timeSinceReceived);
         
         Hash from = state.fromHash;
         if (from == null && state.from != null)
@@ -492,7 +492,7 @@ class BuildHandler implements Runnable {
         RouterInfo nextPeerInfo = _context.netDb().lookupRouterInfoLocally(nextPeer);
         long lookupTime = System.currentTimeMillis()-beforeLookup;
         if (lookupTime > 500 && _log.shouldLog(Log.WARN))
-            _log.warn("Took too long to lookup the request: " + lookupTime + "/" + readPeerTime + " for message " + state.msg.getUniqueId() + " received " + (timeSinceReceived+decryptTime) + " ago");
+            _log.warn("Took too long to lookup the request: " + lookupTime + "/" + readPeerTime + " for " + req);
         if (nextPeerInfo == null) {
             // limit concurrent next-hop lookups to prevent job queue overload attacks
             int numTunnels = _context.tunnelManager().getParticipatingCount();
@@ -500,7 +500,7 @@ class BuildHandler implements Runnable {
             int current = _currentLookups.incrementAndGet();
             if (current <= limit) {
                 if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Request " + state.msg.getUniqueId() + '/' + req.readReceiveTunnelId() + '/' + req.readNextTunnelId() 
+                    _log.debug("Request " + req
                                + " handled, lookup next peer " + nextPeer
                                + " lookups: " + current + '/' + limit);
                 _context.netDb().lookupRouterInfo(nextPeer, new HandleReq(_context, state, req, nextPeer),
@@ -508,7 +508,7 @@ class BuildHandler implements Runnable {
             } else {
                 _currentLookups.decrementAndGet();
                 if (_log.shouldLog(Log.WARN))
-                    _log.warn("Drop next hop lookup, limit " + limit);
+                    _log.warn("Drop next hop lookup, limit " + limit + ": " + req);
                 _context.statManager().addRateData("tunnel.dropLookupThrottle", 1);
             }
             if (from != null)
@@ -519,7 +519,7 @@ class BuildHandler implements Runnable {
             handleReq(nextPeerInfo, state, req, nextPeer);
             long handleTime = System.currentTimeMillis() - beforeHandle;
             if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Request " + state.msg.getUniqueId() + " handled and we know the next peer " 
+                _log.debug("Request " + req + " handled and we know the next peer " 
                            + nextPeer + " after " + handleTime
                            + "/" + decryptTime + "/" + lookupTime + "/" + timeSinceReceived);
             return handleTime;
@@ -560,7 +560,7 @@ class BuildHandler implements Runnable {
             // decrement in-progress counter
             _currentLookups.decrementAndGet();
             if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Request " + _state.msg.getUniqueId() + " handled with a successful deferred lookup for the next peer " + _nextPeer);
+                _log.debug("Request " + _state.msg.getUniqueId() + " handled with a successful deferred lookup: " + _req);
 
             RouterInfo ri = getContext().netDb().lookupRouterInfoLocally(_nextPeer);
             if (ri != null) {
@@ -568,7 +568,7 @@ class BuildHandler implements Runnable {
                 getContext().statManager().addRateData("tunnel.buildLookupSuccess", 1);
             } else {
                 if (_log.shouldLog(Log.WARN))
-                    _log.warn("Deferred successfully, but we couldnt find " + _nextPeer);
+                    _log.warn("Deferred successfully, but we couldnt find " + _nextPeer + "? " + _req);
                 getContext().statManager().addRateData("tunnel.buildLookupSuccess", 0);
             }
         }
@@ -593,15 +593,15 @@ class BuildHandler implements Runnable {
             _currentLookups.decrementAndGet();
             getContext().statManager().addRateData("tunnel.rejectTimeout", 1);
             getContext().statManager().addRateData("tunnel.buildLookupSuccess", 0);
-            // logging commented out so class can be static
-            //if (_log.shouldLog(Log.WARN))
-            //    _log.warn("Request " + _state.msg.getUniqueId() 
-            //              + " could no be satisfied, as the next peer could not be found: " + _nextPeer.toBase64());
+            if (_log.shouldLog(Log.WARN))
+                _log.warn("Next hop lookup failure: " + _req);
 
             // ???  should we blame the peer here?   getContext().profileManager().tunnelTimedOut(_nextPeer);
             getContext().messageHistory().tunnelRejected(_state.fromHash, new TunnelId(_req.readReceiveTunnelId()), _nextPeer, 
-                                                         "rejected because we couldn't find " + _nextPeer + ": " +
-                                                         _state.msg.getUniqueId() + "/" + _req.readNextTunnelId());
+                                                         // this is all disabled anyway
+                                                         //"rejected because we couldn't find " + _nextPeer + ": " +
+                                                         //_state.msg.getUniqueId() + "/" + _req.readNextTunnelId());
+                                                         "lookup fail");
         }
     }
     
@@ -654,7 +654,7 @@ class BuildHandler implements Runnable {
 
         if (isInGW && isOutEnd) {
             _context.statManager().addRateData("tunnel.rejectHostile", 1);
-            _log.error("Dropping build request, IBGW+OBEP");
+            _log.error("Dropping build request, IBGW+OBEP: " + req);
             if (from != null)
                 _context.commSystem().mayDisconnect(from);
             return;
@@ -667,7 +667,7 @@ class BuildHandler implements Runnable {
             // No way to recognize if we are every other hop, but see below
             // old i2pd
             if (_log.shouldWarn())
-                _log.warn("Dropping build request, we are the next hop");
+                _log.warn("Dropping build request, we are the next hop: " + req);
             if (from != null)
                 _context.commSystem().mayDisconnect(from);
             return;
@@ -678,7 +678,7 @@ class BuildHandler implements Runnable {
             // but if not, something is seriously wrong here.
             if (from == null || _context.routerHash().equals(from)) {
                 _context.statManager().addRateData("tunnel.rejectHostile", 1);
-                _log.error("Dropping build request, we are the previous hop");
+                _log.error("Dropping build request, we are the previous hop: " + req);
                 return;
             }
         }
@@ -689,7 +689,7 @@ class BuildHandler implements Runnable {
                 // i2pd does this
                 _context.statManager().addRateData("tunnel.rejectHostile", 1);
                 if (_log.shouldLog(Log.WARN))
-                    _log.warn("Dropping build request with the same previous and next hop");
+                    _log.warn("Dropping build request with the same previous and next hop: " + req);
                 _context.commSystem().mayDisconnect(from);
                 return;
             }
@@ -704,7 +704,7 @@ class BuildHandler implements Runnable {
         if (timeDiff > MAX_REQUEST_AGE) {
             _context.statManager().addRateData("tunnel.rejectTooOld", 1);
             if (_log.shouldLog(Log.WARN))
-                _log.warn("Dropping build request too old... replay attack? " + DataHelper.formatDuration(timeDiff));
+                _log.warn("Dropping build request too old... replay attack? " + DataHelper.formatDuration(timeDiff) + ": " + req);
             if (from != null)
                 _context.commSystem().mayDisconnect(from);
             return;
@@ -712,7 +712,7 @@ class BuildHandler implements Runnable {
         if (timeDiff < 0 - MAX_REQUEST_FUTURE) {
             _context.statManager().addRateData("tunnel.rejectFuture", 1);
             if (_log.shouldLog(Log.WARN))
-                _log.warn("Dropping build request too far in future " + DataHelper.formatDuration(0 - timeDiff));
+                _log.warn("Dropping build request too far in future " + DataHelper.formatDuration(0 - timeDiff) + ": " + req);
             if (from != null)
                 _context.commSystem().mayDisconnect(from);
             return;
@@ -791,7 +791,7 @@ class BuildHandler implements Runnable {
         if (response == 0 && !isInGW) {
             if (from != null && _throttler.shouldThrottle(from)) {
                 if (_log.shouldLog(Log.WARN))
-                    _log.warn("Rejecting tunnel (hop throttle), previous hop: " + from);
+                    _log.warn("Rejecting tunnel (hop throttle), previous hop: " + from + ": " + req);
                 // no setTunnelStatus() indication
                 _context.statManager().addRateData("tunnel.rejectHopThrottle", 1);
                 response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
@@ -800,17 +800,12 @@ class BuildHandler implements Runnable {
         if (response == 0 && (!isOutEnd) &&
             _throttler.shouldThrottle(nextPeer)) {
             if (_log.shouldLog(Log.WARN))
-                _log.warn("Rejecting tunnel (hop throttle), next hop: " + nextPeer);
+                _log.warn("Rejecting tunnel (hop throttle), next hop: " + req);
             _context.statManager().addRateData("tunnel.rejectHopThrottle", 1);
             // no setTunnelStatus() indication
             response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
         }
 
-        if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Responding to " + state.msg.getUniqueId() + "/" + ourId
-                       + " after " + recvDelay + " with " + response 
-                       + " from " + (from != null ? from : "tunnel"));
-
         HopConfig cfg = null;
         if (response == 0) {
             cfg = new HopConfig();
@@ -849,7 +844,7 @@ class BuildHandler implements Runnable {
                 success = _context.tunnelDispatcher().joinParticipant(cfg);
             if (success) {
                 if (_log.shouldLog(Log.DEBUG))
-                    _log.debug("Joining " + state.msg.getUniqueId() + "/" + cfg.getReceiveTunnel() + "/" + recvDelay + " as " + (isOutEnd ? "outbound endpoint" : isInGW ? "inbound gw" : "participant"));
+                    _log.debug("Joining: " + req);
             } else {
                 // Dup Tunnel ID. This can definitely happen (birthday paradox).
                 // Probability in 11 minutes (per hop type):
@@ -857,7 +852,7 @@ class BuildHandler implements Runnable {
                 response = TunnelHistory.TUNNEL_REJECT_BANDWIDTH;
                 _context.statManager().addRateData("tunnel.rejectDupID", 1);
                 if (_log.shouldLog(Log.WARN))
-                    _log.warn("DUP ID failure " + state.msg.getUniqueId() + "/" + cfg.getReceiveTunnel() + " as " + (isOutEnd ? "outbound endpoint" : isInGW ? "inbound gw" : "participant"));
+                    _log.warn("DUP ID failure: " + req);
             }
         }
 
@@ -866,10 +861,12 @@ class BuildHandler implements Runnable {
         if (response != 0) {
             _context.statManager().addRateData("tunnel.reject." + response, 1);
             _context.messageHistory().tunnelRejected(from, new TunnelId(ourId), nextPeer, 
-                                                     "rejecting for " + response + ": " +
-                                                     state.msg.getUniqueId() + "/" + ourId + "/" + req.readNextTunnelId() + " delay " +
-                                                     recvDelay + " as " +
-                                                     (isOutEnd ? "outbound endpoint" : isInGW ? "inbound gw" : "participant"));
+                                                     // this is all disabled anyway
+                                                     //"rejecting for " + response + ": " +
+                                                     //state.msg.getUniqueId() + "/" + ourId + "/" + req.readNextTunnelId() + " delay " +
+                                                     //recvDelay + " as " +
+                                                     //(isOutEnd ? "outbound endpoint" : isInGW ? "inbound gw" : "participant"));
+                                                     Integer.toString(response));
             if (from != null)
                 _context.commSystem().mayDisconnect(from);
             // Connection congestion control:
@@ -881,7 +878,7 @@ class BuildHandler implements Runnable {
                 (! _context.commSystem().isEstablished(nextPeer))) {
                 _context.statManager().addRateData("tunnel.dropConnLimits", 1);
                 if (_log.shouldLog(Log.WARN))
-                    _log.warn("Not sending rejection due to conn limits");
+                    _log.warn("Not sending rejection due to conn limits: " + req);
                 return;
             }
         } else if (isInGW && from != null) {
@@ -889,6 +886,11 @@ class BuildHandler implements Runnable {
             _context.commSystem().mayDisconnect(from);
         }
 
+        if (_log.shouldLog(Log.DEBUG))
+            _log.debug("Responding to " + state.msg.getUniqueId()
+                       + " after " + recvDelay + " with " + response 
+                       + " from " + (from != null ? from : "tunnel") + ": " + req);
+
         EncryptedBuildRecord reply = BuildResponseRecord.create(_context, response, req.readReplyKey(), req.readReplyIV(), state.msg.getUniqueId());
         int records = state.msg.getRecordCount();
         int ourSlot = -1;
@@ -904,19 +906,16 @@ class BuildHandler implements Runnable {
         }
 
         if (_log.shouldLog(Log.DEBUG))
-            _log.debug("Read slot " + ourSlot + " containing our hop @ " + _context.routerHash()
-                      + " accepted? " + response + " receiving on " + ourId 
-                      + " sending to " + nextId
-                      + " on " + nextPeer
-                      + " inGW? " + isInGW + " outEnd? " + isOutEnd
-                      + " recvDelay " + recvDelay + " replyMessage " + req.readReplyMessageId()
-                      + " replyKey " + req.readReplyKey() + " replyIV " + Base64.encode(req.readReplyIV()));
+            _log.debug("Read slot " + ourSlot + " containing: " + req
+                      + " accepted? " + response
+                      + " recvDelay " + recvDelay + " replyMessage " + req.readReplyMessageId());
 
         // now actually send the response
+        long expires = _context.clock().now() + NEXT_HOP_SEND_TIMEOUT;
         if (!isOutEnd) {
             state.msg.setUniqueId(req.readReplyMessageId());
-            state.msg.setMessageExpiration(_context.clock().now() + NEXT_HOP_SEND_TIMEOUT);
-            OutNetMessage msg = new OutNetMessage(_context, state.msg, state.msg.getMessageExpiration(), PRIORITY, nextPeerInfo);
+            state.msg.setMessageExpiration(expires);
+            OutNetMessage msg = new OutNetMessage(_context, state.msg, expires, PRIORITY, nextPeerInfo);
             if (response == 0)
                 msg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
             _context.outNetMessagePool().add(msg);
@@ -932,20 +931,20 @@ class BuildHandler implements Runnable {
             for (int i = 0; i < records; i++)
                 replyMsg.setRecord(i, state.msg.getRecord(i));
             replyMsg.setUniqueId(req.readReplyMessageId());
-            replyMsg.setMessageExpiration(_context.clock().now() + NEXT_HOP_SEND_TIMEOUT);
+            replyMsg.setMessageExpiration(expires);
             TunnelGatewayMessage m = new TunnelGatewayMessage(_context);
             m.setMessage(replyMsg);
-            m.setMessageExpiration(replyMsg.getMessageExpiration());
+            m.setMessageExpiration(expires);
             m.setTunnelId(new TunnelId(nextId));
             if (_context.routerHash().equals(nextPeer)) {
                 // ok, we are the gateway, so inject it
                 if (_log.shouldLog(Log.DEBUG))
                     _log.debug("We are the reply gateway for " + nextId
-                              + " when replying to replyMessage " + req.readReplyMessageId());
+                              + " when replying to replyMessage " + req);
                 _context.tunnelDispatcher().dispatch(m);
             } else {
                 // ok, the gateway is some other peer, shove 'er across
-                OutNetMessage outMsg = new OutNetMessage(_context, m, m.getMessageExpiration(), PRIORITY, nextPeerInfo);
+                OutNetMessage outMsg = new OutNetMessage(_context, m, expires, PRIORITY, nextPeerInfo);
                 if (response == 0)
                     outMsg.setOnFailedSendJob(new TunnelBuildNextHopFailJob(_context, cfg));
                 _context.outNetMessagePool().add(outMsg);
@@ -972,10 +971,10 @@ class BuildHandler implements Runnable {
             // endpoint, receiving the request at the last hop)
             long reqId = receivedMessage.getUniqueId();
             PooledTunnelCreatorConfig cfg = _exec.removeFromBuilding(reqId);
-            if (_log.shouldLog(Log.DEBUG))
-                _log.debug("Receive tunnel build message " + reqId + " from " 
-                           + (from != null ? from.calculateHash() : fromHash != null ? fromHash : "tunnels") 
-                           + ", found matching tunnel? " + (cfg != null));
+            //if (_log.shouldLog(Log.DEBUG))
+            //    _log.debug("Receive tunnel build message " + reqId + " from " 
+            //               + (from != null ? from.calculateHash() : fromHash != null ? fromHash : "tunnels") 
+            //               + ", found matching tunnel? " + (cfg != null));
             if (cfg != null) {
                 if (!cfg.isInbound()) {
                     // shouldnt happen - should we put it back?
@@ -1171,7 +1170,11 @@ class BuildHandler implements Runnable {
         public String getName() { return "Timeout contacting next peer for tunnel join"; }
 
         public void runJob() {
-            getContext().tunnelDispatcher().remove(_cfg);
+            //  TODO
+            //  This doesn't seem to be a reliable indication of actual failure,
+            //  as we sometimes get subsequent tunnel messages.
+            //  Until this is investigated and fixed, don't remove the tunnel.
+            //getContext().tunnelDispatcher().remove(_cfg);
             getContext().statManager().addRateData("tunnel.rejectTimeout2", 1);
             Log log = getContext().logManager().getLog(BuildHandler.class);
             if (log.shouldLog(Log.WARN))
diff --git a/router/java/test/junit/net/i2p/router/tunnel/BuildMessageTestStandalone.java b/router/java/test/junit/net/i2p/router/tunnel/BuildMessageTestStandalone.java
index 1ca7caea82ad28ce19a11ebcf2d3d4ffa15a050b..3479462215c1dc5778921310cdff9521a3f2fa4a 100644
--- a/router/java/test/junit/net/i2p/router/tunnel/BuildMessageTestStandalone.java
+++ b/router/java/test/junit/net/i2p/router/tunnel/BuildMessageTestStandalone.java
@@ -75,7 +75,7 @@ public class BuildMessageTestStandalone extends TestCase {
         for (int i = 0; i < cfg.getLength(); i++) {
             // this not only decrypts the current hop's record, but encrypts the other records
             // with the reply key
-            BuildRequestRecord req = proc.decrypt(ctx, msg, _peers[i], _privKeys[i]);
+            BuildRequestRecord req = proc.decrypt(msg, _peers[i], _privKeys[i]);
             // If false, no records matched the _peers[i], or the decryption failed
             assertTrue("foo @ " + i, req != null);
             long ourId = req.readReceiveTunnelId();