From 2741ac195d91c753a8a3952bcc9de2d1ae87a88f Mon Sep 17 00:00:00 2001
From: jrandom <jrandom>
Date: Tue, 28 Sep 2004 08:34:48 +0000
Subject: [PATCH] * protocol doc & impl cleanup * more defensive programming *
 more javadoc updates

---
 .../i2p/router/transport/TransportImpl.java   | 35 ++++++--
 .../transport/tcp/ConnectionBuilder.java      | 24 +++---
 .../transport/tcp/ConnectionHandler.java      | 86 ++++++++++++-------
 .../transport/tcp/ConnectionRunner.java       |  5 +-
 .../tcp/PersistentConnectionTagManager.java   |  2 +
 .../i2p/router/transport/tcp/TCPListener.java | 17 ++--
 .../router/transport/tcp/TCPTransport.java    | 59 ++++++-------
 .../net/i2p/router/transport/tcp/package.html |  3 +-
 8 files changed, 140 insertions(+), 91 deletions(-)

diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java
index 75c56d86c2..1b81b404e4 100644
--- a/router/java/src/net/i2p/router/transport/TransportImpl.java
+++ b/router/java/src/net/i2p/router/transport/TransportImpl.java
@@ -324,11 +324,36 @@ public abstract class TransportImpl implements Transport {
     }
  	
     /** What addresses are we currently listening to? */
-    public Set getCurrentAddresses() { return _currentAddresses; }
-    /** Add an address to our listening set */
-    protected void addCurrentAddress(RouterAddress address) { _currentAddresses.add(address); }
-    /** Remove an address from our listening set */
-    protected void removeCurrentAddress(RouterAddress address) { _currentAddresses.remove(address); }
+    public Set getCurrentAddresses() { 
+        synchronized (_currentAddresses) { 
+            return new HashSet(_currentAddresses); 
+        }
+    }
+    /**
+     * Replace any existing addresses for the current transport with the given
+     * one.
+     */
+    protected void replaceAddress(RouterAddress address) {
+        synchronized (_currentAddresses) {
+            Set addresses = _currentAddresses;
+            List toRemove = null;
+            for (Iterator iter = addresses.iterator(); iter.hasNext(); ) {
+                RouterAddress cur = (RouterAddress)iter.next();
+                if (getStyle().equals(cur.getTransportStyle())) {
+                    if (toRemove == null)
+                        toRemove = new ArrayList(1);
+                    toRemove.add(cur);
+                }
+            }
+            if (toRemove != null) {
+                for (int i = 0; i < toRemove.size(); i++) {
+                    addresses.remove(toRemove.get(i));
+                }
+            }
+            _currentAddresses.add(address);
+        }
+    }
+    
     /** Who to notify on message availability */
     public void setListener(TransportEventListener listener) { _listener = listener; }
     /** Make this stuff pretty (only used in the old console) */
diff --git a/router/java/src/net/i2p/router/transport/tcp/ConnectionBuilder.java b/router/java/src/net/i2p/router/transport/tcp/ConnectionBuilder.java
index 9a2bfa89d6..d65c8fa5a0 100644
--- a/router/java/src/net/i2p/router/transport/tcp/ConnectionBuilder.java
+++ b/router/java/src/net/i2p/router/transport/tcp/ConnectionBuilder.java
@@ -168,6 +168,7 @@ public class ConnectionBuilder {
         if (!ok) return;
     }
     
