From 5c3e408772b61dc6a537ed407fef66c56f1215bc Mon Sep 17 00:00:00 2001
From: zzz <zzz@mail.i2p>
Date: Tue, 31 Jul 2018 14:13:33 +0000
Subject: [PATCH] i2psnark: Don't disconnect seeds immediately if comments
 enabled (ticket #2288) Implement variable timeout Hardcode handshake bytes
 Log tweaks

---
 .../java/src/org/klomp/snark/Peer.java        | 22 ++++++++++------
 .../src/org/klomp/snark/PeerCheckerTask.java  |  2 +-
 .../src/org/klomp/snark/PeerCoordinator.java  | 13 ++++++----
 .../java/src/org/klomp/snark/PeerState.java   | 25 ++++++++++++++++++-
 4 files changed, 47 insertions(+), 15 deletions(-)

diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java
index 73588d12a9..d1a573e749 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java
@@ -79,6 +79,7 @@ public class Peer implements Comparable<Peer>
   private long uploaded_old[] = {-1,-1,-1};
   private long downloaded_old[] = {-1,-1,-1};
 
+  private static final byte[] HANDSHAKE = DataHelper.getASCII("BitTorrent protocol");
   //  bytes per bt spec:                         0011223344556677
   private static final long OPTION_EXTENSION = 0x0000000000100000l;
   private static final long OPTION_FAST      = 0x0000000000000004l;
@@ -343,8 +344,8 @@ public class Peer implements Comparable<Peer>
     dout = new DataOutputStream(out);
     
     // Handshake write - header
-    dout.write(19);
-    dout.write("BitTorrent protocol".getBytes("UTF-8"));
+    dout.write(HANDSHAKE.length);
+    dout.write(HANDSHAKE);
     // Handshake write - options
     long myOptions = OPTION_EXTENSION;
     // we can't handle HAVE_ALL or HAVE_NONE if we don't know the number of pieces
@@ -365,17 +366,15 @@ public class Peer implements Comparable<Peer>
     
     // Handshake read - header
     byte b = din.readByte();
-    if (b != 19)
+    if (b != HANDSHAKE.length)
       throw new IOException("Handshake failure, expected 19, got "
                             + (b & 0xff) + " on " + sock);
     
-    byte[] bs = new byte[19];
+    byte[] bs = new byte[HANDSHAKE.length];
     din.readFully(bs);
-    String bittorrentProtocol = new String(bs, "UTF-8");
-    if (!"BitTorrent protocol".equals(bittorrentProtocol))
+    if (!Arrays.equals(HANDSHAKE, bs))
       throw new IOException("Handshake failure, expected "
-                            + "'BitTorrent protocol', got '"
-                            + bittorrentProtocol + "'");
+                            + "'BitTorrent protocol'");
     
     // Handshake read - options
     options = din.readLong();
@@ -684,6 +683,13 @@ public class Peer implements Comparable<Peer>
           return -1; //"no state";
       }
   }