+    
     /**
      * Send <code>#bytesFollowing + #versions + v1 [+ v2 [etc]] + 
      * tag? + tagData + properties</code>
@@ -181,10 +182,10 @@ public class ConnectionBuilder {
                 DataHelper.writeLong(baos, 1, TCPTransport.SUPPORTED_PROTOCOLS[i]);
             }
             if (_connectionTag != null) {
-                baos.write(0x1);
+                baos.write(ConnectionHandler.FLAG_TAG_FOLLOWING);
                 baos.write(_connectionTag.getData());
             } else {
-                baos.write(0x0);
+                baos.write(ConnectionHandler.FLAG_TAG_NOT_FOLLOWING);
             }
             DataHelper.writeProperties(baos, null); // no options atm
             byte line[] = baos.toByteArray();
@@ -219,8 +220,7 @@ public class ConnectionBuilder {
         try {
             // #bytesFollowing + versionOk + #bytesIP + IP + tagOk? + nonce + properties
             int numBytes = (int)DataHelper.readLong(_rawIn, 2);
-            // 0xFFFF is a reserved value
-            if ( (numBytes <= 0) || (numBytes >= 0xFFFF) )
+            if ( (numBytes <= 0) || (numBytes >= ConnectionHandler.FLAG_TEST) )
                 throw new IOException("Invalid number of bytes in response");
             
             byte line[] = new byte[numBytes];
@@ -244,7 +244,7 @@ public class ConnectionBuilder {
                     break;
                 }
             }
-            if (_agreedProtocol == -1) {
+            if (_agreedProtocol == ConnectionHandler.FLAG_PROTOCOL_NONE) {
                 fail("No valid protocol versions to contact "
                      + _target.getIdentity().calculateHash().toBase64().substring(0,6));
                 return false;
@@ -259,7 +259,7 @@ public class ConnectionBuilder {
             _transport.ourAddressReceived(_localIP);
             
             int tagOk = (int)DataHelper.readLong(bais, 1);
-            if ( (tagOk == 0x01) && (_connectionTag != null) ) {
+            if ( (tagOk == ConnectionHandler.FLAG_TAG_OK) && (_connectionTag != null) ) {
                 // tag is ok
             } else {
                 _connectionTag = null;
@@ -305,6 +305,8 @@ public class ConnectionBuilder {
     /** Set the next tag to <code>H(E(nonce + tag, sessionKey))</code> */
     private void updateNextTagExisting() {
         byte pre[] = new byte[48];
+        System.arraycopy(_nonce.getData(), 0, pre, 0, 4);
+        System.arraycopy(_connectionTag.getData(), 0, pre, 4, 32);
         byte encr[] = _context.AESEngine().encrypt(pre, _key, _iv);
         Hash h = _context.sha().calculateHash(encr);
         _nextConnectionTag = new ByteArray(h.getData());
@@ -601,23 +603,23 @@ public class ConnectionBuilder {
      */
     private boolean validateStatus(int status) {
         switch (status) {
-            case -1:
+            case -1: // EOF
                 fail("Error reading the status from " 
                      + _target.getIdentity().calculateHash().toBase64().substring(0,6));
                 return false;
-            case 0: // ok
+            case ConnectionHandler.STATUS_OK:
                 return true;
-            case 1: // not reachable
+            case ConnectionHandler.STATUS_UNREACHABLE:
                 fail("According to " 
                      + _target.getIdentity().calculateHash().toBase64().substring(0,6)
                      + ", we are not reachable on " + _localIP + ":" + _transport.getPort());
                 return false;
-            case 2: // clock skew
+            case ConnectionHandler.STATUS_SKEWED:
                 fail("According to " 
                      + _target.getIdentity().calculateHash().toBase64().substring(0,6)
                      + ", our clock is off");
                 return false;
-            case 3: // signature failure (only for new sessions)
+            case ConnectionHandler.STATUS_SIGNATURE_FAILED: // (only for new sessions)
                 fail("Signature failure talking to " 
                      + _target.getIdentity().calculateHash().toBase64().substring(0,6));
                 return false;
diff --git a/router/java/src/net/i2p/router/transport/tcp/ConnectionHandler.java b/router/java/src/net/i2p/router/transport/tcp/ConnectionHandler.java
index 1593b6dda0..8b35003377 100644
--- a/router/java/src/net/i2p/router/transport/tcp/ConnectionHandler.java
+++ b/router/java/src/net/i2p/router/transport/tcp/ConnectionHandler.java
@@ -80,6 +80,31 @@ public class ConnectionHandler {
     private SessionKey _key;
     /** initialization vector for the encryption */
     private byte[] _iv;
+
+    /** for reading/comparing, this is the #bytes sent if we are being tested */
+    public static final int FLAG_TEST = 0xFFFF;
+    /** protocol version sent if no protocols are ok */
+    public static final byte FLAG_PROTOCOL_NONE = 0x0;
+    /** alice is sending a tag to bob */
+    public static final byte FLAG_TAG_FOLLOWING = 0x1;
+    /** alice is not sending a tag to bob */
+    public static final byte FLAG_TAG_NOT_FOLLOWING = 0x0;
+    /** the connection tag is ok (we have an available key for it) */
+    public static final byte FLAG_TAG_OK = 0x1;
+    /** the connection tag is not ok (must go with a full DH) */
+    public static final byte FLAG_TAG_NOT_OK = 0x0;
+    /** dunno why the peer is bad */
+    public static final int STATUS_UNKNOWN = -1;
+    /** the peer's public addresses could not be verified */
+    public static final int STATUS_UNREACHABLE = 1;
+    /** the peer's clock is too far skewed */
+    public static final int STATUS_SKEWED = 2;
+    /** the peer's signature failed (either some crazy corruption or MITM) */
+    public static final int STATUS_SIGNATURE_FAILED = 3;
+    /** the peer is fine */
+    public static final int STATUS_OK = 0;
+    
+    private static final int MAX_VERSIONS = 255;
     
     public ConnectionHandler(RouterContext ctx, TCPTransport transport, Socket socket) {
         _context = ctx;
@@ -173,8 +198,8 @@ public class ConnectionHandler {
             int numBytes = (int)DataHelper.readLong(_rawIn, 2);
             if (numBytes <= 0)
                 throw new IOException("Invalid number of bytes in connection");
-            // 0xFFFF is a reserved value identifying the connection as a reachability test
-            if (numBytes == 0xFFFF) {
+            // reachability test
+            if (numBytes == FLAG_TEST) {
                 if (_log.shouldLog(Log.DEBUG))
                     _log.debug("ReadProtocol[Y]: test called, handle it");
                 handleTest();
@@ -194,7 +219,7 @@ public class ConnectionHandler {
             ByteArrayInputStream bais = new ByteArrayInputStream(line);
             
             int numVersions = (int)DataHelper.readLong(bais, 1);
-            if ( (numVersions <= 0) || (numVersions > 0x8) ) {
+            if ( (numVersions <= 0) || (numVersions > MAX_VERSIONS) ) {
                 fail("Invalid number of protocol versions from " + _from);
                 return false;
             }
@@ -212,7 +237,7 @@ public class ConnectionHandler {
             }
             
             int tag = (int)DataHelper.readLong(bais, 1);
-            if (tag == 0x1) {
+            if (tag == FLAG_TAG_FOLLOWING) {
                 byte tagData[] = new byte[32];
                 read = DataHelper.read(bais, tagData);
                 if (read != 32)
@@ -248,7 +273,7 @@ public class ConnectionHandler {
         try {
             ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
             if (_agreedProtocol <= 0)
-                baos.write(0x0);
+                baos.write(FLAG_PROTOCOL_NONE);
             else
                 baos.write(_agreedProtocol);
             
@@ -257,9 +282,9 @@ public class ConnectionHandler {
             baos.write(ip);
             
             if (_key != null)
-                baos.write(0x1);
+                baos.write(FLAG_TAG_OK);
             else
-                baos.write(0x0);
+                baos.write(FLAG_TAG_NOT_OK);
             
             byte nonce[] = new byte[4];
             _context.random().nextBytes(nonce);
@@ -301,6 +326,8 @@ public class ConnectionHandler {
     /** Set the next tag to <code>H(E(nonce + tag, sessionKey))</code> */
     private void updateNextTagExisting() {
         byte pre[] = new byte[48];
+        System.arraycopy(_nonce.getData(), 0, pre, 0, 4);
+        System.arraycopy(_connectionTag.getData(), 0, pre, 4, 32);
         byte encr[] = _context.AESEngine().encrypt(pre, _key, _iv);
         Hash h = _context.sha().calculateHash(encr);
         _nextConnectionTag = new ByteArray(h.getData());
@@ -313,7 +340,7 @@ public class ConnectionHandler {
      * @return true if the connection went ok, or false if it failed.
      */
     private boolean connectExistingSession() { 
-        // iv to the SHA256 of the tag appended by the nonce.
+        // iv = H(tag+nonce)
         byte data[] = new byte[36];
         System.arraycopy(_connectionTag.getData(), 0, data, 0, 32);
         System.arraycopy(_nonce.getData(), 0, data, 32, 4);
@@ -407,16 +434,16 @@ public class ConnectionHandler {
             
             Properties props = new Properties();
             
-            int status = -1;
+            int status = STATUS_UNKNOWN;
             if (!reachable) {
-                status = 1;
+                status = STATUS_UNREACHABLE;
             } else if ( (clockSkew > Router.CLOCK_FUDGE_FACTOR) 
                         || (clockSkew < 0 - Router.CLOCK_FUDGE_FACTOR) ) {
-                status = 2;
+                status = STATUS_SKEWED;
                 SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddhhmmssSSS");
                 props.setProperty("SKEW", fmt.format(new Date(_context.clock().now())));
             } else {
-                status = 0;
+                status = STATUS_OK;
             }
             
             baos.write(status);
@@ -559,18 +586,18 @@ public class ConnectionHandler {
             
             Properties props = new Properties();
             
-            int status = -1;
+            int status = STATUS_UNKNOWN;
             if (!reachable) {
-                status = 1;
+                status = STATUS_UNREACHABLE;
             } else if ( (clockSkew > Router.CLOCK_FUDGE_FACTOR) 
                         || (clockSkew < 0 - Router.CLOCK_FUDGE_FACTOR) ) {
-                status = 2;
+                status = STATUS_SKEWED;
                 SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMddhhmmssSSS");
                 props.setProperty("SKEW", fmt.format(new Date(_context.clock().now())));
             } else if (!sigOk) {
-                status = 3;
+                status = STATUS_SIGNATURE_FAILED;
             } else {
-                status = 0;
+                status = STATUS_OK;
             }
             
             baos.write(status);
@@ -608,17 +635,17 @@ public class ConnectionHandler {
      */
     private boolean handleStatus(int status, long clockSkew) {
         switch (status) {
-            case 0: // ok
+            case STATUS_OK:
                 return true;
-            case 1:
+            case STATUS_UNREACHABLE:
                 fail("Peer " + _actualPeer.getIdentity().calculateHash().toBase64().substring(0,6)
                      + " at " + _from + " is unreachable");
                 return false;
-            case 2:
+            case STATUS_SKEWED:
                 fail("Peer " + _actualPeer.getIdentity().calculateHash().toBase64().substring(0,6)
                      + " was skewed by " + DataHelper.formatDuration(clockSkew));
                 return false;
-            case 3:
+            case STATUS_SIGNATURE_FAILED:
                 fail("Forged signature on " + _from + " pretending to be "
                      + _actualPeer.getIdentity().calculateHash().toBase64().substring(0,6));
                 return false;
@@ -655,8 +682,7 @@ public class ConnectionHandler {
                 _log.debug("Beginning verification of reachability");
             
             // send: 0xFFFF + #versions + v1 [+ v2 [etc]] + properties
-            out.write(0xFF);
-            out.write(0xFF);
+            DataHelper.writeLong(out, 2, FLAG_TEST);
             out.write(TCPTransport.SUPPORTED_PROTOCOLS.length);
             for (int i = 0; i < TCPTransport.SUPPORTED_PROTOCOLS.length; i++) 
                 out.write(TCPTransport.SUPPORTED_PROTOCOLS[i]);
@@ -667,16 +693,13 @@ public class ConnectionHandler {
                 _log.debug("Verification of reachability request sent");
             
             // read: 0xFFFF + versionOk + #bytesIP + IP + currentTime + properties
-            int ok = in.read();
-            if (ok != 0xFF)
-                throw new IOException("Unable to verify the peer - invalid response");
-            ok = in.read();
-            if (ok != 0xFF)
+            int flag = (int)DataHelper.readLong(in, 2);
+            if (flag != FLAG_TEST)
                 throw new IOException("Unable to verify the peer - invalid response");
             int version = in.read();
             if (version == -1)
                 throw new IOException("Unable to verify the peer - invalid version");
-            if (version == 0)
+            if (version == FLAG_PROTOCOL_NONE)
                 throw new IOException("Unable to verify the peer - no matching version");
             int numBytes = in.read();
             if ( (numBytes == -1) || (numBytes > 32) )
@@ -715,7 +738,7 @@ public class ConnectionHandler {
             // read: #versions + v1 [+ v2 [etc]] + properties
             int numVersions = _rawIn.read();
             if (numVersions == -1) throw new IOException("Unable to read versions");
-            if (numVersions > 256) throw new IOException("Too many versions");
+            if (numVersions > MAX_VERSIONS) throw new IOException("Too many versions");
             int versions[] = new int[numVersions];
             for (int i = 0; i < numVersions; i++) {
                 versions[i] = _rawIn.read();
@@ -738,8 +761,7 @@ public class ConnectionHandler {
                 _log.debug("HandleTest: version=" + version + " opts=" +opts);
             
             // send: 0xFFFF + versionOk + #bytesIP + IP + currentTime + properties
-            _rawOut.write(0xFF);
-            _rawOut.write(0xFF);
+            DataHelper.writeLong(_rawOut, 2, FLAG_TEST);
             _rawOut.write(version);
             byte ip[] = _from.getBytes();
             _rawOut.write(ip.length);
diff --git a/router/java/src/net/i2p/router/transport/tcp/ConnectionRunner.java b/router/java/src/net/i2p/router/transport/tcp/ConnectionRunner.java
index 11ba15c326..b60f3017e9 100644
--- a/router/java/src/net/i2p/router/transport/tcp/ConnectionRunner.java
+++ b/router/java/src/net/i2p/router/transport/tcp/ConnectionRunner.java
@@ -44,8 +44,9 @@ class ConnectionRunner implements Runnable {
     public void run() {
         while (_keepRunning && !_con.getIsClosed()) {
             OutNetMessage msg = _con.getNextMessage();
-            if ( (msg == null) && (_keepRunning) ) {
-                _log.error("next message is null but we should keep running?");
+            if (msg == null) {
+                if (_keepRunning)
+                    _log.error("next message is null but we should keep running?");
             } else {
                 sendMessage(msg);
             }
diff --git a/router/java/src/net/i2p/router/transport/tcp/PersistentConnectionTagManager.java b/router/java/src/net/i2p/router/transport/tcp/PersistentConnectionTagManager.java
index eef5716b80..0c9c0d3c6d 100644
--- a/router/java/src/net/i2p/router/transport/tcp/PersistentConnectionTagManager.java
+++ b/router/java/src/net/i2p/router/transport/tcp/PersistentConnectionTagManager.java
@@ -19,6 +19,8 @@ import net.i2p.router.RouterContext;
 import net.i2p.util.Log;
 
 /**
+ * Simple persistent impl writing the connection tags to connectionTag.keys
+ * (or another file specified via "i2np.tcp.tagFile")
  *
  */
 public class PersistentConnectionTagManager extends ConnectionTagManager {
diff --git a/router/java/src/net/i2p/router/transport/tcp/TCPListener.java b/router/java/src/net/i2p/router/transport/tcp/TCPListener.java
index 9487f1eb6e..10b0f81908 100644
--- a/router/java/src/net/i2p/router/transport/tcp/TCPListener.java
+++ b/router/java/src/net/i2p/router/transport/tcp/TCPListener.java
@@ -34,7 +34,9 @@ class TCPListener {
     private ServerSocket _socket;
     private ListenerRunner _listener;
     private RouterContext _context;
+    /** Client Sockets that have been received but not yet handled (oldest first) */
     private List _pendingSockets;
+    /** List of SocketHandler runners if we're listening (else an empty list) */
     private List _handlers;
     
     /**
@@ -61,6 +63,7 @@ class TCPListener {
         _handlers = new ArrayList(CONCURRENT_HANDLERS);
     }
         
+    /** Make sure we are listening on the transport's getMyAddress() */
     public void startListening() {
         TCPAddress addr = _transport.getMyAddress();
         if ( (addr != null) && (addr.getHost() != null) && (addr.getPort() > 0) ) {
@@ -149,9 +152,10 @@ class TCPListener {
                     curDelay = 0;
                     loop();
                 } catch (IOException ioe) {
-                    if (_log.shouldLog(Log.WARN))
-                        _log.warn("Error listening to tcp connection " + _myAddress.getHost() + ":" 
-                                   + _myAddress.getPort(), ioe);
+                    if (_isRunning && _context.router().isAlive())
+                        if (_log.shouldLog(Log.ERROR))
+                            _log.error("Error listening to tcp connection " + _myAddress.getHost() + ":" 
+                                       + _myAddress.getPort(), ioe);
                 }
                 
                 if (_socket != null) {
@@ -167,12 +171,13 @@ class TCPListener {
                 if (_nextFailDelay > MAX_FAIL_DELAY)
                     _nextFailDelay = MAX_FAIL_DELAY;
             }
-            if (_log.shouldLog(Log.ERROR))
-                _log.error("CANCELING TCP LISTEN.  delay = " + curDelay);
+            if (_isRunning && _context.router().isAlive())
+                if (_log.shouldLog(Log.ERROR))
+                    _log.error("CANCELING TCP LISTEN.  delay = " + curDelay);
             _isRunning = false;
         }
         private void loop() {
-            while (_isRunning) {
+            while (_isRunning && _context.router().isAlive()) {
                 try {
                     if (_log.shouldLog(Log.INFO))
                         _log.info("Waiting for a connection on " + _myAddress.getHost() + ":" + _myAddress.getPort());
diff --git a/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java b/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java
index 9aff2a2a7a..da2339a3dd 100644
--- a/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java
+++ b/router/java/src/net/i2p/router/transport/tcp/TCPTransport.java
@@ -270,25 +270,30 @@ public class TCPTransport extends TransportImpl {
      *
      * @param address address that the remote host said was ours
      */
-    synchronized void ourAddressReceived(String address) {
-        if (allowAddressUpdate()) {
-            int port = getPort();
-            TCPAddress addr = new TCPAddress(address, port);
-            if (addr.getPort() > 0) {
-                if (allowAddress(addr)) {
-                    if (_myAddress != null) {
-                        if (addr.getAddress().equals(_myAddress.getAddress())) {
-                            // ignore, since there is no change
-                            return;
+    void ourAddressReceived(String address) {
+        synchronized (_listener) { // no need to lock on the whole TCPTransport
+            if (allowAddressUpdate()) {
+                int port = getPort();
+                TCPAddress addr = new TCPAddress(address, port);
+                if (addr.getPort() > 0) {
+                    if (allowAddress(addr)) {
+                        if (_myAddress != null) {
+                            if (addr.getAddress().equals(_myAddress.getAddress())) {
+                                // ignore, since there is no change
+                                return;
+                            }
                         }
+                        if (_log.shouldLog(Log.INFO))
+                            _log.info("Update our local address to " + address);
+                        updateAddress(addr);
                     }
-                    if (_log.shouldLog(Log.INFO))
-                        _log.info("Update our local address to " + address);
-                    updateAddress(addr);
+                } else {
+                    if (_log.shouldLog(Log.ERROR))
+                        _log.error("Address specified is not valid [" + address + ":" + port + "]");
                 }
             } else {
-                if (_log.shouldLog(Log.ERROR))
-                    _log.error("Address specified is not valid [" + address + ":" + port + "]");
+                // either we have explicitly specified our IP address, or 
+                // we are already connected to some people.
             }
         }
     }
@@ -321,8 +326,8 @@ public class TCPTransport extends TransportImpl {
     
     /**
      * Add the given message to the list of most recent connection 
-     * establishment error messages.  This should include a timestamp of 
-     * some sort in it.
+     * establishment error messages.  A timestamp is prefixed to it before
+     * being rendered on the router console.
      *
      */
     void addConnectionErrorMessage(String msg) {
@@ -394,22 +399,7 @@ public class TCPTransport extends TransportImpl {
         _myAddress = addr;
         _listener.stopListening();
         
-        Set addresses = getCurrentAddresses();
-        List toRemove = null;
-        for (Iterator iter = addresses.iterator(); iter.hasNext(); ) {
-            RouterAddress cur = (RouterAddress)iter.next();
-            if (STYLE.equals(cur.getTransportStyle())) {
-                if (toRemove == null)
-                    toRemove = new ArrayList(1);
-                toRemove.add(cur);
-            }
-        }
-        if (toRemove != null) {
-            for (int i = 0; i < toRemove.size(); i++) {
-                addresses.remove(toRemove.get(i));
-            }
-        }
-        addresses.add(routerAddr);
+        replaceAddress(routerAddr);
         
         _context.router().rebuildRouterInfo();
       
@@ -492,7 +482,7 @@ public class TCPTransport extends TransportImpl {
      *
      */
     RouterInfo getNextPeer() {
-        while (true) {
+        while (_context.router().isAlive()) {
             synchronized (_connectionLock) {
                 for (Iterator iter = _pendingMessages.keySet().iterator(); iter.hasNext(); ) {
                     Hash peer = (Hash)iter.next();
@@ -526,6 +516,7 @@ public class TCPTransport extends TransportImpl {
                 } catch (InterruptedException ie) {}
             }
         }
+        return null;
     }
     
     /** Called after an establisher finished (or failed) connecting to the peer */
diff --git a/router/java/src/net/i2p/router/transport/tcp/package.html b/router/java/src/net/i2p/router/transport/tcp/package.html
index b2249087bc..7df3c1952f 100644
--- a/router/java/src/net/i2p/router/transport/tcp/package.html
+++ b/router/java/src/net/i2p/router/transport/tcp/package.html
@@ -64,7 +64,8 @@ remainder of the communication is AES256 encrypted per
 <p><b>8) </b> <i>Bob to Alice</i>: <br />
    <code>routerInfo + status + properties + H(routerInfo + status + properties + nonce + tag)</code></p>
 <p><b>9) </b> If the <code>status</code> is ok, both Alice and Bob consume the 
-   <code>tagData</code>, updating the next tag to be <code>H(E(nonce + tag, sessionKey))</code>.
+   <code>tagData</code>, updating the next tag to be <code>H(E(nonce + tag, sessionKey))</code>
+   (with nonce+tag padded with 12 bytes of 0x0 at the end).
    Otherwise, both sides disconnect and do not consume the tag.  In addition, on error the 
    <code>properties</code> mapping has a more detailed reason under the key "MESSAGE".</p>
    
-- 
GitLab