+  
+  /** @since 0.9.36 */
+  public long getMaxInactiveTime() {
+      return isCompleted() && !isInteresting() ?
+             PeerCoordinator.MAX_SEED_INACTIVE :
+             PeerCoordinator.MAX_INACTIVE;
+  }
 
   /**
    * Send keepalive
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java
index 6aacfede49..91e21fa6f8 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCheckerTask.java
@@ -96,7 +96,7 @@ class PeerCheckerTask implements Runnable
                 continue;
               }
 
-            if (peer.getInactiveTime() > PeerCoordinator.MAX_INACTIVE) {
+            if (peer.getInactiveTime() > peer.getMaxInactiveTime()) {
                 if (_log.shouldLog(Log.WARN))
                     _log.warn("Disconnecting peer idle " +
                               DataHelper.formatDuration(peer.getInactiveTime()) + ": " + peer);
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
index 285af4d19d..e21891cadf 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java
@@ -75,6 +75,7 @@ class PeerCoordinator implements PeerListener
   final static long CHECK_PERIOD = 40*1000; // 40 seconds
   final static int MAX_UPLOADERS = 8;
   public static final long MAX_INACTIVE = 8*60*1000;
+  public static final long MAX_SEED_INACTIVE = 2*60*1000;
 
   /**
    * Approximation of the number of current uploaders (unchoked peers),
@@ -496,7 +497,7 @@ class PeerCoordinator implements PeerListener
     synchronized(peers)
       {
         Peer old = peerIDInList(peer.getPeerID(), peers);
-        if ( (old != null) && (old.getInactiveTime() > MAX_INACTIVE) ) {
+        if (old != null && old.getInactiveTime() > old.getMaxInactiveTime()) {
             // idle for 8 minutes, kill the old con (32KB/8min = 68B/sec minimum for one block)
             if (_log.shouldLog(Log.WARN))
               _log.warn("Remomving old peer: " + peer + ": " + old + ", inactive for " + old.getInactiveTime());
@@ -592,8 +593,10 @@ class PeerCoordinator implements PeerListener
         // thus there is an additional check in connected()
         need_more = (!peer.isConnected()) && peersize < getMaxConnections();
         // Check if we already have this peer before we build the connection
-        Peer old = peerIDInList(peer.getPeerID(), peers);
-        need_more = need_more && ((old == null) || (old.getInactiveTime() > MAX_INACTIVE));
+        if (need_more) {
+            Peer old = peerIDInList(peer.getPeerID(), peers);
+            need_more = old == null || old.getInactiveTime() > old.getMaxInactiveTime();
+        }
       }
 
     if (need_more)
@@ -629,9 +632,9 @@ class PeerCoordinator implements PeerListener
       }
     if (_log.shouldLog(Log.DEBUG)) {
       if (peer.isConnected())
-        _log.info("Add peer already connected: " + peer);
+        _log.debug("Add peer already connected: " + peer);
       else
-        _log.info("Connections: " + peersize + "/" + getMaxConnections()
+        _log.debug("Connections: " + peersize + "/" + getMaxConnections()
                   + " not accepting extra peer: " + peer);
     }
     return false;
diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
index 2e3edf1640..9f6b9a65b1 100644
--- a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
+++ b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java
@@ -25,12 +25,16 @@ import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 import net.i2p.I2PAppContext;
 import net.i2p.data.ByteArray;
 import net.i2p.util.Log;
 
+import org.klomp.snark.bencode.BEValue;
+import org.klomp.snark.bencode.InvalidBEncodingException;
+
 class PeerState implements DataLoader
 {
   private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(PeerState.class);
@@ -242,14 +246,33 @@ class PeerState implements DataLoader
     }  // synch
 
     boolean interest = listener.gotBitField(peer, bitfield);
-    setInteresting(interest);
     if (bitfield.complete() && !interest) {
         // They are seeding and we are seeding,
         // why did they contact us? (robert)
         // Dump them quick before we send our whole bitmap
+
+        // If we both support comments, allow it
+        if (listener.getUtil().utCommentsEnabled()) {
+            Map<String, BEValue> handshake = peer.getHandshakeMap();
+            if (handshake != null) {
+                BEValue bev = handshake.get("m");
+                if (bev != null) {
+                    try {
+                        if (bev.getMap().get(ExtensionHandler.TYPE_COMMENT) != null) {
+                            if (_log.shouldLog(Log.WARN))
+                                _log.warn("Allowing seed that connects to seeds for comments: " + peer);
+                            setInteresting(interest);
+                            return;
+                        }
+                    } catch (InvalidBEncodingException ibee) {}
+                }
+            }
+        }
         if (_log.shouldLog(Log.WARN))
             _log.warn("Disconnecting seed that connects to seeds: " + peer);
         peer.disconnect(true);
+    } else {
+        setInteresting(interest);
     }
   }
 
-- 
GitLab