From 1fc288917a5bc519d0684ef030dfd8590b0f4556 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 14 Apr 2010 14:05:25 +0000 Subject: [PATCH 01/49] back to updater --- build.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/build.xml b/build.xml index 911c75d01..f463ba506 100644 --- a/build.xml +++ b/build.xml @@ -255,8 +255,7 @@ - - + From fc6306575db517c06c51580dffce9725dcbba23e Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 5 Jul 2010 16:01:50 +0000 Subject: [PATCH 02/49] new classes, requires Java 1.6 to build --- .../src/net/i2p/util/SecureDirectory.java | 62 ++++++++++++++++ .../net/i2p/util/SecureFileOutputStream.java | 72 +++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 core/java/src/net/i2p/util/SecureDirectory.java create mode 100644 core/java/src/net/i2p/util/SecureFileOutputStream.java diff --git a/core/java/src/net/i2p/util/SecureDirectory.java b/core/java/src/net/i2p/util/SecureDirectory.java new file mode 100644 index 000000000..393e11886 --- /dev/null +++ b/core/java/src/net/i2p/util/SecureDirectory.java @@ -0,0 +1,62 @@ +package net.i2p.util; + +import java.io.File; + +/** + * Same as File but sets the file mode after mkdir() so it can + * be read and written by the owner only (i.e. 700 on linux) + * + * @since 0.8.1 + * @author zzz + */ +public class SecureDirectory extends File { + + private static final boolean canSetPerms = + (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0; + private static final boolean isNotWindows = !System.getProperty("os.name").startsWith("Win"); + + public SecureDirectory(String pathname) { + super(pathname); + } + + public SecureDirectory(String parent, String child) { + super(parent, child); + } + + public SecureDirectory(File parent, String child) { + super(parent, child); + } + + /** + * Sets directory to mode 700 if the directory is created + */ + @Override + public boolean mkdir() { + boolean rv = super.mkdir(); + if (rv) + setPerms(); + return rv; + } + + /** + * Tries to set the permissions to 700, + * ignores errors + */ + private void setPerms() { + if (!canSetPerms) + return; + try { + setReadable(false, false); + setReadable(true, true); + setWritable(false, false); + setWritable(true, true); + if (isNotWindows) { + setExecutable(false, false); + setExecutable(true, true); + } + } catch (Throwable t) { + // NoSuchMethodException or NoSuchMethodError if we somehow got the + // version detection wrong or the JVM doesn't support it + } + } +} diff --git a/core/java/src/net/i2p/util/SecureFileOutputStream.java b/core/java/src/net/i2p/util/SecureFileOutputStream.java new file mode 100644 index 000000000..dd02f2d32 --- /dev/null +++ b/core/java/src/net/i2p/util/SecureFileOutputStream.java @@ -0,0 +1,72 @@ +package net.i2p.util; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; + +/** + * Same as FileOutputStream but sets the file mode so it can only + * be read and written by the owner only (i.e. 600 on linux) + * + * @since 0.8.1 + * @author zzz + */ +public class SecureFileOutputStream extends FileOutputStream { + + private static final boolean canSetPerms = + (new VersionComparator()).compare(System.getProperty("java.version"), "1.6") >= 0; + + /** + * Sets output file to mode 600 + */ + public SecureFileOutputStream(String file) throws FileNotFoundException { + super(file); + setPerms(new File(file)); + } + + /** + * Sets output file to mode 600 only if append = false + * (otherwise it is presumed to be 600 already) + */ + public SecureFileOutputStream(String file, boolean append) throws FileNotFoundException { + super(file, append); + if (!append) + setPerms(new File(file)); + } + + /** + * Sets output file to mode 600 + */ + public SecureFileOutputStream(File file) throws FileNotFoundException { + super(file); + setPerms(file); + } + + /** + * Sets output file to mode 600 only if append = false + * (otherwise it is presumed to be 600 already) + */ + public SecureFileOutputStream(File file, boolean append) throws FileNotFoundException { + super(file, append); + if (!append) + setPerms(file); + } + + /** + * Tries to set the permissions to 600, + * ignores errors + */ + private static void setPerms(File f) { + if (!canSetPerms) + return; + try { + f.setReadable(false, false); + f.setReadable(true, true); + f.setWritable(false, false); + f.setWritable(true, true); + } catch (Throwable t) { + // NoSuchMethodException or NoSuchMethodError if we somehow got the + // version detection wrong or the JVM doesn't support it + } + } +} From 50bda941adfb105915c8d5d0fb30f1fdb29e5c02 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 5 Jul 2010 16:03:13 +0000 Subject: [PATCH 03/49] make most classes package local; stub out a session destroy message; javadoc --- .../i2p/router/transport/udp/ACKBitfield.java | 2 +- .../i2p/router/transport/udp/ACKSender.java | 2 +- .../router/transport/udp/DummyThrottle.java | 2 +- .../transport/udp/EstablishmentManager.java | 2 +- .../transport/udp/InboundEstablishState.java | 2 +- .../udp/InboundMessageFragments.java | 2 +- .../transport/udp/InboundMessageState.java | 2 +- .../transport/udp/IntroductionManager.java | 2 +- .../router/transport/udp/MessageQueue.java | 2 +- .../router/transport/udp/MessageReceiver.java | 2 +- .../transport/udp/OutboundEstablishState.java | 2 +- .../udp/OutboundMessageFragments.java | 2 +- .../transport/udp/OutboundMessageState.java | 2 +- .../transport/udp/OutboundRefiller.java | 2 +- .../router/transport/udp/PacketBuilder.java | 91 ++++++++++++++++++- .../router/transport/udp/PacketHandler.java | 6 +- .../router/transport/udp/PacketPusher.java | 2 +- .../i2p/router/transport/udp/PeerState.java | 2 +- .../TimedWeightedPriorityMessageQueue.java | 2 +- .../i2p/router/transport/udp/UDPEndpoint.java | 2 +- .../i2p/router/transport/udp/UDPPacket.java | 4 +- .../router/transport/udp/UDPPacketReader.java | 7 +- .../i2p/router/transport/udp/UDPReceiver.java | 2 +- .../i2p/router/transport/udp/UDPSender.java | 2 +- 24 files changed, 121 insertions(+), 27 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/udp/ACKBitfield.java b/router/java/src/net/i2p/router/transport/udp/ACKBitfield.java index f0201c4e8..c8b7c9630 100644 --- a/router/java/src/net/i2p/router/transport/udp/ACKBitfield.java +++ b/router/java/src/net/i2p/router/transport/udp/ACKBitfield.java @@ -4,7 +4,7 @@ package net.i2p.router.transport.udp; * Generic means of SACK/NACK transmission for partially or fully * received messages */ -public interface ACKBitfield { +interface ACKBitfield { /** what message is this partially ACKing? */ public long getMessageId(); /** how many fragments are covered in this bitfield? */ diff --git a/router/java/src/net/i2p/router/transport/udp/ACKSender.java b/router/java/src/net/i2p/router/transport/udp/ACKSender.java index 180232d6f..d6035766d 100644 --- a/router/java/src/net/i2p/router/transport/udp/ACKSender.java +++ b/router/java/src/net/i2p/router/transport/udp/ACKSender.java @@ -15,7 +15,7 @@ import net.i2p.util.Log; * any outstanding ACKs. * */ -public class ACKSender implements Runnable { +class ACKSender implements Runnable { private RouterContext _context; private Log _log; private UDPTransport _transport; diff --git a/router/java/src/net/i2p/router/transport/udp/DummyThrottle.java b/router/java/src/net/i2p/router/transport/udp/DummyThrottle.java index 383f807c5..d2cb73934 100644 --- a/router/java/src/net/i2p/router/transport/udp/DummyThrottle.java +++ b/router/java/src/net/i2p/router/transport/udp/DummyThrottle.java @@ -14,7 +14,7 @@ import net.i2p.router.OutNetMessage; * * @since 0.7.12 */ -public class DummyThrottle implements OutboundMessageFragments.ActiveThrottle { +class DummyThrottle implements OutboundMessageFragments.ActiveThrottle { public DummyThrottle() { } diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index 2e1fea71f..275c200c2 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -33,7 +33,7 @@ import net.i2p.util.SimpleTimer; * as well as to drop any failed establishment attempts. * */ -public class EstablishmentManager { +class EstablishmentManager { private final RouterContext _context; private final Log _log; private final UDPTransport _transport; diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java index 829f62ee0..6f4f53980 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java @@ -20,7 +20,7 @@ import net.i2p.util.Log; * we are Bob. * */ -public class InboundEstablishState { +class InboundEstablishState { private final RouterContext _context; private final Log _log; // SessionRequest message diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java index ddd44b143..bc0ddc5f5 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java @@ -17,7 +17,7 @@ import net.i2p.util.Log; * basic line of defense here). * */ -public class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{ +class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{ private RouterContext _context; private Log _log; /** list of message IDs recently received, so we can ignore in flight dups */ diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java index 3159cad87..9261dc70a 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageState.java @@ -11,7 +11,7 @@ import net.i2p.util.Log; * Hold the raw data fragments of an inbound message * */ -public class InboundMessageState { +class InboundMessageState { private RouterContext _context; private Log _log; private long _messageId; diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java index 6a2707f60..c59668866 100644 --- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java +++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java @@ -20,7 +20,7 @@ import net.i2p.util.Log; /** * */ -public class IntroductionManager { +class IntroductionManager { private RouterContext _context; private Log _log; private UDPTransport _transport; diff --git a/router/java/src/net/i2p/router/transport/udp/MessageQueue.java b/router/java/src/net/i2p/router/transport/udp/MessageQueue.java index cc38ebbaf..3d51055ca 100644 --- a/router/java/src/net/i2p/router/transport/udp/MessageQueue.java +++ b/router/java/src/net/i2p/router/transport/udp/MessageQueue.java @@ -5,7 +5,7 @@ import net.i2p.router.OutNetMessage; /** * Base queue for messages not yet packetized */ -public interface MessageQueue { +interface MessageQueue { /** * Get the next message, blocking until one is found or the expiration * reached. diff --git a/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java b/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java index 1a95c227f..08b6088c4 100644 --- a/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java +++ b/router/java/src/net/i2p/router/transport/udp/MessageReceiver.java @@ -19,7 +19,7 @@ import net.i2p.util.Log; * parse 'em into I2NPMessages, and stick them on the * {@link net.i2p.router.InNetMessagePool} by way of the {@link UDPTransport}. */ -public class MessageReceiver { +class MessageReceiver { private RouterContext _context; private Log _log; private UDPTransport _transport; diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java index 6e3738f30..1e23a210c 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundEstablishState.java @@ -21,7 +21,7 @@ import net.i2p.util.Log; * they are Bob. * */ -public class OutboundEstablishState { +class OutboundEstablishState { private final RouterContext _context; private final Log _log; // SessionRequest message diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java index 2d4f27292..e25ab0369 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundMessageFragments.java @@ -22,7 +22,7 @@ import net.i2p.util.Log; * message. * */ -public class OutboundMessageFragments { +class OutboundMessageFragments { private RouterContext _context; private Log _log; private UDPTransport _transport; diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java b/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java index 877ad10b1..a545cfb06 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundMessageState.java @@ -15,7 +15,7 @@ import net.i2p.util.Log; * Maintain the outbound fragmentation for resending * */ -public class OutboundMessageState { +class OutboundMessageState { private I2PAppContext _context; private Log _log; /** may be null if we are part of the establishment */ diff --git a/router/java/src/net/i2p/router/transport/udp/OutboundRefiller.java b/router/java/src/net/i2p/router/transport/udp/OutboundRefiller.java index 841588ee4..078105a4a 100644 --- a/router/java/src/net/i2p/router/transport/udp/OutboundRefiller.java +++ b/router/java/src/net/i2p/router/transport/udp/OutboundRefiller.java @@ -12,7 +12,7 @@ import net.i2p.util.Log; * WARNING - UNUSED since 0.6.1.11 * */ -public class OutboundRefiller implements Runnable { +class OutboundRefiller implements Runnable { private RouterContext _context; private Log _log; private OutboundMessageFragments _fragments; diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java index c0d9e22f3..672744444 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -22,8 +22,80 @@ import net.i2p.util.Log; * Big ol' class to do all our packet formatting. The UDPPackets generated are * fully authenticated, encrypted, and configured for delivery to the peer. * + * The following is from udp.html on the website: + +

+All UDP datagrams begin with a 16 byte MAC (Message Authentication Code) +and a 16 byte IV (Initialization Vector +followed by a variable +size payload encrypted with the appropriate key. The MAC used is +HMAC-MD5, truncated to 16 bytes, while the key is a full 32 byte AES256 +key. The specific construct of the MAC is the first 16 bytes from:

+
+  HMAC-MD5(payload || IV || (payloadLength ^ protocolVersion), macKey)
+
+ +

The protocol version is currently 0.

+ +

The payload itself is AES256/CBC encrypted with the IV and the +sessionKey, with replay prevention addressed within its body, +explained below. The payloadLength in the MAC is a 2 byte unsigned +integer in 2s complement.

+ +

The protocolVersion is a 2 byte unsigned integer in 2s complement, +and currently set to 0. Peers using a different protocol version will +not be able to communicate with this peer, though earlier versions not +using this flag are.

+ +

Payload

+ +

Within the AES encrypted payload, there is a minimal common structure +to the various messages - a one byte flag and a four byte sending +timestamp (*seconds* since the unix epoch). The flag byte contains +the following bitfields:

+
+  bits 0-3: payload type
+     bit 4: rekey?
+     bit 5: extended options included
+  bits 6-7: reserved
+
+ +

If the rekey flag is set, 64 bytes of keying material follow the +timestamp. If the extended options flag is set, a one byte option +size value is appended to, followed by that many extended option +bytes, which are currently uninterpreted.

+ +

When rekeying, the first 32 bytes of the keying material is fed +into a SHA256 to produce the new MAC key, and the next 32 bytes are +fed into a SHA256 to produce the new session key, though the keys are +not immediately used. The other side should also reply with the +rekey flag set and that same keying material. Once both sides have +sent and received those values, the new keys should be used and the +previous keys discarded. It may be useful to keep the old keys +around briefly, to address packet loss and reordering.

+ +

NOTE: Rekeying is currently unimplemented.

+ +
+ Header: 37+ bytes
+ +----+----+----+----+----+----+----+----+
+ |                  MAC                  |
+ |                                       |
+ +----+----+----+----+----+----+----+----+
+ |                   IV                  |
+ |                                       |
+ +----+----+----+----+----+----+----+----+
+ |flag|        time       | (optionally  |
+ +----+----+----+----+----+              |
+ | this may have 64 byte keying material |
+ | and/or a one+N byte extended options) |
+ +---------------------------------------|
+
+ + * + * */ -public class PacketBuilder { +class PacketBuilder { private I2PAppContext _context; private Log _log; private UDPTransport _transport; @@ -62,10 +134,16 @@ public class PacketBuilder { _context.statManager().createRateStat("udp.packetAuthTimeSlow", "How long it takes to encrypt and MAC a packet for sending (when its slow)", "udp", UDPTransport.RATES); } +/**** public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer) { return buildPacket(state, fragment, peer, null, null); } +****/ + /** + * This builds a data packet (PAYLOAD_TYPE_DATA). + * See the methods below for the other message types. + * * @param ackIdsRemaining list of messageIds (Long) that should be acked by this packet. * The list itself is passed by reference, and if a messageId is * transmitted and the sender does not want the ID to be included @@ -231,8 +309,10 @@ public class PacketBuilder { return packet; } - // We use this for keepalive purposes. - // It doesn't generate a reply, but that's ok. + /** + * We use this for keepalive purposes. + * It doesn't generate a reply, but that's ok. + */ public UDPPacket buildPing(PeerState peer) { return buildACK(peer, Collections.EMPTY_LIST); } @@ -1018,6 +1098,9 @@ public class PacketBuilder { return packet; } + /** + * Sends an empty unauthenticated packet for hole punching + */ public UDPPacket buildHolePunch(UDPPacketReader reader) { UDPPacket packet = UDPPacket.acquire(_context, false); byte data[] = packet.getPacket().getData(); @@ -1051,7 +1134,7 @@ public class PacketBuilder { return packet; } - private void setTo(UDPPacket packet, InetAddress ip, int port) { + private static void setTo(UDPPacket packet, InetAddress ip, int port) { packet.getPacket().setAddress(ip); packet.getPacket().setPort(port); } diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java index 1a35c5d19..15dc16457 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java @@ -19,7 +19,7 @@ import net.i2p.util.Log; * receiver's queue and pushing them as necessary. * */ -public class PacketHandler { +class PacketHandler { private RouterContext _context; private Log _log; private UDPTransport _transport; @@ -525,6 +525,10 @@ public class PacketHandler { _establisher.receiveRelayResponse(from, reader); _context.statManager().addRateData("udp.receivePacketSize.relayResponse", packet.getPacket().getLength(), packet.getLifetime()); break; + case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY: + _state = 53; + //_TODO + break; default: _state = 52; if (_log.shouldLog(Log.WARN)) diff --git a/router/java/src/net/i2p/router/transport/udp/PacketPusher.java b/router/java/src/net/i2p/router/transport/udp/PacketPusher.java index 271e83597..50b8d3ba7 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketPusher.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketPusher.java @@ -9,7 +9,7 @@ import net.i2p.util.Log; * pool and toss 'em onto the outbound packet queue * */ -public class PacketPusher implements Runnable { +class PacketPusher implements Runnable { // private RouterContext _context; private Log _log; private OutboundMessageFragments _fragments; diff --git a/router/java/src/net/i2p/router/transport/udp/PeerState.java b/router/java/src/net/i2p/router/transport/udp/PeerState.java index 65fb1c014..6c1dec3e4 100644 --- a/router/java/src/net/i2p/router/transport/udp/PeerState.java +++ b/router/java/src/net/i2p/router/transport/udp/PeerState.java @@ -23,7 +23,7 @@ import net.i2p.util.ConcurrentHashSet; * Contain all of the state about a UDP connection to a peer. * */ -public class PeerState { +class PeerState { private RouterContext _context; private Log _log; /** diff --git a/router/java/src/net/i2p/router/transport/udp/TimedWeightedPriorityMessageQueue.java b/router/java/src/net/i2p/router/transport/udp/TimedWeightedPriorityMessageQueue.java index acd0bd0b5..9721f22cc 100644 --- a/router/java/src/net/i2p/router/transport/udp/TimedWeightedPriorityMessageQueue.java +++ b/router/java/src/net/i2p/router/transport/udp/TimedWeightedPriorityMessageQueue.java @@ -20,7 +20,7 @@ import net.i2p.util.Log; * See comments in DQAT.java and mtn history ca. 2006-02-19 * */ -public class TimedWeightedPriorityMessageQueue implements MessageQueue, OutboundMessageFragments.ActiveThrottle { +class TimedWeightedPriorityMessageQueue implements MessageQueue, OutboundMessageFragments.ActiveThrottle { private RouterContext _context; private Log _log; /** FIFO queue of messages in a particular priority */ diff --git a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java index ac9369d8e..fc2426700 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java @@ -11,7 +11,7 @@ import net.i2p.util.Log; * Coordinate the low level datagram socket, managing the UDPSender and * UDPReceiver */ -public class UDPEndpoint { +class UDPEndpoint { private RouterContext _context; private Log _log; private int _listenPort; diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java index 5dc001e18..1548e6036 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java @@ -16,7 +16,7 @@ import net.i2p.util.Log; * of object instances to allow rapid reuse. * */ -public class UDPPacket { +class UDPPacket { private I2PAppContext _context; private static Log _log; private volatile DatagramPacket _packet; @@ -63,6 +63,8 @@ public class UDPPacket { public static final int PAYLOAD_TYPE_RELAY_INTRO = 5; public static final int PAYLOAD_TYPE_DATA = 6; public static final int PAYLOAD_TYPE_TEST = 7; + /** @since 0.8.1 */ + public static final int PAYLOAD_TYPE_SESSION_DESTROY = 8; // various flag fields for use in the data packets public static final byte DATA_FLAG_EXPLICIT_ACK = (byte)(1 << 7); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java index 395a2fcf0..0b65b2d47 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java @@ -15,7 +15,7 @@ import net.i2p.util.Log; * elements, grab the appropriate subreader. * */ -public class UDPPacketReader { +class UDPPacketReader { private I2PAppContext _context; private Log _log; private byte _message[]; @@ -125,6 +125,8 @@ public class UDPPacketReader { return "Relay request packet"; case UDPPacket.PAYLOAD_TYPE_RELAY_RESPONSE: return "Relay response packet"; + case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY: + return "Session destroyed packet"; default: return "Other packet type..."; } @@ -135,6 +137,8 @@ public class UDPPacketReader { buf.append(Base64.encode(_message, _payloadBeginOffset, _payloadLength)); } + /* ------- Begin Reader Classes ------- */ + /** Help read the SessionRequest payload */ public class SessionRequestReader { public static final int X_LENGTH = 256; @@ -747,6 +751,7 @@ public class UDPPacketReader { } } + /* ------- End Reader Classes ------- */ public static void main(String args[]) { I2PAppContext ctx = I2PAppContext.getGlobalContext(); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java index 88cb20779..0a1937db1 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPReceiver.java @@ -19,7 +19,7 @@ import net.i2p.util.SimpleTimer; * from the queue ASAP by a {@link PacketHandler} * */ -public class UDPReceiver { +class UDPReceiver { private RouterContext _context; private Log _log; private DatagramSocket _socket; diff --git a/router/java/src/net/i2p/router/transport/udp/UDPSender.java b/router/java/src/net/i2p/router/transport/udp/UDPSender.java index 745b50df4..01d10ed1c 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPSender.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPSender.java @@ -15,7 +15,7 @@ import net.i2p.util.Log; * Lowest level packet sender, pushes anything on its queue ASAP. * */ -public class UDPSender { +class UDPSender { private RouterContext _context; private Log _log; private DatagramSocket _socket; From a1524241cb3c8d70bd7975ce732fd86bc0a1c307 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 6 Jul 2010 15:22:48 +0000 Subject: [PATCH 04/49] Set permissions on directories and files when they are created --- .../src/net/i2p/addressbook/ConfigParser.java | 8 +++--- .../java/src/net/i2p/addressbook/Daemon.java | 7 ++--- .../src/org/klomp/snark/I2PSnarkUtil.java | 3 ++- .../src/org/klomp/snark/SnarkManager.java | 5 ++-- .../net/i2p/i2ptunnel/TunnelController.java | 3 ++- .../i2p/i2ptunnel/TunnelControllerGroup.java | 3 ++- .../src/net/i2p/router/web/PluginStarter.java | 2 +- .../i2p/router/web/PluginUpdateHandler.java | 5 ++-- .../i2p/router/web/RouterConsoleRunner.java | 7 ++--- .../src/net/i2p/router/web/WebAppStarter.java | 5 ++-- .../src/i2p/susi/dns/AddressbookBean.java | 3 ++- .../src/java/src/i2p/susi/dns/ConfigBean.java | 4 +-- .../src/i2p/susi/dns/SubscriptionsBean.java | 5 ++-- core/java/src/net/i2p/I2PAppContext.java | 13 ++++----- core/java/src/net/i2p/data/DataHelper.java | 5 ++-- core/java/src/net/i2p/util/LogWriter.java | 6 ++--- core/java/src/net/i2p/util/RandomSource.java | 2 +- .../src/net/i2p/util/SecureDirectory.java | 12 +++++++++ .../java/src/net/i2p/router/KeyManager.java | 6 +++-- router/java/src/net/i2p/router/Router.java | 7 +++-- .../kademlia/PersistentDataStore.java | 6 +++-- .../i2p/router/networkdb/reseed/Reseeder.java | 6 +++-- .../peermanager/ProfilePersistenceHelper.java | 7 ++--- .../i2p/router/startup/ClientAppConfig.java | 3 ++- .../router/startup/CreateRouterInfoJob.java | 5 ++-- .../router/startup/RebuildRouterInfoJob.java | 3 ++- .../net/i2p/router/startup/WorkingDir.java | 27 ++++++++++--------- 27 files changed, 105 insertions(+), 63 deletions(-) diff --git a/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java b/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java index be74de990..6c95e2e19 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/ConfigParser.java @@ -25,9 +25,9 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; -import java.io.FileWriter; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.io.StringReader; import java.util.HashMap; import java.util.Iterator; @@ -35,6 +35,8 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import net.i2p.util.SecureFileOutputStream; + /** * Utility class providing methods to parse and write files in config file * format, and subscription file format. @@ -277,7 +279,7 @@ public class ConfigParser { */ public static void write(Map map, File file) throws IOException { ConfigParser - .write(map, new BufferedWriter(new FileWriter(file, false))); + .write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8"))); } /** @@ -316,7 +318,7 @@ public class ConfigParser { public static void writeSubscriptions(List list, File file) throws IOException { ConfigParser.writeSubscriptions(list, new BufferedWriter( - new FileWriter(file, false))); + new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8"))); } } diff --git a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java index 077acdb7f..258801011 100644 --- a/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java +++ b/apps/addressbook/java/src/net/i2p/addressbook/Daemon.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.Map; import net.i2p.I2PAppContext; +import net.i2p.util.SecureDirectory; /** * Main class of addressbook. Performs updates, and runs the main loop. @@ -131,11 +132,11 @@ public class Daemon { String settingsLocation = "config.txt"; File homeFile; if (args.length > 0) { - homeFile = new File(args[0]); + homeFile = new SecureDirectory(args[0]); if (!homeFile.isAbsolute()) - homeFile = new File(I2PAppContext.getGlobalContext().getRouterDir(), args[0]); + homeFile = new SecureDirectory(I2PAppContext.getGlobalContext().getRouterDir(), args[0]); } else { - homeFile = new File(System.getProperty("user.dir")); + homeFile = new SecureDirectory(System.getProperty("user.dir")); } Map defaultSettings = new HashMap(); diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index 127a2bb19..b0e26bfe6 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -26,6 +26,7 @@ import net.i2p.util.ConcurrentHashSet; import net.i2p.util.EepGet; import net.i2p.util.FileUtil; import net.i2p.util.Log; +import net.i2p.util.SecureDirectory; import net.i2p.util.SimpleScheduler; import net.i2p.util.SimpleTimer; import net.i2p.util.Translate; @@ -77,7 +78,7 @@ public class I2PSnarkUtil { // This is used for both announce replies and .torrent file downloads, // so it must be available even if not connected to I2CP. // so much for multiple instances - _tmpDir = new File(ctx.getTempDir(), "i2psnark"); + _tmpDir = new SecureDirectory(ctx.getTempDir(), "i2psnark"); FileUtil.rmdir(_tmpDir, false); _tmpDir.mkdirs(); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 59ab86a12..a1ddaf25d 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -21,6 +21,7 @@ import net.i2p.data.DataHelper; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; import net.i2p.util.OrderedProperties; +import net.i2p.util.SecureDirectory; /** * Manage multiple snarks @@ -130,9 +131,9 @@ public class SnarkManager implements Snark.CompleteListener { } public File getDataDir() { String dir = _config.getProperty(PROP_DIR, "i2psnark"); - File f = new File(dir); + File f = new SecureDirectory(dir); if (!f.isAbsolute()) - f = new File(_context.getAppDir(), dir); + f = new SecureDirectory(_context.getAppDir(), dir); return f; } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 7235507ba..5b7fd3529 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -18,6 +18,7 @@ import net.i2p.data.Base32; import net.i2p.data.Destination; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; +import net.i2p.util.SecureFileOutputStream; /** * Coordinate the runtime operation and configuration of a tunnel. @@ -84,7 +85,7 @@ public class TunnelController implements Logging { } FileOutputStream fos = null; try { - fos = new FileOutputStream(keyFile); + fos = new SecureFileOutputStream(keyFile); Destination dest = client.createDestination(fos); String destStr = dest.toBase64(); log("Private key created and saved in " + keyFile.getAbsolutePath()); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java index 112f0d62f..7b0c67f70 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelControllerGroup.java @@ -20,6 +20,7 @@ import net.i2p.client.I2PSessionException; import net.i2p.data.DataHelper; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; +import net.i2p.util.SecureFileOutputStream; /** * Coordinate a set of tunnels within the JVM, loading and storing their config @@ -254,7 +255,7 @@ public class TunnelControllerGroup { FileOutputStream fos = null; try { - fos = new FileOutputStream(cfgFile); + fos = new SecureFileOutputStream(cfgFile); fos.write(buf.toString().getBytes("UTF-8")); if (_log.shouldLog(Log.INFO)) _log.info("Config written to " + cfgFile.getPath()); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java index b17dfbdad..6958b350e 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginStarter.java @@ -563,7 +563,7 @@ public class PluginStarter implements Runnable { /** * http://jimlife.wordpress.com/2007/12/19/java-adding-new-classpath-at-runtime/ */ - public static void addPath(URL u) throws Exception { + private static void addPath(URL u) throws Exception { URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader(); Class urlClass = URLClassLoader.class; Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class}); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java index 26d026a16..38e8f28b0 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/PluginUpdateHandler.java @@ -16,6 +16,7 @@ import net.i2p.util.FileUtil; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; import net.i2p.util.OrderedProperties; +import net.i2p.util.SecureDirectory; import net.i2p.util.SimpleScheduler; import net.i2p.util.SimpleTimer; import net.i2p.util.VersionComparator; @@ -150,7 +151,7 @@ public class PluginUpdateHandler extends UpdateHandler { public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { updateStatus("" + _("Plugin downloaded") + ""); File f = new File(_updateFile); - File appDir = new File(_context.getConfigDir(), PLUGIN_DIR); + File appDir = new SecureDirectory(_context.getConfigDir(), PLUGIN_DIR); if ((!appDir.exists()) && (!appDir.mkdir())) { f.delete(); statusDone("" + _("Cannot create plugin directory {0}", appDir.getAbsolutePath()) + ""); @@ -273,7 +274,7 @@ public class PluginUpdateHandler extends UpdateHandler { return; } - File destDir = new File(appDir, appName); + File destDir = new SecureDirectory(appDir, appName); if (destDir.exists()) { if (Boolean.valueOf(props.getProperty("install-only")).booleanValue()) { to.delete(); diff --git a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java index 05bedee37..68533f7ef 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java @@ -14,6 +14,7 @@ import net.i2p.data.DataHelper; import net.i2p.router.RouterContext; import net.i2p.util.FileUtil; import net.i2p.util.I2PAppThread; +import net.i2p.util.SecureDirectory; import org.mortbay.http.DigestAuthenticator; import org.mortbay.http.HashUserRealm; @@ -62,7 +63,7 @@ public class RouterConsoleRunner { } public void startConsole() { - File workDir = new File(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work"); + File workDir = new SecureDirectory(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work"); boolean workDirRemoved = FileUtil.rmdir(workDir, false); if (!workDirRemoved) System.err.println("ERROR: Unable to remove Jetty temporary work directory"); @@ -115,7 +116,7 @@ public class RouterConsoleRunner { } _server.setRootWebApp(ROUTERCONSOLE); WebApplicationContext wac = _server.addWebApplication("/", _webAppsDir + ROUTERCONSOLE + ".war"); - File tmpdir = new File(workDir, ROUTERCONSOLE + "-" + _listenPort); + File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" + _listenPort); tmpdir.mkdir(); wac.setTempDirectory(tmpdir); baseHandler = new LocaleWebAppHandler(I2PAppContext.getGlobalContext()); @@ -130,7 +131,7 @@ public class RouterConsoleRunner { String enabled = props.getProperty(PREFIX + appName + ENABLED); if (! "false".equals(enabled)) { String path = new File(dir, fileNames[i]).getCanonicalPath(); - tmpdir = new File(workDir, appName + "-" + _listenPort); + tmpdir = new SecureDirectory(workDir, appName + "-" + _listenPort); WebAppStarter.addWebApp(I2PAppContext.getGlobalContext(), _server, appName, path, tmpdir); if (enabled == null) { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/WebAppStarter.java b/apps/routerconsole/java/src/net/i2p/router/web/WebAppStarter.java index 0264d6307..1aac8ecc9 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/WebAppStarter.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/WebAppStarter.java @@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap; import net.i2p.I2PAppContext; import net.i2p.util.FileUtil; +import net.i2p.util.SecureDirectory; import org.mortbay.http.HttpContext; import org.mortbay.http.HttpListener; @@ -41,7 +42,7 @@ public class WebAppStarter { * @throws just about anything, caller would be wise to catch Throwable */ static void startWebApp(I2PAppContext ctx, Server server, String appName, String warPath) throws Exception { - File tmpdir = new File(ctx.getTempDir(), "jetty-work-" + appName + ctx.random().nextInt()); + File tmpdir = new SecureDirectory(ctx.getTempDir(), "jetty-work-" + appName + ctx.random().nextInt()); WebApplicationContext wac = addWebApp(ctx, server, appName, warPath, tmpdir); wac.start(); } @@ -73,7 +74,7 @@ public class WebAppStarter { warModTimes.put(warPath, new Long(newmod)); } else if (oldmod.longValue() < newmod) { // copy war to temporary directory - File warTmpDir = new File(ctx.getTempDir(), "war-copy-" + appName + ctx.random().nextInt()); + File warTmpDir = new SecureDirectory(ctx.getTempDir(), "war-copy-" + appName + ctx.random().nextInt()); warTmpDir.mkdir(); String tmpPath = (new File(warTmpDir, appName + ".war")).getAbsolutePath(); if (!FileUtil.copy(warPath, tmpPath, true)) diff --git a/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java b/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java index 63c4db85a..b6af8a12d 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/AddressbookBean.java @@ -38,6 +38,7 @@ import java.util.Properties; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Destination; +import net.i2p.util.SecureFileOutputStream; public class AddressbookBean { @@ -330,7 +331,7 @@ public class AddressbookBean { String filename = properties.getProperty( getBook() + "_addressbook" ); - FileOutputStream fos = new FileOutputStream( ConfigBean.addressbookPrefix + filename ); + FileOutputStream fos = new SecureFileOutputStream( ConfigBean.addressbookPrefix + filename ); addressbook.store( fos, null ); try { fos.close(); diff --git a/apps/susidns/src/java/src/i2p/susi/dns/ConfigBean.java b/apps/susidns/src/java/src/i2p/susi/dns/ConfigBean.java index f6655757d..ee6575356 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/ConfigBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/ConfigBean.java @@ -27,13 +27,13 @@ package i2p.susi.dns; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.io.Serializable; import net.i2p.I2PAppContext; +import net.i2p.util.SecureFileOutputStream; public class ConfigBean implements Serializable { @@ -111,7 +111,7 @@ public class ConfigBean implements Serializable { { File file = new File( configFileName ); try { - PrintWriter out = new PrintWriter( new FileOutputStream( file ) ); + PrintWriter out = new PrintWriter( new SecureFileOutputStream( file ) ); out.print( config ); out.flush(); out.close(); diff --git a/apps/susidns/src/java/src/i2p/susi/dns/SubscriptionsBean.java b/apps/susidns/src/java/src/i2p/susi/dns/SubscriptionsBean.java index 6b1a80576..176561aca 100644 --- a/apps/susidns/src/java/src/i2p/susi/dns/SubscriptionsBean.java +++ b/apps/susidns/src/java/src/i2p/susi/dns/SubscriptionsBean.java @@ -28,12 +28,13 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.Properties; +import net.i2p.util.SecureFileOutputStream; + public class SubscriptionsBean { private String action, fileName, content, serial, lastSerial; @@ -113,7 +114,7 @@ public class SubscriptionsBean { File file = new File( getFileName() ); try { - PrintWriter out = new PrintWriter( new FileOutputStream( file ) ); + PrintWriter out = new PrintWriter( new SecureFileOutputStream( file ) ); out.print( content ); out.flush(); out.close(); diff --git a/core/java/src/net/i2p/I2PAppContext.java b/core/java/src/net/i2p/I2PAppContext.java index aac61d4be..751cbb37a 100644 --- a/core/java/src/net/i2p/I2PAppContext.java +++ b/core/java/src/net/i2p/I2PAppContext.java @@ -31,6 +31,7 @@ import net.i2p.util.KeyRing; import net.i2p.util.LogManager; //import net.i2p.util.PooledRandomSource; import net.i2p.util.RandomSource; +import net.i2p.util.SecureDirectory; /** *

Provide a base scope for accessing singletons that I2P exposes. Rather than @@ -217,7 +218,7 @@ public class I2PAppContext { // config defaults to base s = getProperty("i2p.dir.config"); if (s != null) { - _configDir = new File(s); + _configDir = new SecureDirectory(s); if (!_configDir.exists()) _configDir.mkdir(); } else { @@ -226,7 +227,7 @@ public class I2PAppContext { // router defaults to config s = getProperty("i2p.dir.router"); if (s != null) { - _routerDir = new File(s); + _routerDir = new SecureDirectory(s); if (!_routerDir.exists()) _routerDir.mkdir(); } else { @@ -240,7 +241,7 @@ public class I2PAppContext { // these all default to router s = getProperty("i2p.dir.log"); if (s != null) { - _logDir = new File(s); + _logDir = new SecureDirectory(s); if (!_logDir.exists()) _logDir.mkdir(); } else { @@ -248,7 +249,7 @@ public class I2PAppContext { } s = getProperty("i2p.dir.app"); if (s != null) { - _appDir = new File(s); + _appDir = new SecureDirectory(s); if (!_appDir.exists()) _appDir.mkdir(); } else { @@ -278,14 +279,14 @@ public class I2PAppContext { String d = getProperty("i2p.dir.temp", System.getProperty("java.io.tmpdir")); // our random() probably isn't warmed up yet String f = "i2p-" + Math.abs((new java.util.Random()).nextInt()) + ".tmp"; - _tmpDir = new File(d, f); + _tmpDir = new SecureDirectory(d, f); if (_tmpDir.exists()) { // good or bad ? } else if (_tmpDir.mkdir()) { _tmpDir.deleteOnExit(); } else { System.err.println("Could not create temp dir " + _tmpDir.getAbsolutePath()); - _tmpDir = new File(_routerDir, "tmp"); + _tmpDir = new SecureDirectory(_routerDir, "tmp"); _tmpDir.mkdir(); } } diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index 07997fd10..6ac316ee9 100644 --- a/core/java/src/net/i2p/data/DataHelper.java +++ b/core/java/src/net/i2p/data/DataHelper.java @@ -17,7 +17,6 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -42,6 +41,7 @@ import net.i2p.util.ByteCache; import net.i2p.util.OrderedProperties; import net.i2p.util.ReusableGZIPInputStream; import net.i2p.util.ReusableGZIPOutputStream; +import net.i2p.util.SecureFileOutputStream; /** * Defines some simple IO routines for dealing with marshalling data structures @@ -304,11 +304,12 @@ public class DataHelper { /** * Writes the props to the file, unsorted (unless props is an OrderedProperties) * Note that this does not escape the \r or \n that are unescaped in loadProps() above. + * As of 0.8.1, file will be mode 600. */ public static void storeProps(Properties props, File file) throws IOException { PrintWriter out = null; try { - out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"))); + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8"))); out.println("# NOTE: This I2P config file must use UTF-8 encoding"); for (Iterator iter = props.keySet().iterator(); iter.hasNext(); ) { String name = (String)iter.next(); diff --git a/core/java/src/net/i2p/util/LogWriter.java b/core/java/src/net/i2p/util/LogWriter.java index b15aa6cfb..7dca7ba96 100644 --- a/core/java/src/net/i2p/util/LogWriter.java +++ b/core/java/src/net/i2p/util/LogWriter.java @@ -11,7 +11,6 @@ package net.i2p.util; import java.io.BufferedWriter; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -158,7 +157,8 @@ class LogWriter implements Runnable { File parent = f.getParentFile(); if (parent != null) { if (!parent.exists()) { - boolean ok = parent.mkdirs(); + File sd = new SecureDirectory(parent.getAbsolutePath()); + boolean ok = sd.mkdirs(); if (!ok) { System.err.println("Unable to create the parent directory: " + parent.getAbsolutePath()); //System.exit(0); @@ -171,7 +171,7 @@ class LogWriter implements Runnable { } closeFile(); try { - _currentOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(f), "UTF-8")); + _currentOut = new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(f), "UTF-8")); } catch (IOException ioe) { System.err.println("Error rotating into [" + f.getAbsolutePath() + "]" + ioe); } diff --git a/core/java/src/net/i2p/util/RandomSource.java b/core/java/src/net/i2p/util/RandomSource.java index c6ac80a65..100ec47d7 100644 --- a/core/java/src/net/i2p/util/RandomSource.java +++ b/core/java/src/net/i2p/util/RandomSource.java @@ -145,7 +145,7 @@ public class RandomSource extends SecureRandom implements EntropyHarvester { File f = new File(I2PAppContext.getGlobalContext().getConfigDir(), SEEDFILE); FileOutputStream fos = null; try { - fos = new FileOutputStream(f); + fos = new SecureFileOutputStream(f); fos.write(buf); } catch (IOException ioe) { // ignore diff --git a/core/java/src/net/i2p/util/SecureDirectory.java b/core/java/src/net/i2p/util/SecureDirectory.java index 393e11886..30d609e96 100644 --- a/core/java/src/net/i2p/util/SecureDirectory.java +++ b/core/java/src/net/i2p/util/SecureDirectory.java @@ -38,6 +38,18 @@ public class SecureDirectory extends File { return rv; } + /** + * Sets directory to mode 700 if the directory is created + * Does NOT change the mode of other created directories + */ + @Override + public boolean mkdirs() { + boolean rv = super.mkdirs(); + if (rv) + setPerms(); + return rv; + } + /** * Tries to set the permissions to 700, * ignores errors diff --git a/router/java/src/net/i2p/router/KeyManager.java b/router/java/src/net/i2p/router/KeyManager.java index 4d19228b7..fd0e85a9e 100644 --- a/router/java/src/net/i2p/router/KeyManager.java +++ b/router/java/src/net/i2p/router/KeyManager.java @@ -27,6 +27,8 @@ import net.i2p.data.SigningPrivateKey; import net.i2p.data.SigningPublicKey; import net.i2p.util.Clock; import net.i2p.util.Log; +import net.i2p.util.SecureDirectory; +import net.i2p.util.SecureFileOutputStream; /** * Maintain all of the key pairs for the router. @@ -142,7 +144,7 @@ public class KeyManager { } public void runJob() { String keyDir = getContext().getProperty(PROP_KEYDIR, DEFAULT_KEYDIR); - File dir = new File(getContext().getRouterDir(), keyDir); + File dir = new SecureDirectory(getContext().getRouterDir(), keyDir); if (!dir.exists()) dir.mkdirs(); if (dir.exists() && dir.isDirectory() && dir.canRead() && dir.canWrite()) { @@ -219,7 +221,7 @@ public class KeyManager { FileInputStream in = null; try { if (exists) { - out = new FileOutputStream(keyFile); + out = new SecureFileOutputStream(keyFile); structure.writeBytes(out); return structure; } else { diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 0651e35d2..a091cba13 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -46,6 +46,7 @@ import net.i2p.util.FileUtil; import net.i2p.util.I2PAppThread; import net.i2p.util.I2PThread; import net.i2p.util.Log; +import net.i2p.util.SecureFileOutputStream; import net.i2p.util.SimpleScheduler; import net.i2p.util.SimpleTimer; @@ -305,6 +306,7 @@ public class Router { public void setHigherVersionSeen(boolean seen) { _higherVersionSeen = seen; } public long getWhenStarted() { return _started; } + /** wall clock uptime */ public long getUptime() { if ( (_context == null) || (_context.clock() == null) ) return 1; // racing on startup @@ -1053,11 +1055,12 @@ public class Router { * this does escape the \r or \n that are unescaped in DataHelper.loadProps(). * Note that the escaping of \r or \n was probably a mistake and should be taken out. * + * FIXME Synchronize!! */ public boolean saveConfig() { FileOutputStream fos = null; try { - fos = new FileOutputStream(_configFilename); + fos = new SecureFileOutputStream(_configFilename); StringBuilder buf = new StringBuilder(8*1024); buf.append("# NOTE: This I2P config file must use UTF-8 encoding\n"); synchronized (_config) { @@ -1541,7 +1544,7 @@ private static class PersistRouterInfoJob extends JobImpl { FileOutputStream fos = null; try { - fos = new FileOutputStream(infoFile); + fos = new SecureFileOutputStream(infoFile); info.writeBytes(fos); } catch (DataFormatException dfe) { _log.error("Error rebuilding the router information", dfe); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java index 77ea8c457..0172dfcc6 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java @@ -29,6 +29,8 @@ import net.i2p.router.RouterContext; import net.i2p.router.networkdb.reseed.ReseedChecker; import net.i2p.util.I2PThread; import net.i2p.util.Log; +import net.i2p.util.SecureDirectory; +import net.i2p.util.SecureFileOutputStream; /** * Write out keys to disk when we get them and periodically read ones we don't know @@ -288,7 +290,7 @@ class PersistentDataStore extends TransientDataStore { long dataPublishDate = getPublishDate(data); if (dbFile.lastModified() < dataPublishDate) { // our filesystem is out of date, lets replace it - fos = new FileOutputStream(dbFile); + fos = new SecureFileOutputStream(dbFile); try { data.writeBytes(fos); fos.close(); @@ -440,7 +442,7 @@ class PersistentDataStore extends TransientDataStore { private File getDbDir() throws IOException { - File f = new File(_context.getRouterDir(), _dbDir); + File f = new SecureDirectory(_context.getRouterDir(), _dbDir); if (!f.exists()) { boolean created = f.mkdirs(); if (!created) diff --git a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java index 29fce33a6..21870341e 100644 --- a/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java +++ b/router/java/src/net/i2p/router/networkdb/reseed/Reseeder.java @@ -17,6 +17,8 @@ import net.i2p.router.RouterContext; import net.i2p.util.EepGet; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; +import net.i2p.util.SecureDirectory; +import net.i2p.util.SecureFileOutputStream; import net.i2p.util.SSLEepGet; import net.i2p.util.Translate; @@ -261,11 +263,11 @@ public class Reseeder { private void writeSeed(String name, byte data[]) throws Exception { String dirName = "netDb"; // _context.getProperty("router.networkDatabase.dbDir", "netDb"); - File netDbDir = new File(_context.getRouterDir(), dirName); + File netDbDir = new SecureDirectory(_context.getRouterDir(), dirName); if (!netDbDir.exists()) { boolean ok = netDbDir.mkdirs(); } - FileOutputStream fos = new FileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat")); + FileOutputStream fos = new SecureFileOutputStream(new File(netDbDir, "routerInfo-" + name + ".dat")); fos.write(data); fos.close(); } diff --git a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java index 606b4883c..610dfec4f 100644 --- a/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java +++ b/router/java/src/net/i2p/router/peermanager/ProfilePersistenceHelper.java @@ -3,7 +3,6 @@ package net.i2p.router.peermanager; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; import java.io.OutputStream; @@ -19,6 +18,8 @@ import net.i2p.data.DataHelper; import net.i2p.data.Hash; import net.i2p.router.RouterContext; import net.i2p.util.Log; +import net.i2p.util.SecureDirectory; +import net.i2p.util.SecureFileOutputStream; class ProfilePersistenceHelper { private Log _log; @@ -61,7 +62,7 @@ class ProfilePersistenceHelper { long before = _context.clock().now(); OutputStream fos = null; try { - fos = new BufferedOutputStream(new GZIPOutputStream(new FileOutputStream(f))); + fos = new BufferedOutputStream(new GZIPOutputStream(new SecureFileOutputStream(f))); writeProfile(profile, fos); } catch (IOException ioe) { _log.error("Error writing profile to " + f); @@ -310,7 +311,7 @@ class ProfilePersistenceHelper { private File getProfileDir() { if (_profileDir == null) { String dir = _context.getProperty(PROP_PEER_PROFILE_DIR, DEFAULT_PEER_PROFILE_DIR); - _profileDir = new File(_context.getRouterDir(), dir); + _profileDir = new SecureDirectory(_context.getRouterDir(), dir); } return _profileDir; } diff --git a/router/java/src/net/i2p/router/startup/ClientAppConfig.java b/router/java/src/net/i2p/router/startup/ClientAppConfig.java index 982d4a298..33f4351fe 100644 --- a/router/java/src/net/i2p/router/startup/ClientAppConfig.java +++ b/router/java/src/net/i2p/router/startup/ClientAppConfig.java @@ -11,6 +11,7 @@ import java.util.Properties; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; import net.i2p.router.RouterContext; +import net.i2p.util.SecureFileOutputStream; /** @@ -191,7 +192,7 @@ public class ClientAppConfig { File cfgFile = configFile(ctx); FileOutputStream fos = null; try { - fos = new FileOutputStream(cfgFile); + fos = new SecureFileOutputStream(cfgFile); StringBuilder buf = new StringBuilder(2048); for(int i = 0; i < apps.size(); i++) { ClientAppConfig app = (ClientAppConfig) apps.get(i); diff --git a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java index 459cd0fb3..36b989bc7 100644 --- a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java @@ -27,6 +27,7 @@ import net.i2p.router.JobImpl; import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.util.Log; +import net.i2p.util.SecureFileOutputStream; public class CreateRouterInfoJob extends JobImpl { private static Log _log = new Log(CreateRouterInfoJob.class); @@ -80,12 +81,12 @@ public class CreateRouterInfoJob extends JobImpl { String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT); File ifile = new File(getContext().getRouterDir(), infoFilename); - fos1 = new FileOutputStream(ifile); + fos1 = new SecureFileOutputStream(ifile); info.writeBytes(fos1); String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT); File kfile = new File(getContext().getRouterDir(), keyFilename); - fos2 = new FileOutputStream(kfile); + fos2 = new SecureFileOutputStream(kfile); privkey.writeBytes(fos2); signingPrivKey.writeBytes(fos2); pubkey.writeBytes(fos2); diff --git a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java index e3daf60ea..b417c32bb 100644 --- a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java @@ -26,6 +26,7 @@ import net.i2p.router.JobImpl; import net.i2p.router.Router; import net.i2p.router.RouterContext; import net.i2p.util.Log; +import net.i2p.util.SecureFileOutputStream; /** * This used be called from StartAcceptingClientsJob but is now disabled. @@ -135,7 +136,7 @@ public class RebuildRouterInfoJob extends JobImpl { FileOutputStream fos = null; try { - fos = new FileOutputStream(infoFile); + fos = new SecureFileOutputStream(infoFile); info.writeBytes(fos); } catch (DataFormatException dfe) { _log.log(Log.CRIT, "Error rebuilding the router information", dfe); diff --git a/router/java/src/net/i2p/router/startup/WorkingDir.java b/router/java/src/net/i2p/router/startup/WorkingDir.java index 545239777..0b32c661f 100644 --- a/router/java/src/net/i2p/router/startup/WorkingDir.java +++ b/router/java/src/net/i2p/router/startup/WorkingDir.java @@ -11,6 +11,8 @@ import java.io.PrintWriter; import java.util.Properties; import net.i2p.data.DataHelper; +import net.i2p.util.SecureDirectory; +import net.i2p.util.SecureFileOutputStream; /** * Get a working directory for i2p. @@ -64,19 +66,19 @@ public class WorkingDir { boolean isWindows = System.getProperty("os.name").startsWith("Win"); File dirf = null; if (dir != null) { - dirf = new File(dir); + dirf = new SecureDirectory(dir); } else { String home = System.getProperty("user.home"); if (isWindows) { String appdata = System.getenv("APPDATA"); if (appdata != null) home = appdata; - dirf = new File(home, WORKING_DIR_DEFAULT_WINDOWS); + dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_WINDOWS); } else { if (DAEMON_USER.equals(System.getProperty("user.name"))) - dirf = new File(home, WORKING_DIR_DEFAULT_DAEMON); + dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT_DAEMON); else - dirf = new File(home, WORKING_DIR_DEFAULT); + dirf = new SecureDirectory(home, WORKING_DIR_DEFAULT); } } @@ -143,7 +145,7 @@ public class WorkingDir { // this one must be after MIGRATE_BASE success &= migrateJettyXml(oldDirf, dirf); success &= migrateClientsConfig(oldDirf, dirf); - success &= copy(new File(oldDirf, "docs/news.xml"), new File(dirf, "docs")); + success &= copy(new File(oldDirf, "docs/news.xml"), new SecureDirectory(dirf, "docs")); // Report success or failure if (success) { @@ -197,7 +199,7 @@ public class WorkingDir { PrintWriter out = null; try { in = new FileInputStream(oldFile); - out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), "UTF-8"))); + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(newFile), "UTF-8"))); out.println("# Modified by I2P User dir migration script"); String s = null; boolean isDaemon = DAEMON_USER.equals(System.getProperty("user.name")); @@ -240,7 +242,7 @@ public class WorkingDir { PrintWriter out = null; try { in = new FileInputStream(oldFile); - out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(newFile), "UTF-8"))); + out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(newFile), "UTF-8"))); String s = null; while ((s = DataHelper.readLine(in)) != null) { if (s.indexOf("./eepsite/") >= 0) { @@ -270,7 +272,7 @@ public class WorkingDir { * @param targetDir the directory to copy to, will be created if it doesn't exist * @return true for success OR if src does not exist */ - public static final boolean copy(File src, File targetDir) { + private static boolean copy(File src, File targetDir) { if (!src.exists()) return true; if (!targetDir.exists()) { @@ -280,7 +282,8 @@ public class WorkingDir { } System.err.println("Created " + targetDir.getPath()); } - File targetFile = new File(targetDir, src.getName()); + // SecureDirectory is a File so this works for non-directories too + File targetFile = new SecureDirectory(targetDir, src.getName()); if (!src.isDirectory()) return copyFile(src, targetFile); File children[] = src.listFiles(); @@ -305,10 +308,10 @@ public class WorkingDir { /** * @param src not a directory, must exist - * @param dst not a directory, will be overwritten if existing + * @param dst not a directory, will be overwritten if existing, will be mode 600 * @return true if it was copied successfully */ - public static boolean copyFile(File src, File dst) { + private static boolean copyFile(File src, File dst) { if (!src.exists()) return false; boolean rv = true; @@ -317,7 +320,7 @@ public class WorkingDir { FileOutputStream out = null; try { in = new FileInputStream(src); - out = new FileOutputStream(dst); + out = new SecureFileOutputStream(dst); int read = 0; while ( (read = in.read(buf)) != -1) From 01ef6baa530e30e5274cacc3309dbf505eb86e9b Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 7 Jul 2010 13:30:04 +0000 Subject: [PATCH 05/49] beginnings of SSU destroy msg handling --- .../transport/udp/EstablishmentManager.java | 34 +++++++++++++++++++ .../router/transport/udp/PacketHandler.java | 33 +++++++++++++++++- .../router/transport/udp/UDPTransport.java | 3 +- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java index 275c200c2..d19f929c2 100644 --- a/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java +++ b/router/java/src/net/i2p/router/transport/udp/EstablishmentManager.java @@ -316,6 +316,40 @@ class EstablishmentManager { } } + /** + * Got a SessionDestroy on an established conn + */ + void receiveSessionDestroy(RemoteHostId from, PeerState state) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Receive session destroy (EST) from: " + from); + _transport.dropPeer(state, false, "received destroy message"); + } + + /** + * Got a SessionDestroy during outbound establish + */ + void receiveSessionDestroy(RemoteHostId from, OutboundEstablishState state) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Receive session destroy (OB) from: " + from); + _outboundStates.remove(from); + Hash peer = state.getRemoteIdentity().calculateHash(); + _transport.dropPeer(peer, false, "received destroy message"); + } + + /** + * Got a SessionDestroy - maybe after an inbound establish + */ + void receiveSessionDestroy(RemoteHostId from) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Receive session destroy (IB) from: " + from); + InboundEstablishState state = _inboundStates.remove(from); + if (state != null) { + Hash peer = state.getConfirmedIdentity().calculateHash(); + if (peer != null) + _transport.dropPeer(peer, false, "received destroy message"); + } + } + /** * A data packet arrived on an outbound connection being established, which * means its complete (yay!). This is a blocking call, more than I'd like... diff --git a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java index 15dc16457..d130a4c83 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketHandler.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketHandler.java @@ -186,6 +186,10 @@ class PacketHandler { } //} + /** + * Initial handling, called for every packet + * Find the state and call the correct receivePacket() variant + */ private void handlePacket(UDPPacketReader reader, UDPPacket packet) { if (packet == null) return; @@ -229,6 +233,10 @@ class PacketHandler { } } + /** + * Established conn + * Decrypt and validate the packet then call handlePacket() + */ private void receivePacket(UDPPacketReader reader, UDPPacket packet, PeerState state) { _state = 17; boolean isValid = packet.validate(state.getCurrentMACKey()); @@ -280,6 +288,12 @@ class PacketHandler { _state = 26; } + /** + * New conn or failed validation + * Decrypt and validate the packet then call handlePacket() + * + * @param peerType OUTBOUND_FALLBACK, INBOUND_FALLBACK, or NEW_PEER + */ private void receivePacket(UDPPacketReader reader, UDPPacket packet, short peerType) { _state = 27; boolean isValid = packet.validate(_transport.getIntroKey()); @@ -314,7 +328,11 @@ class PacketHandler { private void receivePacket(UDPPacketReader reader, UDPPacket packet, InboundEstablishState state) { receivePacket(reader, packet, state, true); } + /** + * Inbound establishing conn + * Decrypt and validate the packet then call handlePacket() + * * @param allowFallback if it isn't valid for this establishment state, try as a non-establishment packet */ private void receivePacket(UDPPacketReader reader, UDPPacket packet, InboundEstablishState state, boolean allowFallback) { @@ -355,6 +373,10 @@ class PacketHandler { } } + /** + * Outbound establishing conn + * Decrypt and validate the packet then call handlePacket() + */ private void receivePacket(UDPPacketReader reader, UDPPacket packet, OutboundEstablishState state) { _state = 35; if ( (state != null) && (_log.shouldLog(Log.DEBUG)) ) { @@ -406,6 +428,10 @@ class PacketHandler { /** * Parse out the interesting bits and honor what it says + * + * @param state non-null if fully established + * @param outState non-null if outbound establishing in process + * @param inState unused always null */ private void handlePacket(UDPPacketReader reader, UDPPacket packet, PeerState state, OutboundEstablishState outState, InboundEstablishState inState) { _state = 43; @@ -527,7 +553,12 @@ class PacketHandler { break; case UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY: _state = 53; - //_TODO + if (outState != null) + _establisher.receiveSessionDestroy(from, outState); + else if (state != null) + _establisher.receiveSessionDestroy(from, state); + else + _establisher.receiveSessionDestroy(from); break; default: _state = 52; diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 5570b8389..e046c0a99 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -838,7 +838,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority if (state != null) dropPeer(state, shouldShitlist, why); } - private void dropPeer(PeerState peer, boolean shouldShitlist, String why) { + + void dropPeer(PeerState peer, boolean shouldShitlist, String why) { if (_log.shouldLog(Log.WARN)) { long now = _context.clock().now(); StringBuilder buf = new StringBuilder(4096); From c19af4dbcf48534fc3378c37948cf57a5fb519f7 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 9 Jul 2010 16:32:31 +0000 Subject: [PATCH 06/49] i2psnark: - Rework PeerID class so it depends only on peer desthash, since peer ID is not available with compact format. Implement deferred lookup of destination. - Implement compact tracker requests and response handling (may not be the final format) - Fix Peer hashCode() - Java 5 --- .../src/org/klomp/snark/I2PSnarkUtil.java | 7 +- .../java/src/org/klomp/snark/Peer.java | 12 ++- .../src/org/klomp/snark/PeerCoordinator.java | 3 +- .../java/src/org/klomp/snark/PeerID.java | 87 ++++++++++++++----- .../src/org/klomp/snark/TrackerClient.java | 1 + .../java/src/org/klomp/snark/TrackerInfo.java | 39 ++++++--- .../src/org/klomp/snark/bencode/BEValue.java | 4 +- 7 files changed, 111 insertions(+), 42 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index b0e26bfe6..bbdc09ab4 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -197,11 +197,14 @@ public class I2PSnarkUtil { /** connect to the given destination */ I2PSocket connect(PeerID peer) throws IOException { - Hash dest = peer.getAddress().calculateHash(); + Destination addr = peer.getAddress(); + if (addr == null) + throw new IOException("Null address"); + Hash dest = addr.calculateHash(); if (_shitlist.contains(dest)) throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted"); try { - I2PSocket rv = _manager.connect(peer.getAddress()); + I2PSocket rv = _manager.connect(addr); if (rv != null) _shitlist.remove(dest); return rv; diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java index e0ae6e1f2..4e965a072 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java @@ -129,7 +129,7 @@ public class Peer implements Comparable @Override public int hashCode() { - return peerID.hashCode() ^ (2 << _id); + return peerID.hashCode() ^ (7777 * (int)_id); } /** @@ -150,6 +150,7 @@ public class Peer implements Comparable /** * Compares the PeerIDs. + * @deprecated unused? */ public int compareTo(Object o) { @@ -207,12 +208,15 @@ public class Peer implements Comparable // = new BufferedOutputStream(sock.getOutputStream()); byte [] id = handshake(in, out); //handshake(bis, bos); byte [] expected_id = peerID.getID(); - if (!Arrays.equals(expected_id, id)) - throw new IOException("Unexpected peerID '" + if (expected_id == null) + peerID.setID(id); + else if (Arrays.equals(expected_id, id)) + _log.debug("Handshake got matching IDs with " + toString()); + else + throw new IOException("Unexpected peerID '" + PeerID.idencode(id) + "' expected '" + PeerID.idencode(expected_id) + "'"); - _log.debug("Handshake got matching IDs with " + toString()); } else { _log.debug("Already have din [" + sock + "] with " + toString()); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java index 2a2e40e7d..5e40f662e 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java @@ -373,7 +373,8 @@ public class PeerCoordinator implements PeerListener if (need_more) { - _log.debug("Adding a peer " + peer.getPeerID().getAddress().calculateHash().toBase64() + " for " + metainfo.getName(), new Exception("add/run")); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Adding a peer " + peer.getPeerID().toString() + " for " + metainfo.getName(), new Exception("add/run")); // Run the peer with us as listener and the current bitfield. final PeerListener listener = this; diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerID.java b/apps/i2psnark/java/src/org/klomp/snark/PeerID.java index c4a4e9194..37cf1a9b6 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerID.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerID.java @@ -24,19 +24,33 @@ import java.io.IOException; import java.net.UnknownHostException; import java.util.Map; +import net.i2p.I2PAppContext; +import net.i2p.data.Base32; import net.i2p.data.Base64; +import net.i2p.data.DataHelper; import net.i2p.data.Destination; import org.klomp.snark.bencode.BDecoder; import org.klomp.snark.bencode.BEValue; import org.klomp.snark.bencode.InvalidBEncodingException; +/** + * Store the address information about a peer. + * Prior to 0.8.1, an instantiation required a peer ID, and full Destination address. + * Starting with 0.8.1, to support compact tracker responses, + * a PeerID can be instantiated with a Destination Hash alone. + * The full destination lookup is deferred until getAddress() is called, + * and the PeerID is not required. + * Equality is now determined solely by the dest hash. + */ public class PeerID implements Comparable { - private final byte[] id; - private final Destination address; + private byte[] id; + private Destination address; private final int port; - + private byte[] destHash; + /** whether we have tried to get the dest from the hash - only do once */ + private boolean triedDestLookup; private final int hash; public PeerID(byte[] id, Destination address) @@ -44,7 +58,7 @@ public class PeerID implements Comparable this.id = id; this.address = address; this.port = 6881; - + this.destHash = address.calculateHash().getData(); hash = calculateHash(); } @@ -77,17 +91,49 @@ public class PeerID implements Comparable throw new InvalidBEncodingException("Invalid destination [" + bevalue.getString() + "]"); port = 6881; - + this.destHash = address.calculateHash().getData(); hash = calculateHash(); } + /** + * Creates a PeerID from a destHash + * @since 0.8.1 + */ + public PeerID(byte[] dest_hash) throws InvalidBEncodingException + { + // id and address remain null + port = 6881; + if (dest_hash.length != 32) + throw new InvalidBEncodingException("bad hash length"); + destHash = dest_hash; + hash = DataHelper.hashCode(dest_hash); + } + public byte[] getID() { return id; } - public Destination getAddress() + /** for connecting out to peer based on desthash @since 0.8.1 */ + public void setID(byte[] xid) { + id = xid; + } + + /** + * Get the destination. + * If this PeerId was instantiated with a destHash, + * and we have not yet done so, lookup the full destination, which may take + * up to 10 seconds. + * @return Dest or null if unknown + */ + public synchronized Destination getAddress() + { + if (address == null && destHash != null && !triedDestLookup) { + String b32 = Base32.encode(destHash) + ".b32.i2p"; + address = I2PAppContext.getGlobalContext().namingService().lookup(b32); + triedDestLookup = true; + } return address; } @@ -96,16 +142,19 @@ public class PeerID implements Comparable return port; } + /** @since 0.8.1 */ + public byte[] getDestHash() + { + return destHash; + } + private int calculateHash() { - int b = 0; - for (int i = 0; i < id.length; i++) - b ^= id[i]; - return (b ^ address.hashCode()) ^ port; + return DataHelper.hashCode(destHash); } /** - * The hash code of a PeerID is the exclusive or of all id bytes. + * The hash code of a PeerID is the hashcode of the desthash */ @Override public int hashCode() @@ -115,18 +164,15 @@ public class PeerID implements Comparable /** * Returns true if and only if this peerID and the given peerID have - * the same 20 bytes as ID. + * the same destination hash */ public boolean sameID(PeerID pid) { - boolean equal = true; - for (int i = 0; equal && i < id.length; i++) - equal = id[i] == pid.id[i]; - return equal; + return DataHelper.eq(destHash, pid.getDestHash()); } /** - * Two PeerIDs are equal when they have the same id, address and port. + * Two PeerIDs are equal when they have the same dest hash */ @Override public boolean equals(Object o) @@ -135,9 +181,7 @@ public class PeerID implements Comparable { PeerID pid = (PeerID)o; - return port == pid.port - && address.equals(pid.address) - && sameID(pid); + return sameID(pid); } else return false; @@ -145,6 +189,7 @@ public class PeerID implements Comparable /** * Compares port, address and id. + * @deprecated unused? and will NPE now that address can be null? */ public int compareTo(Object o) { @@ -176,6 +221,8 @@ public class PeerID implements Comparable @Override public String toString() { + if (id == null || address == null) + return "unkn@" + Base64.encode(destHash).substring(0, 6); int nonZero = 0; for (int i = 0; i < id.length; i++) { if (id[i] != 0) { diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index 065e70880..05df351c6 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -357,6 +357,7 @@ public class TrackerClient extends I2PAppThread + "&uploaded=" + uploaded + "&downloaded=" + downloaded + "&left=" + left + + "&compact" + ((! event.equals(NO_EVENT)) ? ("&event=" + event) : ""); if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers()) s += "&numwant=0"; diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java index 84198f12f..ee2c2da25 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java @@ -33,11 +33,17 @@ import org.klomp.snark.bencode.BDecoder; import org.klomp.snark.bencode.BEValue; import org.klomp.snark.bencode.InvalidBEncodingException; +/** + * The data structure for the tracker response. + * Handles both traditional and compact formats. + * Compact format 1 - a list of hashes - implemented + * Compact format 2 - One big string of concatenated hashes - unimplemented + */ public class TrackerInfo { private final String failure_reason; private final int interval; - private final Set peers; + private final Set peers; private int complete; private int incomplete; @@ -76,6 +82,7 @@ public class TrackerInfo if (bePeers == null) peers = Collections.EMPTY_SET; else + // if compact is going to be one big string instead, try/catch here peers = getPeers(bePeers.getList(), my_id, metainfo); BEValue bev = (BEValue)m.get("complete"); @@ -94,33 +101,39 @@ public class TrackerInfo } } - public static Set getPeers(InputStream in, byte[] my_id, MetaInfo metainfo) +/****** + public static Set getPeers(InputStream in, byte[] my_id, MetaInfo metainfo) throws IOException { return getPeers(new BDecoder(in), my_id, metainfo); } - public static Set getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo) + public static Set getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo) throws IOException { return getPeers(be.bdecodeList().getList(), my_id, metainfo); } +******/ - public static Set getPeers(List l, byte[] my_id, MetaInfo metainfo) + private static Set getPeers(List l, byte[] my_id, MetaInfo metainfo) throws IOException { - Set peers = new HashSet(l.size()); + Set peers = new HashSet(l.size()); - Iterator it = l.iterator(); - while (it.hasNext()) - { + for (BEValue bev : l) { PeerID peerID; try { - peerID = new PeerID(((BEValue)it.next()).getMap()); + // Case 1 - non-compact - A list of dictionaries (maps) + peerID = new PeerID(bev.getMap()); } catch (InvalidBEncodingException ibe) { - // don't let one bad entry spoil the whole list - //Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR); - continue; + try { + // Case 2 - compact - A list of 32-byte binary strings (hashes) + peerID = new PeerID(bev.getBytes()); + } catch (InvalidBEncodingException ibe2) { + // don't let one bad entry spoil the whole list + //Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR); + continue; + } } peers.add(new Peer(peerID, my_id, metainfo)); } @@ -128,7 +141,7 @@ public class TrackerInfo return peers; } - public Set getPeers() + public Set getPeers() { return peers; } diff --git a/apps/i2psnark/java/src/org/klomp/snark/bencode/BEValue.java b/apps/i2psnark/java/src/org/klomp/snark/bencode/BEValue.java index cba6b973f..c1733cb13 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/bencode/BEValue.java +++ b/apps/i2psnark/java/src/org/klomp/snark/bencode/BEValue.java @@ -140,7 +140,7 @@ public class BEValue * succeeds when the BEValue is actually a List, otherwise it will * throw a InvalidBEncodingException. */ - public List getList() throws InvalidBEncodingException + public List getList() throws InvalidBEncodingException { try { @@ -157,7 +157,7 @@ public class BEValue * values. This operation only succeeds when the BEValue is actually * a Map, otherwise it will throw a InvalidBEncodingException. */ - public Map getMap() throws InvalidBEncodingException + public Map getMap() throws InvalidBEncodingException { try { From 8353b623da7b2f3a721c7b49ed5d495d697fb786 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 9 Jul 2010 16:34:54 +0000 Subject: [PATCH 07/49] SSU PacketBuilder: Refactor header generation, add destroy msg support --- .../router/transport/udp/PacketBuilder.java | 233 +++++++----------- .../i2p/router/transport/udp/UDPPacket.java | 1 + 2 files changed, 95 insertions(+), 139 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java index 672744444..9bbaa840b 100644 --- a/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java +++ b/router/java/src/net/i2p/router/transport/udp/PacketBuilder.java @@ -4,7 +4,6 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; -import java.util.Date; import java.util.List; import net.i2p.I2PAppContext; @@ -156,7 +155,9 @@ class PacketBuilder { * included, it should be removed from the list. */ public UDPPacket buildPacket(OutboundMessageState state, int fragment, PeerState peer, List ackIdsRemaining, List partialACKsRemaining) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_DATA << 4)); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; StringBuilder msg = null; boolean acksIncluded = false; @@ -168,19 +169,6 @@ class PacketBuilder { msg.append("*"); } - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int start = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - int off = start; - - // header - data[off] |= (UDPPacket.PAYLOAD_TYPE_DATA << 4); - // todo: add support for rekeying and extended options - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; - // ok, now for the body... // just always ask for an ACK for now... @@ -322,11 +310,14 @@ class PacketBuilder { /** * Build the ack packet. The list need not be sorted into full and partial; * this method will put all fulls before the partials in the outgoing packet. + * An ack packet is just a data packet with no data. * * @param ackBitfields list of ACKBitfield instances to either fully or partially ACK */ public UDPPacket buildACK(PeerState peer, List ackBitfields) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_DATA << 4)); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; StringBuilder msg = null; if (_log.shouldLog(Log.DEBUG)) { @@ -334,18 +325,6 @@ class PacketBuilder { msg.append("building ACK packet to ").append(peer.getRemotePeer().toBase64().substring(0,6)); } - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] |= (UDPPacket.PAYLOAD_TYPE_DATA << 4); - // todo: add support for rekeying and extended options - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; - int fullACKCount = 0; int partialACKCount = 0; for (int i = 0; i < ackBitfields.size(); i++) { @@ -432,7 +411,9 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildSessionCreatedPacket(InboundEstablishState state, int externalPort, SessionKey ourIntroKey) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(SESSION_CREATED_FLAG_BYTE); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; InetAddress to = null; try { @@ -446,17 +427,6 @@ class PacketBuilder { state.prepareSessionCreated(); - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = SESSION_CREATED_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; - byte sentIP[] = state.getSentIP(); if ( (sentIP == null) || (sentIP.length <= 0) || ( (_transport != null) && (!_transport.isValid(sentIP)) ) ) { if (_log.shouldLog(Log.ERROR)) @@ -535,7 +505,10 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildSessionRequestPacket(OutboundEstablishState state) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(SESSION_REQUEST_FLAG_BYTE); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; + byte toIP[] = state.getSentIP(); if ( (_transport !=null) && (!_transport.isValid(toIP)) ) { packet.release(); @@ -550,19 +523,8 @@ class PacketBuilder { packet.release(); return null; } - - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = SESSION_REQUEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending request with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending request"); // now for the body System.arraycopy(state.getSentX(), 0, data, off, state.getSentX().length); @@ -622,7 +584,10 @@ class PacketBuilder { * @return ready to send packets, or null if there was a problem */ public UDPPacket buildSessionConfirmedPacket(OutboundEstablishState state, int fragmentNum, int numFragments, byte identity[]) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(SESSION_CONFIRMED_FLAG_BYTE); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; + InetAddress to = null; try { to = InetAddress.getByAddress(state.getSentIP()); @@ -633,17 +598,6 @@ class PacketBuilder { return null; } - byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = SESSION_CONFIRMED_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; - // now for the body data[off] |= fragmentNum << 4; data[off] |= (numFragments & 0xF); @@ -699,6 +653,36 @@ class PacketBuilder { } + /** + * Build a destroy packet, which contains a header but no body. + * + * @since 0.8.1 + */ + public UDPPacket buildSessionDestroyPacket(PeerState peer) { + UDPPacket packet = buildPacketHeader((byte)(UDPPacket.PAYLOAD_TYPE_SESSION_DESTROY << 4)); + byte data[] = packet.getPacket().getData(); + int off = HEADER_SIZE; + + StringBuilder msg = null; + if (_log.shouldLog(Log.DEBUG)) { + msg = new StringBuilder(128); + msg.append("building session destroy packet to ").append(peer.getRemotePeer().toBase64().substring(0,6)); + } + + // no body in this message + + if (msg != null) + _log.debug(msg.toString()); + + // pad up so we're on the encryption boundary + if ( (off % 16) != 0) + off += 16 - (off % 16); + packet.getPacket().setLength(off); + authenticate(packet, peer.getCurrentCipherKey(), peer.getCurrentMACKey()); + setTo(packet, peer.getRemoteIPAddress(), peer.getRemotePort()); + return packet; + } + /** * full flag info for a peerTest message. this can be fixed, * since we never rekey on test, and don't need any extended options @@ -715,19 +699,11 @@ class PacketBuilder { return buildPeerTestFromAlice(toIP, toPort, toIntroKey, toIntroKey, nonce, aliceIntroKey); } public UDPPacket buildPeerTestFromAlice(InetAddress toIP, int toPort, SessionKey toCipherKey, SessionKey toMACKey, long nonce, SessionKey aliceIntroKey) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_TEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Bob with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending peer test " + nonce + " to Bob"); // now for the body DataHelper.toLong(data, off, 4, nonce); @@ -757,19 +733,11 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildPeerTestToAlice(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, SessionKey charlieIntroKey, long nonce) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_TEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Alice with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending peer test " + nonce + " to Alice"); // now for the body DataHelper.toLong(data, off, 4, nonce); @@ -804,19 +772,11 @@ class PacketBuilder { public UDPPacket buildPeerTestToCharlie(InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, InetAddress charlieIP, int charliePort, SessionKey charlieCipherKey, SessionKey charlieMACKey) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_TEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Charlie with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending peer test " + nonce + " to Charlie"); // now for the body DataHelper.toLong(data, off, 4, nonce); @@ -849,19 +809,11 @@ class PacketBuilder { * @return ready to send packet, or null if there was a problem */ public UDPPacket buildPeerTestToBob(InetAddress bobIP, int bobPort, InetAddress aliceIP, int alicePort, SessionKey aliceIntroKey, long nonce, SessionKey bobCipherKey, SessionKey bobMACKey) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_TEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_TEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sending peer test " + nonce + " to Bob with time = " + new Date(now*1000)); - off += 4; + _log.debug("Sending peer test " + nonce + " to Bob"); // now for the body DataHelper.toLong(data, off, 4, nonce); @@ -925,22 +877,15 @@ class PacketBuilder { } public UDPPacket buildRelayRequest(InetAddress introHost, int introPort, byte introKey[], long introTag, SessionKey ourIntroKey, long introNonce, boolean encrypt) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_RELAY_REQUEST_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; + int off = HEADER_SIZE; byte ourIP[] = getOurExplicitIP(); int ourPort = getOurExplicitPort(); - // header - data[off] = PEER_RELAY_REQUEST_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); if (_log.shouldLog(Log.INFO)) _log.info("Sending intro relay request to " + introHost + ":" + introPort); // + " regarding " + state.getRemoteIdentity().calculateHash().toBase64()); - off += 4; // now for the body DataHelper.toLong(data, off, 4, introTag); @@ -994,19 +939,11 @@ class PacketBuilder { private static final byte PEER_RELAY_INTRO_FLAG_BYTE = (UDPPacket.PAYLOAD_TYPE_RELAY_INTRO << 4); UDPPacket buildRelayIntro(RemoteHostId alice, PeerState charlie, UDPPacketReader.RelayRequestReader request) { - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_RELAY_INTRO_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_RELAY_INTRO_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); + int off = HEADER_SIZE; if (_log.shouldLog(Log.INFO)) _log.info("Sending intro to " + charlie + " for " + alice); - off += 4; // now for the body byte ip[] = alice.getIP(); @@ -1051,17 +988,9 @@ class PacketBuilder { return null; } - UDPPacket packet = UDPPacket.acquire(_context, false); + UDPPacket packet = buildPacketHeader(PEER_RELAY_RESPONSE_FLAG_BYTE); byte data[] = packet.getPacket().getData(); - Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; - - // header - data[off] = PEER_RELAY_RESPONSE_FLAG_BYTE; - off++; - long now = _context.clock().now() / 1000; - DataHelper.toLong(data, off, 4, now); - off += 4; + int off = HEADER_SIZE; if (_log.shouldLog(Log.INFO)) _log.info("Sending relay response to " + alice + " for " + charlie + " with alice's intro key " + aliceIntroKey.toBase64()); @@ -1105,7 +1034,6 @@ class PacketBuilder { UDPPacket packet = UDPPacket.acquire(_context, false); byte data[] = packet.getPacket().getData(); Arrays.fill(data, 0, data.length, (byte)0x0); - int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; int ipSize = reader.getRelayIntroReader().readIPSize(); byte ip[] = new byte[ipSize]; @@ -1134,6 +1062,33 @@ class PacketBuilder { return packet; } + /** if no extended options or rekey data, which we don't support */ + private static final int HEADER_SIZE = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE + 1 + 4; + + /** + * Create a new packet and add the flag byte and the time stamp. + * Caller should add data starting at HEADER_SIZE. + * At this point, adding support for extended options and rekeying is unlikely, + * but if we do, we'll have to change this. + * + * @param flagByte contains type and flags + * @since 0.8.1 + */ + private UDPPacket buildPacketHeader(byte flagByte) { + UDPPacket packet = UDPPacket.acquire(_context, false); + byte data[] = packet.getPacket().getData(); + Arrays.fill(data, 0, data.length, (byte)0x0); + int off = UDPPacket.MAC_SIZE + UDPPacket.IV_SIZE; + + // header + data[off] = flagByte; + off++; + long now = _context.clock().now() / 1000; + DataHelper.toLong(data, off, 4, now); + // todo: add support for rekeying and extended options + return packet; + } + private static void setTo(UDPPacket packet, InetAddress ip, int port) { packet.getPacket().setAddress(ip); packet.getPacket().setPort(port); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java index 1548e6036..83e7d570e 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java @@ -55,6 +55,7 @@ class UDPPacket { public static final int IV_SIZE = 16; public static final int MAC_SIZE = 16; + /** Message types, 4 bits max */ public static final int PAYLOAD_TYPE_SESSION_REQUEST = 0; public static final int PAYLOAD_TYPE_SESSION_CREATED = 1; public static final int PAYLOAD_TYPE_SESSION_CONFIRMED = 2; From 05ac2594b64a51cef8c1d9c6eb00f52dc22f3eb6 Mon Sep 17 00:00:00 2001 From: zzz Date: Fri, 9 Jul 2010 17:40:59 +0000 Subject: [PATCH 08/49] more shouldLog() --- .../src/org/klomp/snark/I2PSnarkUtil.java | 9 ++++-- .../java/src/org/klomp/snark/MetaInfo.java | 9 ++++-- .../java/src/org/klomp/snark/Peer.java | 31 ++++++++++++------- .../src/org/klomp/snark/PeerAcceptor.java | 8 +++-- .../src/org/klomp/snark/SnarkManager.java | 11 ++++--- 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java index bbdc09ab4..6f17a810a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/I2PSnarkUtil.java @@ -228,7 +228,8 @@ public class I2PSnarkUtil { public File get(String url, boolean rewrite) { return get(url, rewrite, 0); } public File get(String url, int retries) { return get(url, true, retries); } public File get(String url, boolean rewrite, int retries) { - _log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy); File out = null; try { // we could use the system tmp dir but deleteOnExit() doesn't seem to work on all platforms... @@ -252,10 +253,12 @@ public class I2PSnarkUtil { } EepGet get = new I2PSocketEepGet(_context, _manager, retries, out.getAbsolutePath(), fetchURL); if (get.fetch()) { - _log.debug("Fetch successful [" + url + "]: size=" + out.length()); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Fetch successful [" + url + "]: size=" + out.length()); return out; } else { - _log.warn("Fetch failed [" + url + "]"); + if (_log.shouldLog(Log.WARN)) + _log.warn("Fetch failed [" + url + "]"); out.delete(); return null; } diff --git a/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java b/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java index f8c8feaed..140d3cb46 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java @@ -109,7 +109,8 @@ public class MetaInfo */ public MetaInfo(Map m) throws InvalidBEncodingException { - _log.debug("Creating a metaInfo: " + m, new Exception("source")); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Creating a metaInfo: " + m, new Exception("source")); BEValue val = (BEValue)m.get("announce"); if (val == null) throw new InvalidBEncodingException("Missing announce string"); @@ -446,14 +447,16 @@ public class MetaInfo else buf.append(val.toString()); } - _log.debug(buf.toString()); + if (_log.shouldLog(Log.DEBUG)) + _log.debug(buf.toString()); byte[] infoBytes = BEncoder.bencode(info); //_log.debug("info bencoded: [" + Base64.encode(infoBytes, true) + "]"); try { MessageDigest digest = MessageDigest.getInstance("SHA"); byte hash[] = digest.digest(infoBytes); - _log.debug("info hash: [" + net.i2p.data.Base64.encode(hash) + "]"); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("info hash: [" + net.i2p.data.Base64.encode(hash) + "]"); return hash; } catch(NoSuchAlgorithmException nsa) diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java index 4e965a072..da1003aab 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java @@ -92,7 +92,8 @@ public class Peer implements Comparable byte[] id = handshake(in, out); this.peerID = new PeerID(id, sock.getPeerDestination()); _id = ++__id; - _log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id)); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id)); } /** @@ -182,14 +183,16 @@ public class Peer implements Comparable if (state != null) throw new IllegalStateException("Peer already started"); - _log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting")); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting")); try { // Do we need to handshake? if (din == null) { sock = util.connect(peerID); - _log.debug("Connected to " + peerID + ": " + sock); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Connected to " + peerID + ": " + sock); if ((sock == null) || (sock.isClosed())) { throw new IOException("Unable to reach " + peerID); } @@ -208,17 +211,20 @@ public class Peer implements Comparable // = new BufferedOutputStream(sock.getOutputStream()); byte [] id = handshake(in, out); //handshake(bis, bos); byte [] expected_id = peerID.getID(); - if (expected_id == null) + if (expected_id == null) { peerID.setID(id); - else if (Arrays.equals(expected_id, id)) - _log.debug("Handshake got matching IDs with " + toString()); - else + } else if (Arrays.equals(expected_id, id)) { + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Handshake got matching IDs with " + toString()); + } else { throw new IOException("Unexpected peerID '" + PeerID.idencode(id) + "' expected '" + PeerID.idencode(expected_id) + "'"); + } } else { - _log.debug("Already have din [" + sock + "] with " + toString()); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Already have din [" + sock + "] with " + toString()); } PeerConnectionIn in = new PeerConnectionIn(this, din); @@ -233,7 +239,8 @@ public class Peer implements Comparable state = s; listener.connected(this); - _log.debug("Start running the reader with " + toString()); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Start running the reader with " + toString()); // Use this thread for running the incomming connection. // The outgoing connection creates its own Thread. out.startup(); @@ -283,7 +290,8 @@ public class Peer implements Comparable dout.write(my_id); dout.flush(); - _log.debug("Wrote my shared hash and ID to " + toString()); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Wrote my shared hash and ID to " + toString()); // Handshake read - header byte b = din.readByte(); @@ -310,7 +318,8 @@ public class Peer implements Comparable // Handshake read - peer id din.readFully(bs); - _log.debug("Read the remote side's hash and peerID fully from " + toString()); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Read the remote side's hash and peerID fully from " + toString()); return bs; } diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerAcceptor.java b/apps/i2psnark/java/src/org/klomp/snark/PeerAcceptor.java index c86938437..58ef3ae2d 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerAcceptor.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerAcceptor.java @@ -76,10 +76,12 @@ public class PeerAcceptor // is this working right? try { peerInfoHash = readHash(in); - _log.info("infohash read from " + socket.getPeerDestination().calculateHash().toBase64() - + ": " + Base64.encode(peerInfoHash)); + if (_log.shouldLog(Log.INFO)) + _log.info("infohash read from " + socket.getPeerDestination().calculateHash().toBase64() + + ": " + Base64.encode(peerInfoHash)); } catch (IOException ioe) { - _log.info("Unable to read the infohash from " + socket.getPeerDestination().calculateHash().toBase64()); + if (_log.shouldLog(Log.INFO)) + _log.info("Unable to read the infohash from " + socket.getPeerDestination().calculateHash().toBase64()); throw ioe; } in = new SequenceInputStream(new ByteArrayInputStream(peerInfoHash), in); diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index a1ddaf25d..c1ce6164d 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -202,7 +202,8 @@ public class SnarkManager implements Snark.CompleteListener { } if (i2cpHost != null) { _util.setI2CPConfig(i2cpHost, i2cpPort, i2cpOpts); - _log.debug("Configuring with I2CP options " + i2cpOpts); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Configuring with I2CP options " + i2cpOpts); } //I2PSnarkUtil.instance().setI2CPConfig("66.111.51.110", 7654, new Properties()); //String eepHost = _config.getProperty(PROP_EEP_HOST); @@ -332,8 +333,9 @@ public class SnarkManager implements Snark.CompleteListener { p.putAll(opts); _util.setI2CPConfig(i2cpHost, port, p); addMessage(_("I2CP and tunnel changes will take effect after stopping all torrents")); - _log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts - + "] oldOpts [" + oldOpts + "]"); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("i2cp host [" + i2cpHost + "] i2cp port " + port + " opts [" + opts + + "] oldOpts [" + oldOpts + "]"); } else { if (_util.connected()) { _util.disconnect(); @@ -712,7 +714,8 @@ public class SnarkManager implements Snark.CompleteListener { getBWLimit(); while (true) { File dir = getDataDir(); - _log.debug("Directory Monitor loop over " + dir.getAbsolutePath()); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Directory Monitor loop over " + dir.getAbsolutePath()); try { monitorTorrents(dir); } catch (Exception e) { From 0634154b284c3c22d810987fdaf24bea12376666 Mon Sep 17 00:00:00 2001 From: zzz Date: Sun, 11 Jul 2010 14:45:12 +0000 Subject: [PATCH 09/49] final compact response format --- .../java/src/org/klomp/snark/TrackerInfo.java | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java index ee2c2da25..360a4f47e 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerInfo.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -36,8 +35,8 @@ import org.klomp.snark.bencode.InvalidBEncodingException; /** * The data structure for the tracker response. * Handles both traditional and compact formats. - * Compact format 1 - a list of hashes - implemented - * Compact format 2 - One big string of concatenated hashes - unimplemented + * Compact format 1 - a list of hashes - early format for testing + * Compact format 2 - One big string of concatenated hashes - official format */ public class TrackerInfo { @@ -79,11 +78,19 @@ public class TrackerInfo interval = beInterval.getInt(); BEValue bePeers = (BEValue)m.get("peers"); - if (bePeers == null) + if (bePeers == null) { peers = Collections.EMPTY_SET; - else - // if compact is going to be one big string instead, try/catch here - peers = getPeers(bePeers.getList(), my_id, metainfo); + } else { + Set p; + try { + // One big string (the official compact format) + p = getPeers(bePeers.getBytes(), my_id, metainfo); + } catch (InvalidBEncodingException ibe) { + // List of Dictionaries or List of Strings + p = getPeers(bePeers.getList(), my_id, metainfo); + } + peers = p; + } BEValue bev = (BEValue)m.get("complete"); if (bev != null) try { @@ -115,6 +122,7 @@ public class TrackerInfo } ******/ + /** List of Dictionaries or List of Strings */ private static Set getPeers(List l, byte[] my_id, MetaInfo metainfo) throws IOException { @@ -128,6 +136,7 @@ public class TrackerInfo } catch (InvalidBEncodingException ibe) { try { // Case 2 - compact - A list of 32-byte binary strings (hashes) + // This was just for testing and is not the official format peerID = new PeerID(bev.getBytes()); } catch (InvalidBEncodingException ibe2) { // don't let one bad entry spoil the whole list @@ -141,6 +150,34 @@ public class TrackerInfo return peers; } + private static final int HASH_LENGTH = 32; + + /** + * One big string of concatenated 32-byte hashes + * @since 0.8.1 + */ + private static Set getPeers(byte[] l, byte[] my_id, MetaInfo metainfo) + throws IOException + { + int count = l.length / HASH_LENGTH; + Set peers = new HashSet(count); + + for (int i = 0; i < count; i++) { + PeerID peerID; + byte[] hash = new byte[HASH_LENGTH]; + System.arraycopy(l, i * HASH_LENGTH, hash, 0, HASH_LENGTH); + try { + peerID = new PeerID(hash); + } catch (InvalidBEncodingException ibe) { + // won't happen + continue; + } + peers.add(new Peer(peerID, my_id, metainfo)); + } + + return peers; + } + public Set getPeers() { return peers; From 2bffeea7eb05dbdab9276399f567b8df3cabbecc Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:01:38 +0000 Subject: [PATCH 10/49] comment out main --- .../java/src/net/i2p/client/streaming/ConnectionOptions.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java index 627113abe..c6596b7c9 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java @@ -646,6 +646,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl { return Boolean.valueOf(val).booleanValue(); } +/**** public static void main(String args[]) { Properties p = new Properties(); @@ -656,4 +657,5 @@ public class ConnectionOptions extends I2PSocketOptionsImpl { c = new ConnectionOptions(new I2PSocketOptionsImpl(p)); System.out.println("opts: " + c); } +****/ } From 171e3abe341bd44a203cae033ddcf1d14ebfe997 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:02:41 +0000 Subject: [PATCH 11/49] cleanups --- .../client/streaming/MessageInputStream.java | 49 ++++++++----------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageInputStream.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageInputStream.java index 14b304b26..5a87d6be1 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/MessageInputStream.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageInputStream.java @@ -11,7 +11,7 @@ import java.util.Map; import net.i2p.I2PAppContext; import net.i2p.data.ByteArray; -import net.i2p.util.ByteCache; +//import net.i2p.util.ByteCache; import net.i2p.util.Log; /** @@ -20,8 +20,8 @@ import net.i2p.util.Log; * */ public class MessageInputStream extends InputStream { - private I2PAppContext _context; - private Log _log; + private final I2PAppContext _context; + private final Log _log; /** * List of ByteArray objects of data ready to be read, * with the first ByteArray at index 0, and the next @@ -29,7 +29,7 @@ public class MessageInputStream extends InputStream { * that array. * */ - private List _readyDataBlocks; + private final List _readyDataBlocks; private int _readyDataBlockIndex; /** highest message ID used in the readyDataBlocks */ private volatile long _highestReadyBlockId; @@ -40,7 +40,7 @@ public class MessageInputStream extends InputStream { * out of order when there are lower IDs not yet * received */ - private Map _notYetReadyBlocks; + private final Map _notYetReadyBlocks; /** * if we have received a flag saying there won't be later messages, EOF * after we have cleared what we have received. @@ -51,9 +51,9 @@ public class MessageInputStream extends InputStream { private int _readTimeout; private IOException _streamError; private long _readTotal; - private ByteCache _cache; + //private ByteCache _cache; - private byte[] _oneByte = new byte[1]; + private final byte[] _oneByte = new byte[1]; private final Object _dataLock; @@ -61,16 +61,12 @@ public class MessageInputStream extends InputStream { _context = ctx; _log = ctx.logManager().getLog(MessageInputStream.class); _readyDataBlocks = new ArrayList(4); - _readyDataBlockIndex = 0; _highestReadyBlockId = -1; _highestBlockId = -1; _readTimeout = -1; - _readTotal = 0; _notYetReadyBlocks = new HashMap(4); _dataLock = new Object(); - _closeReceived = false; - _locallyClosed = false; - _cache = ByteCache.getInstance(128, Packet.MAX_PAYLOAD_SIZE); + //_cache = ByteCache.getInstance(128, Packet.MAX_PAYLOAD_SIZE); } /** What is the highest block ID we've completely received through? @@ -140,10 +136,8 @@ public class MessageInputStream extends InputStream { if (num <= 0) return null; blocks = new long[num]; int i = 0; - for (Iterator iter = _notYetReadyBlocks.keySet().iterator(); iter.hasNext(); ) { - Long id = (Long)iter.next(); - blocks[i] = id.longValue(); - i++; + for (Long id : _notYetReadyBlocks.keySet()) { + blocks[i++] = id.longValue(); } } Arrays.sort(blocks); @@ -178,16 +172,15 @@ public class MessageInputStream extends InputStream { buf.append("Close received, ready bytes: "); long available = 0; for (int i = 0; i < _readyDataBlocks.size(); i++) - available += ((ByteArray)_readyDataBlocks.get(i)).getValid(); + available += _readyDataBlocks.get(i).getValid(); available -= _readyDataBlockIndex; buf.append(available); buf.append(" blocks: ").append(_readyDataBlocks.size()); buf.append(" not ready blocks: "); long notAvailable = 0; - for (Iterator iter = _notYetReadyBlocks.keySet().iterator(); iter.hasNext(); ) { - Long id = (Long)iter.next(); - ByteArray ba = (ByteArray)_notYetReadyBlocks.get(id); + for (Long id : _notYetReadyBlocks.keySet()) { + ByteArray ba = _notYetReadyBlocks.get(id); buf.append(id).append(" "); if (ba != null) @@ -237,7 +230,7 @@ public class MessageInputStream extends InputStream { long cur = _highestReadyBlockId + 1; // now pull in any previously pending blocks while (_notYetReadyBlocks.containsKey(new Long(cur))) { - ByteArray ba = (ByteArray)_notYetReadyBlocks.remove(new Long(cur)); + ByteArray ba = _notYetReadyBlocks.remove(new Long(cur)); if ( (ba != null) && (ba.getData() != null) && (ba.getValid() > 0) ) { _readyDataBlocks.add(ba); } @@ -341,7 +334,7 @@ public class MessageInputStream extends InputStream { return i; } else { // either was already ready, or we wait()ed and it arrived - ByteArray cur = (ByteArray)_readyDataBlocks.get(0); + ByteArray cur = _readyDataBlocks.get(0); byte rv = cur.getData()[cur.getOffset()+_readyDataBlockIndex]; _readyDataBlockIndex++; boolean removed = false; @@ -378,7 +371,7 @@ public class MessageInputStream extends InputStream { int numBytes = 0; synchronized (_dataLock) { for (int i = 0; i < _readyDataBlocks.size(); i++) { - ByteArray cur = (ByteArray)_readyDataBlocks.get(i); + ByteArray cur = _readyDataBlocks.get(i); if (i == 0) numBytes += cur.getValid() - _readyDataBlockIndex; else @@ -402,14 +395,13 @@ public class MessageInputStream extends InputStream { if (_locallyClosed) return 0; int numBytes = 0; for (int i = 0; i < _readyDataBlocks.size(); i++) { - ByteArray cur = (ByteArray)_readyDataBlocks.get(i); + ByteArray cur = _readyDataBlocks.get(i); if (i == 0) numBytes += cur.getValid() - _readyDataBlockIndex; else numBytes += cur.getValid(); } - for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) { - ByteArray cur = (ByteArray)iter.next(); + for (ByteArray cur : _notYetReadyBlocks.values()) { numBytes += cur.getValid(); } return numBytes; @@ -421,7 +413,7 @@ public class MessageInputStream extends InputStream { if (_locallyClosed) return 0; int numBytes = 0; for (int i = 0; i < _readyDataBlocks.size(); i++) { - ByteArray cur = (ByteArray)_readyDataBlocks.get(i); + ByteArray cur = _readyDataBlocks.get(i); if (i == 0) numBytes += cur.getValid() - _readyDataBlockIndex; else @@ -440,8 +432,7 @@ public class MessageInputStream extends InputStream { // we don't need the data, but we do need to keep track of the messageIds // received, so we can ACK accordingly - for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) { - ByteArray ba = (ByteArray)iter.next(); + for (ByteArray ba : _notYetReadyBlocks.values()) { ba.setData(null); //_cache.release(ba); } From 6f449aa4f6ba7f39cb15bab19f47547331c3fe72 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:07:46 +0000 Subject: [PATCH 12/49] Naming: - Increase cache size and expiration time - Add clearCache() method - Don't use EepGet or Exec for b32 - Javadoc updates --- .../net/i2p/client/naming/EepGetNamingService.java | 4 ++++ .../src/net/i2p/client/naming/ExecNamingService.java | 4 ++++ core/java/src/net/i2p/client/naming/LookupDest.java | 2 ++ .../java/src/net/i2p/client/naming/NamingService.java | 11 +++++++++-- 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/core/java/src/net/i2p/client/naming/EepGetNamingService.java b/core/java/src/net/i2p/client/naming/EepGetNamingService.java index d83317967..737e33423 100644 --- a/core/java/src/net/i2p/client/naming/EepGetNamingService.java +++ b/core/java/src/net/i2p/client/naming/EepGetNamingService.java @@ -66,6 +66,10 @@ public class EepGetNamingService extends NamingService { hostname = hostname.toLowerCase(); + // If you want b32, chain with HostsTxtNamingService + if (hostname.length() == 60 && hostname.endsWith(".b32.i2p")) + return null; + // check the cache Destination d = getCache(hostname); if (d != null) diff --git a/core/java/src/net/i2p/client/naming/ExecNamingService.java b/core/java/src/net/i2p/client/naming/ExecNamingService.java index 446e907c4..118f06eac 100644 --- a/core/java/src/net/i2p/client/naming/ExecNamingService.java +++ b/core/java/src/net/i2p/client/naming/ExecNamingService.java @@ -66,6 +66,10 @@ public class ExecNamingService extends NamingService { hostname = hostname.toLowerCase(); + // If you want b32, chain with HostsTxtNamingService + if (hostname.length() == 60 && hostname.endsWith(".b32.i2p")) + return null; + // check the cache Destination d = getCache(hostname); if (d != null) diff --git a/core/java/src/net/i2p/client/naming/LookupDest.java b/core/java/src/net/i2p/client/naming/LookupDest.java index 1a8a1632b..2bbb2eeb6 100644 --- a/core/java/src/net/i2p/client/naming/LookupDest.java +++ b/core/java/src/net/i2p/client/naming/LookupDest.java @@ -27,6 +27,7 @@ class LookupDest { protected LookupDest(I2PAppContext context) {} + /** @param key 52 chars (do not include the .b32.i2p suffix) */ static Destination lookupBase32Hash(I2PAppContext ctx, String key) { byte[] h = Base32.decode(key); if (h == null) @@ -44,6 +45,7 @@ class LookupDest { } ****/ + /** @param h 32 byte hash */ static Destination lookupHash(I2PAppContext ctx, byte[] h) { Hash key = new Hash(h); Destination rv = null; diff --git a/core/java/src/net/i2p/client/naming/NamingService.java b/core/java/src/net/i2p/client/naming/NamingService.java index a7098d799..fc9a53414 100644 --- a/core/java/src/net/i2p/client/naming/NamingService.java +++ b/core/java/src/net/i2p/client/naming/NamingService.java @@ -32,7 +32,7 @@ public abstract class NamingService { public static final String PROP_IMPL = "i2p.naming.impl"; private static final String DEFAULT_IMPL = "net.i2p.client.naming.HostsTxtNamingService"; - protected static final int CACHE_MAX_SIZE = 8; + protected static final int CACHE_MAX_SIZE = 16; /** @@ -107,7 +107,7 @@ public abstract class NamingService { * The service may override the age and/or size limit */ /** Don't know why a dest would ever change but keep this short anyway */ - protected static final long CACHE_MAX_AGE = 60*1000; + protected static final long CACHE_MAX_AGE = 7*60*1000; private class CacheEntry { public Destination dest; @@ -174,4 +174,11 @@ public abstract class NamingService { return ce.dest; } } + + /** @since 0.8.1 */ + public void clearCache() { + synchronized (_cache) { + _cache.clear(); + } + } } From 53dd0c76551a3fcf1e6c501affdbedba2125f7e6 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:23:56 +0000 Subject: [PATCH 13/49] * Crypto: - Convert all ArrayList caching to LBQs in YKGenerator, HMACGenerator, and AESKeyCache. - Change DSAEngine params from Hash to new SHA1Hash, since these were really 20 byte hashes, not 32 byte Hashes. - Add stats to track YKGenerator caching success - Fix YKGenerator precalculation to be much more useful by increasing the cache size and dramatically shortening the delay - Option cleanups - YKGenerator cleanups - Mark HMAC256Generator unused --- .../net/i2p/crypto/CryptixAESKeyCache.java | 19 +- .../net/i2p/crypto/DHSessionKeyBuilder.java | 30 +--- core/java/src/net/i2p/crypto/DSAEngine.java | 25 ++- .../src/net/i2p/crypto/HMAC256Generator.java | 10 +- .../src/net/i2p/crypto/HMACGenerator.java | 38 ++-- core/java/src/net/i2p/crypto/SHA1.java | 2 +- core/java/src/net/i2p/crypto/SHA1Hash.java | 81 +++++++++ core/java/src/net/i2p/crypto/YKGenerator.java | 168 ++++++++---------- 8 files changed, 202 insertions(+), 171 deletions(-) create mode 100644 core/java/src/net/i2p/crypto/SHA1Hash.java diff --git a/core/java/src/net/i2p/crypto/CryptixAESKeyCache.java b/core/java/src/net/i2p/crypto/CryptixAESKeyCache.java index 5084ae2b0..5d39c1578 100644 --- a/core/java/src/net/i2p/crypto/CryptixAESKeyCache.java +++ b/core/java/src/net/i2p/crypto/CryptixAESKeyCache.java @@ -1,7 +1,6 @@ package net.i2p.crypto; -import java.util.ArrayList; -import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; /** * Cache the objects used in CryptixRijndael_Algorithm.makeKey to reduce @@ -11,7 +10,7 @@ import java.util.List; * */ public final class CryptixAESKeyCache { - private final List _availableKeys; + private final LinkedBlockingQueue _availableKeys; private static final int KEYSIZE = 32; // 256bit AES private static final int BLOCKSIZE = 16; @@ -22,7 +21,7 @@ public final class CryptixAESKeyCache { private static final int MAX_KEYS = 64; public CryptixAESKeyCache() { - _availableKeys = new ArrayList(MAX_KEYS); + _availableKeys = new LinkedBlockingQueue(MAX_KEYS); } /** @@ -30,10 +29,9 @@ public final class CryptixAESKeyCache { * */ public final KeyCacheEntry acquireKey() { - synchronized (_availableKeys) { - if (!_availableKeys.isEmpty()) - return (KeyCacheEntry)_availableKeys.remove(0); - } + KeyCacheEntry rv = _availableKeys.poll(); + if (rv != null) + return rv; return createNew(); } @@ -42,10 +40,7 @@ public final class CryptixAESKeyCache { * */ public final void releaseKey(KeyCacheEntry key) { - synchronized (_availableKeys) { - if (_availableKeys.size() < MAX_KEYS) - _availableKeys.add(key); - } + _availableKeys.offer(key); } public static final KeyCacheEntry createNew() { diff --git a/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java b/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java index 2c1e57d8e..b438f2b47 100644 --- a/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java +++ b/core/java/src/net/i2p/crypto/DHSessionKeyBuilder.java @@ -65,35 +65,17 @@ public class DHSessionKeyBuilder { public final static String PROP_DH_PRECALC_MIN = "crypto.dh.precalc.min"; public final static String PROP_DH_PRECALC_MAX = "crypto.dh.precalc.max"; public final static String PROP_DH_PRECALC_DELAY = "crypto.dh.precalc.delay"; - public final static String DEFAULT_DH_PRECALC_MIN = "5"; - public final static String DEFAULT_DH_PRECALC_MAX = "50"; - public final static String DEFAULT_DH_PRECALC_DELAY = "10000"; + public final static int DEFAULT_DH_PRECALC_MIN = 5; + public final static int DEFAULT_DH_PRECALC_MAX = 50; + public final static int DEFAULT_DH_PRECALC_DELAY = 10000; static { I2PAppContext ctx = _context; ctx.statManager().createRateStat("crypto.dhGeneratePublicTime", "How long it takes to create x and X", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 }); ctx.statManager().createRateStat("crypto.dhCalculateSessionTime", "How long it takes to create the session key", "Encryption", new long[] { 60*1000, 5*60*1000, 60*60*1000 }); - try { - int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN)); - MIN_NUM_BUILDERS = val; - } catch (Throwable t) { - int val = Integer.parseInt(DEFAULT_DH_PRECALC_MIN); - MIN_NUM_BUILDERS = val; - } - try { - int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX)); - MAX_NUM_BUILDERS = val; - } catch (Throwable t) { - int val = Integer.parseInt(DEFAULT_DH_PRECALC_MAX); - MAX_NUM_BUILDERS = val; - } - try { - int val = Integer.parseInt(ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY)); - CALC_DELAY = val; - } catch (Throwable t) { - int val = Integer.parseInt(DEFAULT_DH_PRECALC_DELAY); - CALC_DELAY = val; - } + MIN_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MIN, DEFAULT_DH_PRECALC_MIN); + MAX_NUM_BUILDERS = ctx.getProperty(PROP_DH_PRECALC_MAX, DEFAULT_DH_PRECALC_MAX); + CALC_DELAY = ctx.getProperty(PROP_DH_PRECALC_DELAY, DEFAULT_DH_PRECALC_DELAY); if (_log.shouldLog(Log.DEBUG)) _log.debug("DH Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: " diff --git a/core/java/src/net/i2p/crypto/DSAEngine.java b/core/java/src/net/i2p/crypto/DSAEngine.java index b083cc248..941774253 100644 --- a/core/java/src/net/i2p/crypto/DSAEngine.java +++ b/core/java/src/net/i2p/crypto/DSAEngine.java @@ -41,6 +41,10 @@ import net.i2p.data.SigningPublicKey; import net.i2p.util.Log; import net.i2p.util.NativeBigInteger; +/** + * Params and rv's changed from Hash to SHA1Hash for version 0.8.1 + * There shouldn't be any external users of those variants. + */ public class DSAEngine { private Log _log; private I2PAppContext _context; @@ -61,7 +65,9 @@ public class DSAEngine { public boolean verifySignature(Signature signature, InputStream in, SigningPublicKey verifyingKey) { return verifySignature(signature, calculateHash(in), verifyingKey); } - public boolean verifySignature(Signature signature, Hash hash, SigningPublicKey verifyingKey) { + + /** @param hash SHA-1 hash, NOT a SHA-256 hash */ + public boolean verifySignature(Signature signature, SHA1Hash hash, SigningPublicKey verifyingKey) { long start = _context.clock().now(); try { @@ -111,17 +117,18 @@ public class DSAEngine { } public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) { if ((signingKey == null) || (data == null) || (data.length <= 0)) return null; - Hash h = calculateHash(data, offset, length); + SHA1Hash h = calculateHash(data, offset, length); return sign(h, signingKey); } public Signature sign(InputStream in, SigningPrivateKey signingKey) { if ((signingKey == null) || (in == null) ) return null; - Hash h = calculateHash(in); + SHA1Hash h = calculateHash(in); return sign(h, signingKey); } - public Signature sign(Hash hash, SigningPrivateKey signingKey) { + /** @param hash SHA-1 hash, NOT a SHA-256 hash */ + public Signature sign(SHA1Hash hash, SigningPrivateKey signingKey) { if ((signingKey == null) || (hash == null)) return null; long start = _context.clock().now(); @@ -186,7 +193,8 @@ public class DSAEngine { return sig; } - public Hash calculateHash(InputStream in) { + /** @return hash SHA-1 hash, NOT a SHA-256 hash */ + public SHA1Hash calculateHash(InputStream in) { SHA1 digest = new SHA1(); byte buf[] = new byte[64]; int read = 0; @@ -199,14 +207,15 @@ public class DSAEngine { _log.warn("Unable to hash the stream", ioe); return null; } - return new Hash(digest.engineDigest()); + return new SHA1Hash(digest.engineDigest()); } - public static Hash calculateHash(byte[] source, int offset, int len) { + /** @return hash SHA-1 hash, NOT a SHA-256 hash */ + public static SHA1Hash calculateHash(byte[] source, int offset, int len) { SHA1 h = new SHA1(); h.engineUpdate(source, offset, len); byte digested[] = h.digest(); - return new Hash(digested); + return new SHA1Hash(digested); } public static void main(String args[]) { diff --git a/core/java/src/net/i2p/crypto/HMAC256Generator.java b/core/java/src/net/i2p/crypto/HMAC256Generator.java index bf0532109..3fc554639 100644 --- a/core/java/src/net/i2p/crypto/HMAC256Generator.java +++ b/core/java/src/net/i2p/crypto/HMAC256Generator.java @@ -15,16 +15,16 @@ import org.bouncycastle.crypto.macs.I2PHMac; * in {@link org.bouncycastle.crypto.macs.I2PHMac} and * {@link org.bouncycastle.crypto.digests.MD5Digest}. * + * deprecated unused */ public class HMAC256Generator extends HMACGenerator { public HMAC256Generator(I2PAppContext context) { super(context); } @Override protected I2PHMac acquire() { - synchronized (_available) { - if (!_available.isEmpty()) - return (I2PHMac)_available.remove(0); - } + I2PHMac rv = _available.poll(); + if (rv != null) + return rv; // the HMAC is hardcoded to use SHA256 digest size // for backwards compatability. next time we have a backwards // incompatible change, we should update this by removing ", 32" @@ -43,6 +43,7 @@ public class HMAC256Generator extends HMACGenerator { } +/****** public static void main(String args[]) { I2PAppContext ctx = I2PAppContext.getGlobalContext(); byte data[] = new byte[64]; @@ -51,4 +52,5 @@ public class HMAC256Generator extends HMACGenerator { Hash mac = ctx.hmac256().calculate(key, data); System.out.println(Base64.encode(mac.getData())); } +******/ } diff --git a/core/java/src/net/i2p/crypto/HMACGenerator.java b/core/java/src/net/i2p/crypto/HMACGenerator.java index aed444ed0..237c65055 100644 --- a/core/java/src/net/i2p/crypto/HMACGenerator.java +++ b/core/java/src/net/i2p/crypto/HMACGenerator.java @@ -1,8 +1,7 @@ package net.i2p.crypto; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; import net.i2p.I2PAppContext; import net.i2p.data.DataHelper; @@ -22,14 +21,14 @@ import org.bouncycastle.crypto.macs.I2PHMac; public class HMACGenerator { private I2PAppContext _context; /** set of available HMAC instances for calculate */ - protected final List _available; + protected final LinkedBlockingQueue _available; /** set of available byte[] buffers for verify */ - private final List _availableTmp; + private final LinkedBlockingQueue _availableTmp; public HMACGenerator(I2PAppContext context) { _context = context; - _available = new ArrayList(32); - _availableTmp = new ArrayList(32); + _available = new LinkedBlockingQueue(32); + _availableTmp = new LinkedBlockingQueue(32); } /** @@ -88,39 +87,30 @@ public class HMACGenerator { } protected I2PHMac acquire() { - synchronized (_available) { - if (!_available.isEmpty()) - return (I2PHMac)_available.remove(0); - } + I2PHMac rv = _available.poll(); + if (rv != null) + return rv; // the HMAC is hardcoded to use SHA256 digest size // for backwards compatability. next time we have a backwards // incompatible change, we should update this by removing ", 32" return new I2PHMac(new MD5Digest(), 32); } - private void release(Mac mac) { - synchronized (_available) { - if (_available.size() < 64) - _available.add(mac); - } + + private void release(I2PHMac mac) { + _available.offer(mac); } // temp buffers for verify(..) private byte[] acquireTmp() { - byte rv[] = null; - synchronized (_availableTmp) { - if (!_availableTmp.isEmpty()) - rv = (byte[])_availableTmp.remove(0); - } + byte rv[] = _availableTmp.poll(); if (rv != null) Arrays.fill(rv, (byte)0x0); else rv = new byte[Hash.HASH_LENGTH]; return rv; } + private void releaseTmp(byte tmp[]) { - synchronized (_availableTmp) { - if (_availableTmp.size() < 64) - _availableTmp.add((Object)tmp); - } + _availableTmp.offer(tmp); } } diff --git a/core/java/src/net/i2p/crypto/SHA1.java b/core/java/src/net/i2p/crypto/SHA1.java index d8a0ac1c2..6dbdee074 100644 --- a/core/java/src/net/i2p/crypto/SHA1.java +++ b/core/java/src/net/i2p/crypto/SHA1.java @@ -63,7 +63,7 @@ public final class SHA1 extends MessageDigest implements Cloneable { /** * This implementation returns a fixed-size digest. */ - private static final int HASH_LENGTH = 20; // bytes == 160 bits + static final int HASH_LENGTH = 20; // bytes == 160 bits /** * Private context for incomplete blocks and padding bytes. diff --git a/core/java/src/net/i2p/crypto/SHA1Hash.java b/core/java/src/net/i2p/crypto/SHA1Hash.java new file mode 100644 index 000000000..acbb68d4b --- /dev/null +++ b/core/java/src/net/i2p/crypto/SHA1Hash.java @@ -0,0 +1,81 @@ +package net.i2p.crypto; + +/* + * free (adj.): unencumbered; not under the control of others + * Written by jrandom in 2003 and released into the public domain + * with no warranty of any kind, either expressed or implied. + * It probably won't make your computer catch on fire, or eat + * your children, but it might. Use at your own risk. + * + */ + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import net.i2p.data.DataFormatException; +import net.i2p.data.DataHelper; +import net.i2p.data.DataStructureImpl; + +/** + * Because DSAEngine was abusing Hash for 20-byte hashes + * + * @since 0.8.1 + * @author zzz + */ +public class SHA1Hash extends DataStructureImpl { + private byte[] _data; + private int _cachedHashCode; + + public final static int HASH_LENGTH = SHA1.HASH_LENGTH; + + /** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */ + public SHA1Hash(byte data[]) { + setData(data); + } + + public byte[] getData() { + return _data; + } + + /** @throws IllegalArgumentException if data is not 20 bytes (null is ok) */ + public void setData(byte[] data) { + // FIXME DSAEngine uses a SHA-1 "Hash" as parameters and return values! + if (data != null && data.length != HASH_LENGTH) + throw new IllegalArgumentException("Hash must be 20 bytes"); + _data = data; + _cachedHashCode = calcHashCode(); + } + + /** @throws IOException always */ + public void readBytes(InputStream in) throws DataFormatException, IOException { + throw new IOException("unimplemented"); + } + + /** @throws IOException always */ + public void writeBytes(OutputStream out) throws DataFormatException, IOException { + throw new IOException("unimplemented"); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof SHA1Hash)) return false; + return DataHelper.eq(_data, ((SHA1Hash) obj)._data); + } + + /** a Hash is a hash, so just use the first 4 bytes for speed */ + @Override + public int hashCode() { + return _cachedHashCode; + } + + /** a Hash is a hash, so just use the first 4 bytes for speed */ + private int calcHashCode() { + int rv = 0; + if (_data != null) { + for (int i = 0; i < 4; i++) + rv ^= (_data[i] << (i*8)); + } + return rv; + } +} diff --git a/core/java/src/net/i2p/crypto/YKGenerator.java b/core/java/src/net/i2p/crypto/YKGenerator.java index ad68ebec1..af83f2c92 100644 --- a/core/java/src/net/i2p/crypto/YKGenerator.java +++ b/core/java/src/net/i2p/crypto/YKGenerator.java @@ -10,24 +10,22 @@ package net.i2p.crypto; */ import java.math.BigInteger; -import java.util.ArrayList; -import java.util.List; +import java.util.concurrent.LinkedBlockingQueue; import net.i2p.I2PAppContext; import net.i2p.util.Clock; import net.i2p.util.I2PThread; import net.i2p.util.Log; import net.i2p.util.NativeBigInteger; -import net.i2p.util.RandomSource; /** * Precalculate the Y and K for ElGamal encryption operations. * * This class precalcs a set of values on its own thread, using those transparently * when a new instance is created. By default, the minimum threshold for creating - * new values for the pool is 5, and the max pool size is 10. Whenever the pool has + * new values for the pool is 20, and the max pool size is 50. Whenever the pool has * less than the minimum, it fills it up again to the max. There is a delay after - * each precalculation so that the CPU isn't hosed during startup (defaulting to 10 seconds). + * each precalculation so that the CPU isn't hosed during startup. * These three parameters are controlled by java environmental variables and * can be adjusted via: * -Dcrypto.yk.precalc.min=40 -Dcrypto.yk.precalc.max=100 -Dcrypto.yk.precalc.delay=60000 @@ -39,51 +37,36 @@ import net.i2p.util.RandomSource; * @author jrandom */ class YKGenerator { - private final static Log _log = new Log(YKGenerator.class); - private static int MIN_NUM_BUILDERS = -1; - private static int MAX_NUM_BUILDERS = -1; - private static int CALC_DELAY = -1; - /* FIXME final type if you are to syncronize FIXME */ - private static volatile List _values = new ArrayList(50); // list of BigInteger[] values (y and k) - private static Thread _precalcThread = null; + //private final static Log _log = new Log(YKGenerator.class); + private static final int MIN_NUM_BUILDERS; + private static final int MAX_NUM_BUILDERS; + private static final int CALC_DELAY; + private static final LinkedBlockingQueue _values = new LinkedBlockingQueue(50); // list of BigInteger[] values (y and k) + private static final Thread _precalcThread; + private static final I2PAppContext ctx; public final static String PROP_YK_PRECALC_MIN = "crypto.yk.precalc.min"; public final static String PROP_YK_PRECALC_MAX = "crypto.yk.precalc.max"; public final static String PROP_YK_PRECALC_DELAY = "crypto.yk.precalc.delay"; - public final static String DEFAULT_YK_PRECALC_MIN = "10"; - public final static String DEFAULT_YK_PRECALC_MAX = "30"; - public final static String DEFAULT_YK_PRECALC_DELAY = "10000"; + public final static int DEFAULT_YK_PRECALC_MIN = 20; + public final static int DEFAULT_YK_PRECALC_MAX = 50; + public final static int DEFAULT_YK_PRECALC_DELAY = 200; /** check every 30 seconds whether we have less than the minimum */ - private final static long CHECK_DELAY = 30 * 1000; + private static long CHECK_DELAY = 30 * 1000; static { - I2PAppContext ctx = I2PAppContext.getGlobalContext(); - try { - int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN)); - MIN_NUM_BUILDERS = val; - } catch (Throwable t) { - int val = Integer.parseInt(DEFAULT_YK_PRECALC_MIN); - MIN_NUM_BUILDERS = val; - } - try { - int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX)); - MAX_NUM_BUILDERS = val; - } catch (Throwable t) { - int val = Integer.parseInt(DEFAULT_YK_PRECALC_MAX); - MAX_NUM_BUILDERS = val; - } - try { - int val = Integer.parseInt(ctx.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY)); - CALC_DELAY = val; - } catch (Throwable t) { - int val = Integer.parseInt(DEFAULT_YK_PRECALC_DELAY); - CALC_DELAY = val; - } + ctx = I2PAppContext.getGlobalContext(); + MIN_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MIN, DEFAULT_YK_PRECALC_MIN); + MAX_NUM_BUILDERS = ctx.getProperty(PROP_YK_PRECALC_MAX, DEFAULT_YK_PRECALC_MAX); + CALC_DELAY = ctx.getProperty(PROP_YK_PRECALC_DELAY, DEFAULT_YK_PRECALC_DELAY); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: " - + CALC_DELAY + ")"); + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("ElGamal YK Precalc (minimum: " + MIN_NUM_BUILDERS + " max: " + MAX_NUM_BUILDERS + ", delay: " + // + CALC_DELAY + ")"); + + ctx.statManager().createRateStat("crypto.YKUsed", "Need a YK from the queue", "Encryption", new long[] { 60*60*1000 }); + ctx.statManager().createRateStat("crypto.YKEmpty", "YK queue empty", "Encryption", new long[] { 60*60*1000 }); _precalcThread = new I2PThread(new YKPrecalcRunner(MIN_NUM_BUILDERS, MAX_NUM_BUILDERS)); _precalcThread.setName("YK Precalc"); @@ -93,45 +76,36 @@ class YKGenerator { } private static final int getSize() { - synchronized (_values) { - return _values.size(); - } + return _values.size(); } - private static final int addValues(BigInteger yk[]) { - int sz = 0; - synchronized (_values) { - _values.add(yk); - sz = _values.size(); - } - return sz; + /** @return true if successful, false if full */ + private static final boolean addValues(BigInteger yk[]) { + return _values.offer(yk); } + /** @return rv[0] = Y; rv[1] = K */ public static BigInteger[] getNextYK() { - if (true) { - synchronized (_values) { - if (!_values.isEmpty()) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Sufficient precalculated YK values - fetch the existing"); - return (BigInteger[]) _values.remove(0); - } - } - } - if (_log.shouldLog(Log.INFO)) _log.info("Insufficient precalculated YK values - create a new one"); + ctx.statManager().addRateData("crypto.YKUsed", 1, 0); + BigInteger[] rv = _values.poll(); + if (rv != null) + return rv; + ctx.statManager().addRateData("crypto.YKEmpty", 1, 0); return generateYK(); } private final static BigInteger _two = new NativeBigInteger(1, new byte[] { 0x02}); + /** @return rv[0] = Y; rv[1] = K */ private static final BigInteger[] generateYK() { NativeBigInteger k = null; BigInteger y = null; - long t0 = 0; - long t1 = 0; + //long t0 = 0; + //long t1 = 0; while (k == null) { - t0 = Clock.getInstance().now(); - k = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, RandomSource.getInstance()); - t1 = Clock.getInstance().now(); + //t0 = Clock.getInstance().now(); + k = new NativeBigInteger(KeyGenerator.PUBKEY_EXPONENT_SIZE, ctx.random()); + //t1 = Clock.getInstance().now(); if (BigInteger.ZERO.compareTo(k) == 0) { k = null; continue; @@ -139,39 +113,31 @@ class YKGenerator { BigInteger kPlus2 = k.add(_two); if (kPlus2.compareTo(CryptoConstants.elgp) > 0) k = null; } - long t2 = Clock.getInstance().now(); + //long t2 = Clock.getInstance().now(); y = CryptoConstants.elgg.modPow(k, CryptoConstants.elgp); BigInteger yk[] = new BigInteger[2]; yk[0] = y; yk[1] = k; - long diff = t2 - t0; - if (diff > 1000) { - if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to generate YK value for ElGamal (" + diff + "ms)"); - } + //long diff = t2 - t0; + //if (diff > 1000) { + // if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to generate YK value for ElGamal (" + diff + "ms)"); + //} return yk; } public static void main(String args[]) { - RandomSource.getInstance().nextBoolean(); // warm it up - try { - Thread.sleep(20 * 1000); - } catch (InterruptedException ie) { // nop - } - _log.debug("\n\n\n\nBegin test\n"); + System.out.println("\n\n\n\nBegin test\n"); long negTime = 0; for (int i = 0; i < 5; i++) { long startNeg = Clock.getInstance().now(); getNextYK(); long endNeg = Clock.getInstance().now(); + negTime += endNeg - startNeg; } - _log.debug("YK fetch time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each"); - try { - Thread.sleep(30 * 1000); - } catch (InterruptedException ie) { // nop - } + System.out.println("YK fetch time for 5 runs: " + negTime + " @ " + negTime / 5l + "ms each"); } private static class YKPrecalcRunner implements Runnable { @@ -186,15 +152,21 @@ class YKGenerator { public void run() { while (true) { int curSize = 0; - long start = Clock.getInstance().now(); + //long start = Clock.getInstance().now(); int startSize = getSize(); + // Adjust delay + if (startSize <= (_minSize / 2) && CHECK_DELAY > 1000) + CHECK_DELAY -= 1000; + else if (startSize > (_minSize * 2) && CHECK_DELAY < 60000) + CHECK_DELAY += 1000; curSize = startSize; - while (curSize < _minSize) { - while (curSize < _maxSize) { - long begin = Clock.getInstance().now(); - curSize = addValues(generateYK()); - long end = Clock.getInstance().now(); - if (_log.shouldLog(Log.DEBUG)) _log.debug("Precalculated YK value in " + (end - begin) + "ms"); + if (curSize < _minSize) { + for (int i = curSize; i < _maxSize; i++) { + //long begin = Clock.getInstance().now(); + if (!addValues(generateYK())) + break; + //long end = Clock.getInstance().now(); + //if (_log.shouldLog(Log.DEBUG)) _log.debug("Precalculated YK value in " + (end - begin) + "ms"); // for some relief... try { Thread.sleep(CALC_DELAY); @@ -202,14 +174,14 @@ class YKGenerator { } } } - long end = Clock.getInstance().now(); - int numCalc = curSize - startSize; - if (numCalc > 0) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Precalced " + numCalc + " to " + curSize + " in " - + (end - start - CALC_DELAY * numCalc) + "ms (not counting " - + (CALC_DELAY * numCalc) + "ms relief). now sleeping"); - } + //long end = Clock.getInstance().now(); + //int numCalc = curSize - startSize; + //if (numCalc > 0) { + // if (_log.shouldLog(Log.DEBUG)) + // _log.debug("Precalced " + numCalc + " to " + curSize + " in " + // + (end - start - CALC_DELAY * numCalc) + "ms (not counting " + // + (CALC_DELAY * numCalc) + "ms relief). now sleeping"); + //} try { Thread.sleep(CHECK_DELAY); } catch (InterruptedException ie) { // nop @@ -217,4 +189,4 @@ class YKGenerator { } } } -} \ No newline at end of file +} From c10a4f51baa010df8fe63159cca823d823d141c7 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:27:01 +0000 Subject: [PATCH 14/49] I2CP: cleanups to replace method calls with fields --- .../src/net/i2p/data/i2cp/AbuseReason.java | 6 ++--- .../src/net/i2p/data/i2cp/AbuseSeverity.java | 6 ++--- .../i2p/data/i2cp/CreateSessionMessage.java | 8 +++---- .../i2p/data/i2cp/DestroySessionMessage.java | 4 ++-- .../net/i2p/data/i2cp/I2CPMessageHandler.java | 8 ++++--- .../java/src/net/i2p/data/i2cp/MessageId.java | 8 +++---- .../i2p/data/i2cp/MessagePayloadMessage.java | 12 +++++----- .../i2p/data/i2cp/MessageStatusMessage.java | 22 ++++++++++--------- .../data/i2cp/ReceiveMessageBeginMessage.java | 8 +++---- .../data/i2cp/ReceiveMessageEndMessage.java | 8 +++---- .../data/i2cp/ReconfigureSessionMessage.java | 8 +++---- .../i2p/data/i2cp/RequestLeaseSetMessage.java | 8 +++---- .../src/net/i2p/data/i2cp/SessionConfig.java | 4 ++-- .../java/src/net/i2p/data/i2cp/SessionId.java | 8 +++---- .../src/net/i2p/data/i2cp/SetDateMessage.java | 6 ++--- 15 files changed, 63 insertions(+), 61 deletions(-) diff --git a/core/java/src/net/i2p/data/i2cp/AbuseReason.java b/core/java/src/net/i2p/data/i2cp/AbuseReason.java index f631fa591..99388faf2 100644 --- a/core/java/src/net/i2p/data/i2cp/AbuseReason.java +++ b/core/java/src/net/i2p/data/i2cp/AbuseReason.java @@ -49,16 +49,16 @@ public class AbuseReason extends DataStructureImpl { @Override public boolean equals(Object object) { if ((object == null) || !(object instanceof AbuseReason)) return false; - return DataHelper.eq(getReason(), ((AbuseReason) object).getReason()); + return DataHelper.eq(_reason, ((AbuseReason) object).getReason()); } @Override public int hashCode() { - return DataHelper.hashCode(getReason()); + return DataHelper.hashCode(_reason); } @Override public String toString() { - return "[AbuseReason: " + getReason() + "]"; + return "[AbuseReason: " + _reason + "]"; } } diff --git a/core/java/src/net/i2p/data/i2cp/AbuseSeverity.java b/core/java/src/net/i2p/data/i2cp/AbuseSeverity.java index 69cba7d24..bc3b9bbc9 100644 --- a/core/java/src/net/i2p/data/i2cp/AbuseSeverity.java +++ b/core/java/src/net/i2p/data/i2cp/AbuseSeverity.java @@ -28,7 +28,7 @@ public class AbuseSeverity extends DataStructureImpl { private int _severityId; public AbuseSeverity() { - setSeverity(-1); + _severityId = -1; } public int getSeverity() { @@ -56,11 +56,11 @@ public class AbuseSeverity extends DataStructureImpl { @Override public int hashCode() { - return getSeverity(); + return _severityId; } @Override public String toString() { - return "[AbuseSeverity: " + getSeverity() + "]"; + return "[AbuseSeverity: " + _severityId + "]"; } } diff --git a/core/java/src/net/i2p/data/i2cp/CreateSessionMessage.java b/core/java/src/net/i2p/data/i2cp/CreateSessionMessage.java index 1b8004ce0..8590a554c 100644 --- a/core/java/src/net/i2p/data/i2cp/CreateSessionMessage.java +++ b/core/java/src/net/i2p/data/i2cp/CreateSessionMessage.java @@ -27,11 +27,11 @@ public class CreateSessionMessage extends I2CPMessageImpl { private SessionConfig _sessionConfig; public CreateSessionMessage(SessionConfig config) { - setSessionConfig(config); + _sessionConfig = config; } public CreateSessionMessage() { - setSessionConfig(new SessionConfig()); + _sessionConfig = new SessionConfig(); } public SessionConfig getSessionConfig() { @@ -75,7 +75,7 @@ public class CreateSessionMessage extends I2CPMessageImpl { public boolean equals(Object object) { if ((object != null) && (object instanceof CreateSessionMessage)) { CreateSessionMessage msg = (CreateSessionMessage) object; - return DataHelper.eq(getSessionConfig(), msg.getSessionConfig()); + return DataHelper.eq(_sessionConfig, msg.getSessionConfig()); } return false; @@ -85,7 +85,7 @@ public class CreateSessionMessage extends I2CPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[CreateSessionMessage: "); - buf.append("\n\tConfig: ").append(getSessionConfig()); + buf.append("\n\tConfig: ").append(_sessionConfig); buf.append("]"); return buf.toString(); } diff --git a/core/java/src/net/i2p/data/i2cp/DestroySessionMessage.java b/core/java/src/net/i2p/data/i2cp/DestroySessionMessage.java index 8b4db852f..edb07ae41 100644 --- a/core/java/src/net/i2p/data/i2cp/DestroySessionMessage.java +++ b/core/java/src/net/i2p/data/i2cp/DestroySessionMessage.java @@ -69,7 +69,7 @@ public class DestroySessionMessage extends I2CPMessageImpl { public boolean equals(Object object) { if ((object != null) && (object instanceof DestroySessionMessage)) { DestroySessionMessage msg = (DestroySessionMessage) object; - return DataHelper.eq(getSessionId(), msg.getSessionId()); + return DataHelper.eq(_sessionId, msg.getSessionId()); } return false; @@ -86,7 +86,7 @@ public class DestroySessionMessage extends I2CPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[DestroySessionMessage: "); - buf.append("\n\tSessionId: ").append(getSessionId()); + buf.append("\n\tSessionId: ").append(_sessionId); buf.append("]"); return buf.toString(); } diff --git a/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java b/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java index 739f7823e..aaa35427b 100644 --- a/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java +++ b/core/java/src/net/i2p/data/i2cp/I2CPMessageHandler.java @@ -41,7 +41,7 @@ public class I2CPMessageHandler { try { if (length < 0) throw new I2CPMessageException("Invalid message length specified"); int type = (int) DataHelper.readLong(in, 1); - I2CPMessage msg = createMessage(in, length, type); + I2CPMessage msg = createMessage(type); msg.readMessage(in, length, type); return msg; } catch (DataFormatException dfe) { @@ -53,8 +53,8 @@ public class I2CPMessageHandler { * Yes, this is fairly ugly, but its the only place it ever happens. * */ - private static I2CPMessage createMessage(InputStream in, int length, int type) throws IOException, - I2CPMessageException { + private static I2CPMessage createMessage(int type) throws IOException, + I2CPMessageException { switch (type) { case CreateLeaseSetMessage.MESSAGE_TYPE: return new CreateLeaseSetMessage(); @@ -101,6 +101,7 @@ public class I2CPMessageHandler { } } +/*** public static void main(String args[]) { try { I2CPMessage msg = readMessage(new FileInputStream(args[0])); @@ -109,4 +110,5 @@ public class I2CPMessageHandler { e.printStackTrace(); } } +***/ } diff --git a/core/java/src/net/i2p/data/i2cp/MessageId.java b/core/java/src/net/i2p/data/i2cp/MessageId.java index d1db73761..365dc6dbb 100644 --- a/core/java/src/net/i2p/data/i2cp/MessageId.java +++ b/core/java/src/net/i2p/data/i2cp/MessageId.java @@ -27,10 +27,10 @@ public class MessageId extends DataStructureImpl { private long _messageId; public MessageId() { - setMessageId(-1); + _messageId = -1; } public MessageId(long id) { - setMessageId(id); + _messageId = id; } public long getMessageId() { @@ -58,11 +58,11 @@ public class MessageId extends DataStructureImpl { @Override public int hashCode() { - return (int)getMessageId(); + return (int)_messageId; } @Override public String toString() { - return "[MessageId: " + getMessageId() + "]"; + return "[MessageId: " + _messageId + "]"; } } diff --git a/core/java/src/net/i2p/data/i2cp/MessagePayloadMessage.java b/core/java/src/net/i2p/data/i2cp/MessagePayloadMessage.java index f06fe86f3..d9739ee58 100644 --- a/core/java/src/net/i2p/data/i2cp/MessagePayloadMessage.java +++ b/core/java/src/net/i2p/data/i2cp/MessagePayloadMessage.java @@ -29,8 +29,8 @@ public class MessagePayloadMessage extends I2CPMessageImpl { private Payload _payload; public MessagePayloadMessage() { - setSessionId(-1); - setMessageId(-1); + _sessionId = -1; + _messageId = -1; } public long getSessionId() { @@ -113,7 +113,7 @@ public class MessagePayloadMessage extends I2CPMessageImpl { MessagePayloadMessage msg = (MessagePayloadMessage) object; return _sessionId == msg.getSessionId() && _messageId == msg.getMessageId() - && DataHelper.eq(getPayload(), msg.getPayload()); + && DataHelper.eq(_payload, msg.getPayload()); } return false; @@ -123,9 +123,9 @@ public class MessagePayloadMessage extends I2CPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[MessagePayloadMessage: "); - buf.append("\n\tSessionId: ").append(getSessionId()); - buf.append("\n\tMessageId: ").append(getMessageId()); - buf.append("\n\tPayload: ").append(getPayload()); + buf.append("\n\tSessionId: ").append(_sessionId); + buf.append("\n\tMessageId: ").append(_messageId); + buf.append("\n\tPayload: ").append(_payload); buf.append("]"); return buf.toString(); } diff --git a/core/java/src/net/i2p/data/i2cp/MessageStatusMessage.java b/core/java/src/net/i2p/data/i2cp/MessageStatusMessage.java index 67fde8134..933a80785 100644 --- a/core/java/src/net/i2p/data/i2cp/MessageStatusMessage.java +++ b/core/java/src/net/i2p/data/i2cp/MessageStatusMessage.java @@ -32,17 +32,19 @@ public class MessageStatusMessage extends I2CPMessageImpl { public final static int STATUS_AVAILABLE = 0; public final static int STATUS_SEND_ACCEPTED = 1; + /** unused */ public final static int STATUS_SEND_BEST_EFFORT_SUCCESS = 2; + /** unused */ public final static int STATUS_SEND_BEST_EFFORT_FAILURE = 3; public final static int STATUS_SEND_GUARANTEED_SUCCESS = 4; public final static int STATUS_SEND_GUARANTEED_FAILURE = 5; public MessageStatusMessage() { - setSessionId(-1); - setStatus(-1); - setMessageId(-1); - setSize(-1); - setNonce(-1); + _sessionId = -1; + _status = -1; + _messageId = -1; + _size = -1; + _nonce = -1; } public long getSessionId() { @@ -169,11 +171,11 @@ public class MessageStatusMessage extends I2CPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[MessageStatusMessage: "); - buf.append("\n\tSessionId: ").append(getSessionId()); - buf.append("\n\tNonce: ").append(getNonce()); - buf.append("\n\tMessageId: ").append(getMessageId()); - buf.append("\n\tStatus: ").append(getStatusString(getStatus())); - buf.append("\n\tSize: ").append(getSize()); + buf.append("\n\tSessionId: ").append(_sessionId); + buf.append("\n\tNonce: ").append(_nonce); + buf.append("\n\tMessageId: ").append(_messageId); + buf.append("\n\tStatus: ").append(getStatusString(_status)); + buf.append("\n\tSize: ").append(_size); buf.append("]"); return buf.toString(); } diff --git a/core/java/src/net/i2p/data/i2cp/ReceiveMessageBeginMessage.java b/core/java/src/net/i2p/data/i2cp/ReceiveMessageBeginMessage.java index 6ad5e2745..15bd6779d 100644 --- a/core/java/src/net/i2p/data/i2cp/ReceiveMessageBeginMessage.java +++ b/core/java/src/net/i2p/data/i2cp/ReceiveMessageBeginMessage.java @@ -28,8 +28,8 @@ public class ReceiveMessageBeginMessage extends I2CPMessageImpl { private long _messageId; public ReceiveMessageBeginMessage() { - setSessionId(-1); - setMessageId(-1); + _sessionId = -1; + _messageId = -1; } public long getSessionId() { @@ -103,8 +103,8 @@ public class ReceiveMessageBeginMessage extends I2CPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[ReceiveMessageBeginMessage: "); - buf.append("\n\tSessionId: ").append(getSessionId()); - buf.append("\n\tMessageId: ").append(getMessageId()); + buf.append("\n\tSessionId: ").append(_sessionId); + buf.append("\n\tMessageId: ").append(_messageId); buf.append("]"); return buf.toString(); } diff --git a/core/java/src/net/i2p/data/i2cp/ReceiveMessageEndMessage.java b/core/java/src/net/i2p/data/i2cp/ReceiveMessageEndMessage.java index 79cf88382..718fd8b73 100644 --- a/core/java/src/net/i2p/data/i2cp/ReceiveMessageEndMessage.java +++ b/core/java/src/net/i2p/data/i2cp/ReceiveMessageEndMessage.java @@ -27,8 +27,8 @@ public class ReceiveMessageEndMessage extends I2CPMessageImpl { private long _messageId; public ReceiveMessageEndMessage() { - setSessionId(-1); - setMessageId(-1); + _sessionId = -1; + _messageId = -1; } public long getSessionId() { @@ -87,8 +87,8 @@ public class ReceiveMessageEndMessage extends I2CPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[ReceiveMessageEndMessage: "); - buf.append("\n\tSessionId: ").append(getSessionId()); - buf.append("\n\tMessageId: ").append(getMessageId()); + buf.append("\n\tSessionId: ").append(_sessionId); + buf.append("\n\tMessageId: ").append(_messageId); buf.append("]"); return buf.toString(); } diff --git a/core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java b/core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java index 9400091be..336fb3ed5 100644 --- a/core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java +++ b/core/java/src/net/i2p/data/i2cp/ReconfigureSessionMessage.java @@ -81,8 +81,8 @@ public class ReconfigureSessionMessage extends I2CPMessageImpl { public boolean equals(Object object) { if ((object != null) && (object instanceof ReconfigureSessionMessage)) { ReconfigureSessionMessage msg = (ReconfigureSessionMessage) object; - return DataHelper.eq(getSessionId(), msg.getSessionId()) - && DataHelper.eq(getSessionConfig(), msg.getSessionConfig()); + return DataHelper.eq(_sessionId, msg.getSessionId()) + && DataHelper.eq(_sessionConfig, msg.getSessionConfig()); } return false; @@ -92,8 +92,8 @@ public class ReconfigureSessionMessage extends I2CPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[ReconfigureSessionMessage: "); - buf.append("\n\tSessionId: ").append(getSessionId()); - buf.append("\n\tSessionConfig: ").append(getSessionConfig()); + buf.append("\n\tSessionId: ").append(_sessionId); + buf.append("\n\tSessionConfig: ").append(_sessionConfig); buf.append("]"); return buf.toString(); } diff --git a/core/java/src/net/i2p/data/i2cp/RequestLeaseSetMessage.java b/core/java/src/net/i2p/data/i2cp/RequestLeaseSetMessage.java index ef877afe5..32241e833 100644 --- a/core/java/src/net/i2p/data/i2cp/RequestLeaseSetMessage.java +++ b/core/java/src/net/i2p/data/i2cp/RequestLeaseSetMessage.java @@ -30,7 +30,7 @@ import net.i2p.data.TunnelId; public class RequestLeaseSetMessage extends I2CPMessageImpl { public final static int MESSAGE_TYPE = 21; private SessionId _sessionId; - private List _endpoints; + private List _endpoints; private Date _end; public RequestLeaseSetMessage() { @@ -51,12 +51,12 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl { public Hash getRouter(int endpoint) { if ((endpoint < 0) || (_endpoints.size() < endpoint)) return null; - return ((TunnelEndpoint) _endpoints.get(endpoint)).getRouter(); + return _endpoints.get(endpoint).getRouter(); } public TunnelId getTunnelId(int endpoint) { if ((endpoint < 0) || (_endpoints.size() < endpoint)) return null; - return ((TunnelEndpoint) _endpoints.get(endpoint)).getTunnelId(); + return _endpoints.get(endpoint).getTunnelId(); } /** @deprecated unused - presumably he meant remove? */ @@ -159,8 +159,6 @@ public class RequestLeaseSetMessage extends I2CPMessageImpl { private TunnelId _tunnelId; public TunnelEndpoint() { - _router = null; - _tunnelId = null; } public TunnelEndpoint(Hash router, TunnelId id) { diff --git a/core/java/src/net/i2p/data/i2cp/SessionConfig.java b/core/java/src/net/i2p/data/i2cp/SessionConfig.java index b96918a45..6534e0dbb 100644 --- a/core/java/src/net/i2p/data/i2cp/SessionConfig.java +++ b/core/java/src/net/i2p/data/i2cp/SessionConfig.java @@ -168,8 +168,8 @@ public class SessionConfig extends DataStructureImpl { ByteArrayOutputStream out = new ByteArrayOutputStream(); try { - _log.debug("PubKey size for destination: " + _destination.getPublicKey().getData().length); - _log.debug("SigningKey size for destination: " + _destination.getSigningPublicKey().getData().length); + //_log.debug("PubKey size for destination: " + _destination.getPublicKey().getData().length); + //_log.debug("SigningKey size for destination: " + _destination.getSigningPublicKey().getData().length); _destination.writeBytes(out); DataHelper.writeProperties(out, _options, true); // UTF-8 DataHelper.writeDate(out, _creationDate); diff --git a/core/java/src/net/i2p/data/i2cp/SessionId.java b/core/java/src/net/i2p/data/i2cp/SessionId.java index f79589c42..708ba9c46 100644 --- a/core/java/src/net/i2p/data/i2cp/SessionId.java +++ b/core/java/src/net/i2p/data/i2cp/SessionId.java @@ -26,7 +26,7 @@ public class SessionId extends DataStructureImpl { private int _sessionId; public SessionId() { - setSessionId(-1); + _sessionId = -1; } public int getSessionId() { @@ -49,16 +49,16 @@ public class SessionId extends DataStructureImpl { @Override public boolean equals(Object obj) { if ((obj == null) || !(obj instanceof SessionId)) return false; - return getSessionId() == ((SessionId) obj).getSessionId(); + return _sessionId == ((SessionId) obj).getSessionId(); } @Override public int hashCode() { - return getSessionId(); + return _sessionId; } @Override public String toString() { - return "[SessionId: " + getSessionId() + "]"; + return "[SessionId: " + _sessionId + "]"; } } diff --git a/core/java/src/net/i2p/data/i2cp/SetDateMessage.java b/core/java/src/net/i2p/data/i2cp/SetDateMessage.java index f0abce1a5..9f6633b6d 100644 --- a/core/java/src/net/i2p/data/i2cp/SetDateMessage.java +++ b/core/java/src/net/i2p/data/i2cp/SetDateMessage.java @@ -28,7 +28,7 @@ public class SetDateMessage extends I2CPMessageImpl { public SetDateMessage() { super(); - setDate(new Date(Clock.getInstance().now())); + _date = new Date(Clock.getInstance().now()); } public Date getDate() { @@ -70,7 +70,7 @@ public class SetDateMessage extends I2CPMessageImpl { public boolean equals(Object object) { if ((object != null) && (object instanceof SetDateMessage)) { SetDateMessage msg = (SetDateMessage) object; - return DataHelper.eq(getDate(), msg.getDate()); + return DataHelper.eq(_date, msg.getDate()); } return false; @@ -80,7 +80,7 @@ public class SetDateMessage extends I2CPMessageImpl { public String toString() { StringBuilder buf = new StringBuilder(); buf.append("[SetDateMessage"); - buf.append("\n\tDate: ").append(getDate()); + buf.append("\n\tDate: ").append(_date); buf.append("]"); return buf.toString(); } From 4c31c70298aba8f4d47c3f4a8c7f5f37a4faeda6 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:28:27 +0000 Subject: [PATCH 15/49] Startup hint in installer for non-x86 --- installer/resources/start-i2p.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/installer/resources/start-i2p.txt b/installer/resources/start-i2p.txt index e20ed1e73..2e51ea48c 100644 --- a/installer/resources/start-i2p.txt +++ b/installer/resources/start-i2p.txt @@ -1,3 +1,8 @@ To start I2P, run: $INSTALL_PATH/i2prouter start + + +On non-x86, run: + +$INSTALL_PATH/runplain.sh From 4456048e79cde5019108db7c8406a9d8169cccc9 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:43:40 +0000 Subject: [PATCH 16/49] * HTTPResponseOutputStream - More caching - Stats cleanup - Max header length check - Catch OOM - Initializer cleanup - Javadoc --- .../i2ptunnel/HTTPResponseOutputStream.java | 68 ++++++++++++------- .../i2p/i2ptunnel/I2PTunnelHTTPServer.java | 1 + 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java index af4049a21..326ebb644 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/HTTPResponseOutputStream.java @@ -24,6 +24,9 @@ import net.i2p.util.I2PAppThread; import net.i2p.util.Log; /** + * This does the transparent gzip decompression on the client side. + * Extended in I2PTunnelHTTPServer to do the compression on the server side. + * * Simple stream for delivering an HTTP response to * the client, trivially filtered to make sure "Connection: close" * is always in the response. Perhaps add transparent handling of the @@ -33,29 +36,27 @@ import net.i2p.util.Log; * */ class HTTPResponseOutputStream extends FilterOutputStream { - private I2PAppContext _context; - private Log _log; - private ByteCache _cache; + private final I2PAppContext _context; + private final Log _log; protected ByteArray _headerBuffer; private boolean _headerWritten; - private byte _buf1[]; + private final byte _buf1[]; protected boolean _gzip; private long _dataWritten; private InternalGZIPInputStream _in; private static final int CACHE_SIZE = 8*1024; + private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE); + // OOM DOS prevention + private static final int MAX_HEADER_SIZE = 64*1024; public HTTPResponseOutputStream(OutputStream raw) { super(raw); _context = I2PAppContext.getGlobalContext(); - _context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*1000, 30*60*1000 }); - _context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*1000, 30*60*1000 }); - _context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*1000, 30*60*1000 }); + _context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*60*1000 }); + _context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*60*1000 }); + _context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*60*1000 }); _log = _context.logManager().getLog(getClass()); - _cache = ByteCache.getInstance(8, CACHE_SIZE); _headerBuffer = _cache.acquire(); - _headerWritten = false; - _gzip = false; - _dataWritten = 0; _buf1 = new byte[1]; } @@ -96,14 +97,20 @@ class HTTPResponseOutputStream extends FilterOutputStream { } } - /** grow (and free) the buffer as necessary */ - private void ensureCapacity() { + /** + * grow (and free) the buffer as necessary + * @throws IOException if the headers are too big + */ + private void ensureCapacity() throws IOException { + if (_headerBuffer.getValid() >= MAX_HEADER_SIZE) + throw new IOException("Max header size exceeded: " + MAX_HEADER_SIZE); if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) { int newSize = (int)(_headerBuffer.getData().length * 1.5); ByteArray newBuf = new ByteArray(new byte[newSize]); System.arraycopy(_headerBuffer.getData(), 0, newBuf.getData(), 0, _headerBuffer.getValid()); newBuf.setValid(_headerBuffer.getValid()); newBuf.setOffset(0); + // if we changed the ByteArray size, don't put it back in the cache if (_headerBuffer.getData().length == CACHE_SIZE) _cache.release(_headerBuffer); _headerBuffer = newBuf; @@ -219,7 +226,7 @@ class HTTPResponseOutputStream extends FilterOutputStream { //out.flush(); PipedInputStream pi = new PipedInputStream(); PipedOutputStream po = new PipedOutputStream(pi); - new I2PAppThread(new Pusher(pi, out), "HTTP decompresser").start(); + new I2PAppThread(new Pusher(pi, out), "HTTP decompressor").start(); out = po; } @@ -231,13 +238,13 @@ class HTTPResponseOutputStream extends FilterOutputStream { _out = out; } public void run() { - OutputStream to = null; _in = null; - long start = System.currentTimeMillis(); long written = 0; + ByteArray ba = null; try { _in = new InternalGZIPInputStream(_inRaw); - byte buf[] = new byte[8192]; + ba = _cache.acquire(); + byte buf[] = ba.getData(); int read = -1; while ( (read = _in.read(buf)) != -1) { if (_log.shouldLog(Log.DEBUG)) @@ -251,6 +258,8 @@ class HTTPResponseOutputStream extends FilterOutputStream { } catch (IOException ioe) { if (_log.shouldLog(Log.WARN)) _log.warn("Error decompressing: " + written + ", " + (_in != null ? _in.getTotalRead() + "/" + _in.getTotalExpanded() : ""), ioe); + } catch (OutOfMemoryError oom) { + _log.error("OOM in HTTP Decompressor", oom); } finally { if (_log.shouldLog(Log.WARN) && (_in != null)) _log.warn("After decompression, written=" + written + @@ -259,23 +268,26 @@ class HTTPResponseOutputStream extends FilterOutputStream { + ", expanded=" + _in.getTotalExpanded() + ", remaining=" + _in.getRemaining() + ", finished=" + _in.getFinished() : "")); + if (ba != null) + _cache.release(ba); if (_out != null) try { _out.close(); } catch (IOException ioe) {} } - long end = System.currentTimeMillis(); + double compressed = (_in != null ? _in.getTotalRead() : 0); double expanded = (_in != null ? _in.getTotalExpanded() : 0); - double ratio = 0; - if (expanded > 0) - ratio = compressed/expanded; - - _context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start); - _context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, end-start); - _context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start); + if (compressed > 0 && expanded > 0) { + // only update the stats if we did something + double ratio = compressed/expanded; + _context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), 0); + _context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, 0); + _context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, 0); + } } } + /** just a wrapper to provide stats for debugging */ private static class InternalGZIPInputStream extends GZIPInputStream { public InternalGZIPInputStream(InputStream in) throws IOException { super(in); @@ -294,6 +306,12 @@ class HTTPResponseOutputStream extends FilterOutputStream { return 0; } } + + /** + * From Inflater javadoc: + * Returns the total number of bytes remaining in the input buffer. This can be used to find out + * what bytes still remain in the input buffer after decompression has finished. + */ public long getRemaining() { try { return super.inf.getRemaining(); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index 728d3537c..ca568ab92 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -290,6 +290,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { } } + /** just a wrapper to provide stats for debugging */ private static class InternalGZIPOutputStream extends GZIPOutputStream { public InternalGZIPOutputStream(OutputStream target) throws IOException { super(target); From 7424fdd623ee52585b0b0169f1def03ad24aad3b Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:45:13 +0000 Subject: [PATCH 17/49] javadoc --- .../src/net/i2p/data/i2np/BuildRequestRecord.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java b/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java index 7ff81a2ad..667d7fb18 100644 --- a/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java +++ b/router/java/src/net/i2p/data/i2np/BuildRequestRecord.java @@ -9,7 +9,9 @@ import net.i2p.data.PublicKey; import net.i2p.data.SessionKey; /** - * Hold the tunnel request record, managing its encryption and decryption. + * Hold the tunnel request record, managing its ElGamal encryption and decryption. + * Iterative AES encryption/decryption is done elsewhere. + * * Cleartext: *

  *   bytes     0-3: tunnel ID to receive messages as
@@ -26,6 +28,12 @@ import net.i2p.data.SessionKey;
  *   bytes 193-221: uninterpreted / random padding
  * 
* + * Encrypted: + *
+ *   bytes    0-15: First 16 bytes of router hash
+ *   bytes  16-527: ElGamal encrypted block (discarding zero bytes at elg[0] and elg[257])
+ * 
+ * */ public class BuildRequestRecord { private ByteArray _data; @@ -152,7 +160,7 @@ public class BuildRequestRecord { /** * Encrypt the record to the specified peer. The result is formatted as:
-     *   bytes 0-15: SHA-256-128 of the current hop's identity (the toPeer parameter)
+     *   bytes 0-15: truncated SHA-256 of the current hop's identity (the toPeer parameter)
      * bytes 15-527: ElGamal-2048 encrypted block
      * 
*/ From 939dcee537c20b9bfae720535774d6c943c91636 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:48:50 +0000 Subject: [PATCH 18/49] * GeoIP: Fix locking bug causing lookups to stop --- .../src/net/i2p/router/transport/GeoIP.java | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/GeoIP.java b/router/java/src/net/i2p/router/transport/GeoIP.java index cc0a63ba6..85a89b3c6 100644 --- a/router/java/src/net/i2p/router/transport/GeoIP.java +++ b/router/java/src/net/i2p/router/transport/GeoIP.java @@ -98,23 +98,26 @@ public class GeoIP { public void run() { if (_lock.getAndSet(true)) return; - // clear the negative cache every few runs, to prevent it from getting too big - if (((++_lookupRunCount) % CLEAR) == 0) - _notFound.clear(); - Long[] search = _pendingSearch.toArray(new Long[_pendingSearch.size()]); - if (search.length <= 0) - return; - _pendingSearch.clear(); - Arrays.sort(search); - String[] countries = readGeoIPFile(search); - - for (int i = 0; i < countries.length; i++) { - if (countries[i] != null) - _IPToCountry.put(search[i], countries[i]); - else - _notFound.add(search[i]); + try { + // clear the negative cache every few runs, to prevent it from getting too big + if (((++_lookupRunCount) % CLEAR) == 0) + _notFound.clear(); + Long[] search = _pendingSearch.toArray(new Long[_pendingSearch.size()]); + if (search.length <= 0) + return; + _pendingSearch.clear(); + Arrays.sort(search); + String[] countries = readGeoIPFile(search); + + for (int i = 0; i < countries.length; i++) { + if (countries[i] != null) + _IPToCountry.put(search[i], countries[i]); + else + _notFound.add(search[i]); + } + } finally { + _lock.set(false); } - _lock.set(false); } } From ed4c09b45668074d0891f64b33d122b2576b71b8 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 14:56:45 +0000 Subject: [PATCH 19/49] * Tunnels: - Don't use peers < 0.7.9 for tunnels due to the old message corruption bugs - Javadoc - Cleanups --- .../router/tunnel/BuildMessageProcessor.java | 2 +- .../tunnel/InboundEndpointProcessor.java | 13 +++-- .../tunnel/OutboundGatewayProcessor.java | 17 +++--- .../net/i2p/router/tunnel/TunnelGateway.java | 9 ++-- .../tunnel/pool/ClientPeerSelector.java | 5 +- .../tunnel/pool/ExploratoryPeerSelector.java | 49 +++++++++-------- .../pool/PooledTunnelCreatorConfig.java | 2 +- .../tunnel/pool/TunnelPeerSelector.java | 52 ++++++++++++++++--- 8 files changed, 99 insertions(+), 50 deletions(-) diff --git a/router/java/src/net/i2p/router/tunnel/BuildMessageProcessor.java b/router/java/src/net/i2p/router/tunnel/BuildMessageProcessor.java index fddccd52f..1194cc103 100644 --- a/router/java/src/net/i2p/router/tunnel/BuildMessageProcessor.java +++ b/router/java/src/net/i2p/router/tunnel/BuildMessageProcessor.java @@ -24,7 +24,7 @@ public class BuildMessageProcessor { public BuildMessageProcessor(I2PAppContext ctx) { _filter = new DecayingHashSet(ctx, 60*1000, 32, "TunnelBMP"); - ctx.statManager().createRateStat("tunnel.buildRequestDup", "How frequently we get dup build request messages", "Tunnels", new long[] { 60*1000, 10*60*1000 }); + ctx.statManager().createRateStat("tunnel.buildRequestDup", "How frequently we get dup build request messages", "Tunnels", new long[] { 60*60*1000 }); } /** * Decrypt the record targetting us, encrypting all of the other records with the included diff --git a/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java b/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java index 7376a5046..6d467016a 100644 --- a/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java +++ b/router/java/src/net/i2p/router/tunnel/InboundEndpointProcessor.java @@ -15,17 +15,19 @@ import net.i2p.util.Log; * */ public class InboundEndpointProcessor { - private RouterContext _context; - private Log _log; - private TunnelCreatorConfig _config; - private IVValidator _validator; + private final RouterContext _context; + private final Log _log; + private final TunnelCreatorConfig _config; + private final IVValidator _validator; static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION; private static final ByteCache _cache = ByteCache.getInstance(128, HopProcessor.IV_LENGTH); + /** @deprecated unused */ public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg) { this(ctx, cfg, DummyValidator.getInstance()); } + public InboundEndpointProcessor(RouterContext ctx, TunnelCreatorConfig cfg, IVValidator validator) { _context = ctx; _log = ctx.logManager().getLog(InboundEndpointProcessor.class); @@ -84,6 +86,9 @@ public class InboundEndpointProcessor { return true; } + /** + * Iteratively undo the crypto that the various layers in the tunnel added. + */ private void decrypt(RouterContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) { Log log = ctx.logManager().getLog(OutboundGatewayProcessor.class); ByteArray ba = _cache.acquire(); diff --git a/router/java/src/net/i2p/router/tunnel/OutboundGatewayProcessor.java b/router/java/src/net/i2p/router/tunnel/OutboundGatewayProcessor.java index 36593db09..bd9120080 100644 --- a/router/java/src/net/i2p/router/tunnel/OutboundGatewayProcessor.java +++ b/router/java/src/net/i2p/router/tunnel/OutboundGatewayProcessor.java @@ -14,9 +14,9 @@ import net.i2p.util.Log; * */ public class OutboundGatewayProcessor { - private I2PAppContext _context; - private Log _log; - private TunnelCreatorConfig _config; + private final I2PAppContext _context; + private final Log _log; + private final TunnelCreatorConfig _config; static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION; private static final ByteCache _cache = ByteCache.getInstance(128, HopProcessor.IV_LENGTH); @@ -54,10 +54,8 @@ public class OutboundGatewayProcessor { } /** - * Undo the crypto that the various layers in the tunnel added. This is used - * by both the outbound gateway (preemptively undoing the crypto peers will add) - * and by the inbound endpoint. - * + * Iteratively undo the crypto that the various layers in the tunnel added. This is used + * by the outbound gateway (preemptively undoing the crypto peers will add). */ private void decrypt(I2PAppContext ctx, TunnelCreatorConfig cfg, byte iv[], byte orig[], int offset, int length) { Log log = ctx.logManager().getLog(OutboundGatewayProcessor.class); @@ -73,6 +71,11 @@ public class OutboundGatewayProcessor { _cache.release(ba); } + /** + * Undo the crypto for a single hop. This is used + * by both the outbound gateway (preemptively undoing the crypto peers will add) + * and by the inbound endpoint. + */ static void decrypt(I2PAppContext ctx, byte iv[], byte orig[], int offset, int length, byte cur[], HopConfig config) { // update the IV for the previous (next?) hop ctx.aes().decryptBlock(orig, offset, config.getIVKey(), orig, offset); diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java index 7291c8706..3ec567098 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java @@ -60,7 +60,6 @@ public class TunnelGateway { _preprocessor = preprocessor; _sender = sender; _receiver = receiver; - _messagesSent = 0; _flushFrequency = 500; _delayedFlush = new DelayedFlush(); _lastFlush = _context.clock().now(); @@ -128,8 +127,8 @@ public class TunnelGateway { FlushTimer.getInstance().addEvent(_delayedFlush, delayAmount); } _context.statManager().addRateData("tunnel.lockedGatewayAdd", afterAdded-beforeLock, remaining); - long complete = System.currentTimeMillis(); - if (_log.shouldLog(Log.DEBUG)) + if (_log.shouldLog(Log.DEBUG)) { + long complete = System.currentTimeMillis(); _log.debug("Time to add the message " + msg.getUniqueId() + ": " + (complete-startAdd) + " delayed? " + delayedFlush + " remaining: " + remaining + " prepare: " + (beforeLock-startAdd) @@ -137,6 +136,7 @@ public class TunnelGateway { + " preprocess: " + (afterPreprocess-afterAdded) + " expire: " + (afterExpire-afterPreprocess) + " queue flush: " + (complete-afterExpire)); + } } public int getMessagesSent() { return _messagesSent; } @@ -202,10 +202,7 @@ public class TunnelGateway { _messageId = message.getUniqueId(); _expiration = message.getMessageExpiration(); _remaining = message.toByteArray(); - _offset = 0; - _fragmentNumber = 0; _created = now; - _messageIds = null; } /** may be null */ public Hash getToRouter() { return _toRouter; } diff --git a/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java index a9199b690..f4042feee 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ClientPeerSelector.java @@ -5,6 +5,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import net.i2p.data.Hash; import net.i2p.router.RouterContext; import net.i2p.router.TunnelPoolSettings; @@ -14,7 +15,7 @@ import net.i2p.router.TunnelPoolSettings; * */ class ClientPeerSelector extends TunnelPeerSelector { - public List selectPeers(RouterContext ctx, TunnelPoolSettings settings) { + public List selectPeers(RouterContext ctx, TunnelPoolSettings settings) { int length = getLength(ctx, settings); if (length < 0) return null; @@ -31,7 +32,7 @@ class ClientPeerSelector extends TunnelPeerSelector { ctx.profileOrganizer().selectFastPeers(length, exclude, matches, settings.getIPRestriction()); matches.remove(ctx.routerHash()); - ArrayList rv = new ArrayList(matches); + ArrayList rv = new ArrayList(matches); if (rv.size() > 1) orderPeers(rv, settings.getRandomKey()); if (settings.isInbound()) diff --git a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java index 073088fb3..503504cac 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/ExploratoryPeerSelector.java @@ -5,6 +5,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import net.i2p.data.Hash; import net.i2p.router.RouterContext; import net.i2p.router.TunnelPoolSettings; import net.i2p.stat.Rate; @@ -17,7 +18,7 @@ import net.i2p.util.Log; * */ class ExploratoryPeerSelector extends TunnelPeerSelector { - public List selectPeers(RouterContext ctx, TunnelPoolSettings settings) { + public List selectPeers(RouterContext ctx, TunnelPoolSettings settings) { Log l = ctx.logManager().getLog(getClass()); int length = getLength(ctx, settings); if (length < 0) { @@ -33,7 +34,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { return rv; } - Set exclude = getExclude(ctx, settings.isInbound(), settings.isExploratory()); + Set exclude = getExclude(ctx, settings.isInbound(), settings.isExploratory()); exclude.add(ctx.routerHash()); // Don't use ff peers for exploratory tunnels to lessen exposure to netDb searches and stores // Hmm if they don't get explored they don't get a speed/capacity rating @@ -56,7 +57,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { l.debug("profileOrganizer.selectNotFailing(" + length + ") found " + matches); matches.remove(ctx.routerHash()); - ArrayList rv = new ArrayList(matches); + ArrayList rv = new ArrayList(matches); if (rv.size() > 1) orderPeers(rv, settings.getRandomKey()); if (settings.isInbound()) @@ -67,7 +68,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { } private static final int MIN_NONFAILING_PCT = 25; - private boolean shouldPickHighCap(RouterContext ctx) { + private static boolean shouldPickHighCap(RouterContext ctx) { if (Boolean.valueOf(ctx.getProperty("router.exploreHighCapacity", "false")).booleanValue()) return true; // no need to explore too wildly at first @@ -86,9 +87,9 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { failPct = 100 - MIN_NONFAILING_PCT; } else { failPct = getExploratoryFailPercentage(ctx); - Log l = ctx.logManager().getLog(getClass()); - if (l.shouldLog(Log.DEBUG)) - l.debug("Normalized Fail pct: " + failPct); + //Log l = ctx.logManager().getLog(getClass()); + //if (l.shouldLog(Log.DEBUG)) + // l.debug("Normalized Fail pct: " + failPct); // always try a little, this helps keep the failPct stat accurate too if (failPct > 100 - MIN_NONFAILING_PCT) failPct = 100 - MIN_NONFAILING_PCT; @@ -96,21 +97,23 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { return (failPct >= ctx.random().nextInt(100)); } - // We should really use the difference between the exploratory fail rate - // and the high capacity fail rate - but we don't have a stat for high cap, - // so use the fast (== client) fail rate, it should be close - // if the expl. and client tunnel lengths aren't too different. - // So calculate the difference between the exploratory fail rate - // and the client fail rate, normalized to 100: - // 100 * ((Efail - Cfail) / (100 - Cfail)) - // Even this isn't the "true" rate for the NonFailingPeers pool, since we - // are often building exploratory tunnels using the HighCapacity pool. - private int getExploratoryFailPercentage(RouterContext ctx) { + /** + * We should really use the difference between the exploratory fail rate + * and the high capacity fail rate - but we don't have a stat for high cap, + * so use the fast (== client) fail rate, it should be close + * if the expl. and client tunnel lengths aren't too different. + * So calculate the difference between the exploratory fail rate + * and the client fail rate, normalized to 100: + * 100 * ((Efail - Cfail) / (100 - Cfail)) + * Even this isn't the "true" rate for the NonFailingPeers pool, since we + * are often building exploratory tunnels using the HighCapacity pool. + */ + private static int getExploratoryFailPercentage(RouterContext ctx) { int c = getFailPercentage(ctx, "Client"); int e = getFailPercentage(ctx, "Exploratory"); - Log l = ctx.logManager().getLog(getClass()); - if (l.shouldLog(Log.DEBUG)) - l.debug("Client, Expl. Fail pct: " + c + ", " + e); + //Log l = ctx.logManager().getLog(getClass()); + //if (l.shouldLog(Log.DEBUG)) + // l.debug("Client, Expl. Fail pct: " + c + ", " + e); if (e <= c || e <= 25) // doing very well (unlikely) return 0; if (c >= 90) // doing very badly @@ -118,7 +121,7 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { return (100 * (e-c)) / (100-c); } - private int getFailPercentage(RouterContext ctx, String t) { + private static int getFailPercentage(RouterContext ctx, String t) { String pfx = "tunnel.build" + t; int timeout = getEvents(ctx, pfx + "Expire", 10*60*1000); int reject = getEvents(ctx, pfx + "Reject", 10*60*1000); @@ -129,8 +132,8 @@ class ExploratoryPeerSelector extends TunnelPeerSelector { return (int)(100 * pct); } - // Use current + last to get more recent and smoother data - private int getEvents(RouterContext ctx, String stat, long period) { + /** Use current + last to get more recent and smoother data */ + private static int getEvents(RouterContext ctx, String stat, long period) { RateStat rs = ctx.statManager().getRate(stat); if (rs == null) return 0; diff --git a/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java b/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java index afe149fc6..cbe794815 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java +++ b/router/java/src/net/i2p/router/tunnel/pool/PooledTunnelCreatorConfig.java @@ -12,7 +12,7 @@ import net.i2p.util.Log; /** * */ -public class PooledTunnelCreatorConfig extends TunnelCreatorConfig { +class PooledTunnelCreatorConfig extends TunnelCreatorConfig { private TunnelPool _pool; private TestJob _testJob; // private Job _expireJob; 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 bb22ab0e4..083e2501a 100644 --- a/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java +++ b/router/java/src/net/i2p/router/tunnel/pool/TunnelPeerSelector.java @@ -21,10 +21,13 @@ import net.i2p.router.TunnelPoolSettings; import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade; import net.i2p.router.networkdb.kademlia.HashDistance; import net.i2p.util.Log; +import net.i2p.util.VersionComparator; /** * Coordinate the selection of peers to go into a tunnel for one particular * pool. + * + * Todo: there's nothing non-static in here */ public abstract class TunnelPeerSelector { /** @@ -36,7 +39,7 @@ public abstract class TunnelPeerSelector { * to build through, and the settings reject 0 hop tunnels, this will * return null. */ - public abstract List selectPeers(RouterContext ctx, TunnelPoolSettings settings); + public abstract List selectPeers(RouterContext ctx, TunnelPoolSettings settings); protected int getLength(RouterContext ctx, TunnelPoolSettings settings) { int length = settings.getLength(); @@ -79,6 +82,11 @@ public abstract class TunnelPeerSelector { return length; } + /** + * For debugging, also possibly for restricted routes? + * Needs analysis and testing + * @return should always be false + */ protected boolean shouldSelectExplicit(TunnelPoolSettings settings) { if (settings.isExploratory()) return false; Properties opts = settings.getUnknownOptions(); @@ -92,7 +100,12 @@ public abstract class TunnelPeerSelector { return false; } - protected List selectExplicit(RouterContext ctx, TunnelPoolSettings settings, int length) { + /** + * For debugging, also possibly for restricted routes? + * Needs analysis and testing + * @return should always be false + */ + protected List selectExplicit(RouterContext ctx, TunnelPoolSettings settings, int length) { String peers = null; Properties opts = settings.getUnknownOptions(); if (opts != null) @@ -102,7 +115,7 @@ public abstract class TunnelPeerSelector { peers = I2PAppContext.getGlobalContext().getProperty("explicitPeers"); Log log = ctx.logManager().getLog(ClientPeerSelector.class); - List rv = new ArrayList(); + List rv = new ArrayList(); StringTokenizer tok = new StringTokenizer(peers, ","); while (tok.hasMoreTokens()) { String peerStr = tok.nextToken(); @@ -156,7 +169,7 @@ public abstract class TunnelPeerSelector { /** * Pick peers that we want to avoid */ - public Set getExclude(RouterContext ctx, boolean isInbound, boolean isExploratory) { + public Set getExclude(RouterContext ctx, boolean isInbound, boolean isExploratory) { // we may want to update this to skip 'hidden' or 'unreachable' peers, but that // isn't safe, since they may publish one set of routerInfo to us and another to // other peers. the defaults for filterUnreachable has always been to return false, @@ -175,11 +188,12 @@ public abstract class TunnelPeerSelector { // // Defaults changed to true for inbound only in filterUnreachable below. - Set peers = new HashSet(1); + Set peers = new HashSet(1); peers.addAll(ctx.profileOrganizer().selectPeersRecentlyRejecting()); peers.addAll(ctx.tunnelManager().selectPeersInTooManyTunnels()); // if (false && filterUnreachable(ctx, isInbound, isExploratory)) { if (filterUnreachable(ctx, isInbound, isExploratory)) { + // NOTE: filterUnreachable returns true for inbound, false for outbound // This is the only use for getPeersByCapability? And the whole set of datastructures in PeerManager? List caps = ctx.peerManager().getPeersByCapability(Router.CAPABILITY_UNREACHABLE); if (caps != null) @@ -189,6 +203,7 @@ public abstract class TunnelPeerSelector { peers.addAll(caps); } if (filterSlow(ctx, isInbound, isExploratory)) { + // NOTE: filterSlow always returns true Log log = ctx.logManager().getLog(TunnelPeerSelector.class); char excl[] = getExcludeCaps(ctx); if (excl != null) { @@ -301,6 +316,7 @@ public abstract class TunnelPeerSelector { return peers; } + /** warning, this is also called by ProfileOrganizer.isSelectable() */ public static boolean shouldExclude(RouterContext ctx, RouterInfo peer) { Log log = ctx.logManager().getLog(TunnelPeerSelector.class); return shouldExclude(ctx, log, peer, getExcludeCaps(ctx)); @@ -318,6 +334,10 @@ public abstract class TunnelPeerSelector { } private static final long DONT_EXCLUDE_PERIOD = 15*60*1000; + /** 0.7.8 and earlier had major message corruption bugs */ + private static final String MIN_VERSION = "0.7.9"; + private static final VersionComparator _versionComparator = new VersionComparator(); + private static boolean shouldExclude(RouterContext ctx, Log log, RouterInfo peer, char excl[]) { String cap = peer.getCapabilities(); if (cap == null) { @@ -340,6 +360,13 @@ public abstract class TunnelPeerSelector { // otherwise, it contains flags we aren't trying to focus on, // so don't exclude it based on published capacity + // minimum version check + String v = peer.getOption("router.version"); + if (v == null || _versionComparator.compare(v, MIN_VERSION) < 0) + return true; + + // uptime is always spoofed to 90m, so just remove all this + /****** String val = peer.getOption("stat_uptime"); if (val != null) { long uptimeMs = 0; @@ -390,6 +417,8 @@ public abstract class TunnelPeerSelector { // not publishing an uptime, so exclude it return true; } + ******/ + return false; } private static final String PROP_OUTBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "router.outboundExploratoryExcludeUnreachable"; @@ -403,6 +432,10 @@ public abstract class TunnelPeerSelector { private static final String DEFAULT_INBOUND_EXPLORATORY_EXCLUDE_UNREACHABLE = "true"; private static final String DEFAULT_INBOUND_CLIENT_EXCLUDE_UNREACHABLE = "true"; + /** + * do we want to skip peers who haven't been up for long? + * @return true for inbound, false for outbound, unless configured otherwise + */ protected boolean filterUnreachable(RouterContext ctx, boolean isInbound, boolean isExploratory) { boolean def = false; String val = null; @@ -429,6 +462,10 @@ public abstract class TunnelPeerSelector { private static final String PROP_INBOUND_EXPLORATORY_EXCLUDE_SLOW = "router.inboundExploratoryExcludeSlow"; private static final String PROP_INBOUND_CLIENT_EXCLUDE_SLOW = "router.inboundClientExcludeSlow"; + /** + * do we want to skip peers that are slow? + * @return true unless configured otherwise + */ protected boolean filterSlow(RouterContext ctx, boolean isInbound, boolean isExploratory) { boolean def = true; String val = null; @@ -454,7 +491,10 @@ public abstract class TunnelPeerSelector { private static final String PROP_INBOUND_EXPLORATORY_EXCLUDE_UPTIME = "router.inboundExploratoryExcludeUptime"; private static final String PROP_INBOUND_CLIENT_EXCLUDE_UPTIME = "router.inboundClientExcludeUptime"; - /** do we want to skip peers who haven't been up for long? */ + /** + * do we want to skip peers who haven't been up for long? + * @return true unless configured otherwise + */ protected boolean filterUptime(RouterContext ctx, boolean isInbound, boolean isExploratory) { boolean def = true; String val = null; From 4a96e881189383399206504e139094f6d60fe353 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:00:30 +0000 Subject: [PATCH 20/49] * Hash: Throw IAE if data length is not 32 bytes, now that DSAEngine abuse is gone --- core/java/src/net/i2p/data/Hash.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/java/src/net/i2p/data/Hash.java b/core/java/src/net/i2p/data/Hash.java index 83579d116..afd173c24 100644 --- a/core/java/src/net/i2p/data/Hash.java +++ b/core/java/src/net/i2p/data/Hash.java @@ -31,6 +31,7 @@ public class Hash extends DataStructureImpl { public Hash() { } + /** @throws IllegalArgumentException if data is not 32 bytes (null is ok) */ public Hash(byte data[]) { setData(data); } @@ -39,7 +40,10 @@ public class Hash extends DataStructureImpl { return _data; } + /** @throws IllegalArgumentException if data is not 32 bytes (null is ok) */ public void setData(byte[] data) { + if (data != null && data.length != HASH_LENGTH) + throw new IllegalArgumentException("Hash must be 32 bytes"); _data = data; _stringified = null; _base64ed = null; From 6100c799b7b120b3f3f22dcc8bdae9c5ea04ab3a Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:03:20 +0000 Subject: [PATCH 21/49] LogConsoleBuffer cleanup --- .../java/src/net/i2p/router/web/LogsHelper.java | 7 +++++-- core/java/src/net/i2p/util/LogConsoleBuffer.java | 10 +++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java index d6eb2edd1..f499b2ea4 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/LogsHelper.java @@ -44,13 +44,16 @@ public class LogsHelper extends HelperBase { } ******/ - private String formatMessages(List msgs) { + /** formats in reverse order */ + private String formatMessages(List msgs) { + if (msgs.isEmpty()) + return "

" + _("No log messages") + "

"; boolean colorize = Boolean.valueOf(_context.getProperty("routerconsole.logs.color")).booleanValue(); StringBuilder buf = new StringBuilder(16*1024); buf.append("
    "); buf.append("\n"); for (int i = msgs.size(); i > 0; i--) { - String msg = (String)msgs.get(i - 1); + String msg = msgs.get(i - 1); buf.append("
  • "); if (colorize) { String color; diff --git a/core/java/src/net/i2p/util/LogConsoleBuffer.java b/core/java/src/net/i2p/util/LogConsoleBuffer.java index a9aab2625..2fe270849 100644 --- a/core/java/src/net/i2p/util/LogConsoleBuffer.java +++ b/core/java/src/net/i2p/util/LogConsoleBuffer.java @@ -11,8 +11,8 @@ import net.i2p.I2PAppContext; */ public class LogConsoleBuffer { private I2PAppContext _context; - private final List _buffer; - private final List _critBuffer; + private final List _buffer; + private final List _critBuffer; public LogConsoleBuffer(I2PAppContext context) { _context = context; @@ -43,7 +43,7 @@ public class LogConsoleBuffer { * in the logs) * */ - public List getMostRecentMessages() { + public List getMostRecentMessages() { synchronized (_buffer) { return new ArrayList(_buffer); } @@ -54,9 +54,9 @@ public class LogConsoleBuffer { * in the logs) * */ - public List getMostRecentCriticalMessages() { + public List getMostRecentCriticalMessages() { synchronized (_critBuffer) { return new ArrayList(_critBuffer); } } -} \ No newline at end of file +} From 3489512a54d21df354bb23798875a93b14896dc5 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:07:35 +0000 Subject: [PATCH 22/49] client cleanups --- core/java/src/net/i2p/client/HandlerImpl.java | 6 +++--- core/java/src/net/i2p/client/MessageState.java | 6 ------ core/java/src/net/i2p/client/SessionIdleTimer.java | 7 +++---- 3 files changed, 6 insertions(+), 13 deletions(-) diff --git a/core/java/src/net/i2p/client/HandlerImpl.java b/core/java/src/net/i2p/client/HandlerImpl.java index 98d1e76ff..d1b5100e2 100644 --- a/core/java/src/net/i2p/client/HandlerImpl.java +++ b/core/java/src/net/i2p/client/HandlerImpl.java @@ -18,9 +18,9 @@ import net.i2p.util.Log; * @author jrandom */ abstract class HandlerImpl implements I2CPMessageHandler { - protected Log _log; - private int _type; - protected I2PAppContext _context; + protected final Log _log; + private final int _type; + protected final I2PAppContext _context; public HandlerImpl(I2PAppContext context, int type) { _context = context; diff --git a/core/java/src/net/i2p/client/MessageState.java b/core/java/src/net/i2p/client/MessageState.java index 84ec9ad6a..a67b52770 100644 --- a/core/java/src/net/i2p/client/MessageState.java +++ b/core/java/src/net/i2p/client/MessageState.java @@ -37,13 +37,7 @@ class MessageState { _context = ctx; _nonce = nonce; _prefix = prefix + "[" + _stateId + "]: "; - _id = null; _receivedStatus = new HashSet(); - _cancelled = false; - _key = null; - _newKey = null; - _tags = null; - _to = null; _created = ctx.clock().now(); //ctx.statManager().createRateStat("i2cp.checkStatusTime", "how long it takes to go through the states", "i2cp", new long[] { 60*1000 }); } diff --git a/core/java/src/net/i2p/client/SessionIdleTimer.java b/core/java/src/net/i2p/client/SessionIdleTimer.java index dad736d97..91c1c4b0f 100644 --- a/core/java/src/net/i2p/client/SessionIdleTimer.java +++ b/core/java/src/net/i2p/client/SessionIdleTimer.java @@ -18,13 +18,13 @@ import net.i2p.util.SimpleTimer; * * @author zzz */ -public class SessionIdleTimer implements SimpleTimer.TimedEvent { +class SessionIdleTimer implements SimpleTimer.TimedEvent { public static final long MINIMUM_TIME = 5*60*1000; private static final long DEFAULT_REDUCE_TIME = 20*60*1000; private static final long DEFAULT_CLOSE_TIME = 30*60*1000; private final static Log _log = new Log(SessionIdleTimer.class); - private I2PAppContext _context; - private I2PSessionImpl _session; + private final I2PAppContext _context; + private final I2PSessionImpl _session; private boolean _reduceEnabled; private int _reduceQuantity; private long _reduceTime; @@ -36,7 +36,6 @@ public class SessionIdleTimer implements SimpleTimer.TimedEvent { /** * reduce, shutdown, or both must be true */ - /* FIXME Exporting non-public type through public API FIXME */ public SessionIdleTimer(I2PAppContext context, I2PSessionImpl session, boolean reduce, boolean shutdown) { _context = context; _session = session; From 333f80680e9c3c6038554b9a379fbc9e8a88b571 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:11:41 +0000 Subject: [PATCH 23/49] * UDP: - Avoid rare AIOOBE - Comments - Logging cleanup - Comment out a main() --- .../transport/udp/InboundEstablishState.java | 7 +++++++ .../net/i2p/router/transport/udp/UDPPacket.java | 2 ++ .../i2p/router/transport/udp/UDPPacketReader.java | 2 ++ .../net/i2p/router/transport/udp/UDPTransport.java | 14 ++++++++------ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java index 829f62ee0..6bdfa23a5 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundEstablishState.java @@ -218,6 +218,13 @@ public class InboundEstablishState { if (_receivedIdentity == null) _receivedIdentity = new byte[conf.readTotalFragmentNum()][]; int cur = conf.readCurrentFragmentNum(); + if (cur >= _receivedIdentity.length) { + // avoid AIOOBE + // should do more than this, but what? disconnect? + fail(); + packetReceived(); + return; + } if (_receivedIdentity[cur] == null) { byte fragment[] = new byte[conf.readCurrentFragmentSize()]; conf.readFragmentData(fragment, 0); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java index 5dc001e18..588b0ff49 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacket.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacket.java @@ -67,9 +67,11 @@ public class UDPPacket { // various flag fields for use in the data packets public static final byte DATA_FLAG_EXPLICIT_ACK = (byte)(1 << 7); public static final byte DATA_FLAG_ACK_BITFIELDS = (1 << 6); + // unused public static final byte DATA_FLAG_ECN = (1 << 4); public static final byte DATA_FLAG_WANT_ACKS = (1 << 3); public static final byte DATA_FLAG_WANT_REPLY = (1 << 2); + // unused public static final byte DATA_FLAG_EXTENDED = (1 << 1); public static final byte BITFIELD_CONTINUATION = (byte)(1 << 7); diff --git a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java index aaf0f721a..9d2c32939 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPPacketReader.java @@ -756,6 +756,7 @@ public class UDPPacketReader { } +/****** public static void main(String args[]) { I2PAppContext ctx = I2PAppContext.getGlobalContext(); try { @@ -781,4 +782,5 @@ public class UDPPacketReader { } } +*******/ } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index 5570b8389..0617790a3 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -945,7 +945,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority // try to shift 'em around every 10 minutes or so if (_introducersSelectedOn < _context.clock().now() - 10*60*1000) { if (_log.shouldLog(Log.WARN)) - _log.warn("Our introducers are valid, but thy havent changed in a while, so lets rechoose"); + _log.warn("Our introducers are valid, but havent changed in a while, so lets rechoose"); return true; } else { if (_log.shouldLog(Log.INFO)) @@ -954,7 +954,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority } } else { if (_log.shouldLog(Log.INFO)) - _log.info("Our introducers are not valid (" +valid + ")"); + _log.info("Need more introducers (have " +valid + " need " + PUBLIC_RELAY_COUNT + ')'); return true; } } else { @@ -1233,7 +1233,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _introducersSelectedOn = _context.clock().now(); introducersIncluded = true; } else { + // FIXME // maybe we should fail to publish an address at all in this case? + // YES that would be better if (_log.shouldLog(Log.WARN)) _log.warn("Need introducers but we don't know any"); } @@ -1331,13 +1333,13 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority switch (status) { case CommSystemFacade.STATUS_REJECT_UNSOLICITED: case CommSystemFacade.STATUS_DIFFERENT: - if (_log.shouldLog(Log.INFO)) - _log.info("Require introducers, because our status is " + status); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Require introducers, because our status is " + status); return true; default: if (!allowDirectUDP()) { - if (_log.shouldLog(Log.INFO)) - _log.info("Require introducers, because we do not allow direct UDP connections"); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Require introducers, because we do not allow direct UDP connections"); return true; } return false; From f0f1a6f529b505d95eef0b4d30cd654609dc1060 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:15:03 +0000 Subject: [PATCH 24/49] initializer cleanups, reduce rates, javadoc --- .../src/net/i2p/router/client/ClientListenerRunner.java | 2 -- .../src/net/i2p/router/client/ClientManagerFacadeImpl.java | 1 - .../java/src/net/i2p/router/client/LeaseRequestState.java | 1 - .../java/src/net/i2p/router/client/MessageReceivedJob.java | 4 +--- .../java/src/net/i2p/router/client/RequestLeaseSetJob.java | 6 +++--- 5 files changed, 4 insertions(+), 10 deletions(-) diff --git a/router/java/src/net/i2p/router/client/ClientListenerRunner.java b/router/java/src/net/i2p/router/client/ClientListenerRunner.java index 2c96fa487..7a7d448ea 100644 --- a/router/java/src/net/i2p/router/client/ClientListenerRunner.java +++ b/router/java/src/net/i2p/router/client/ClientListenerRunner.java @@ -41,8 +41,6 @@ public class ClientListenerRunner implements Runnable { _log = _context.logManager().getLog(ClientListenerRunner.class); _manager = manager; _port = port; - _running = false; - _listening = false; String val = context.getProperty(BIND_ALL_INTERFACES); _bindAllInterfaces = Boolean.valueOf(val).booleanValue(); diff --git a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java index e90d12f53..066d6cc35 100644 --- a/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java +++ b/router/java/src/net/i2p/router/client/ClientManagerFacadeImpl.java @@ -43,7 +43,6 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade { public ClientManagerFacadeImpl(RouterContext context) { _context = context; - _manager = null; _log.debug("Client manager facade created"); } diff --git a/router/java/src/net/i2p/router/client/LeaseRequestState.java b/router/java/src/net/i2p/router/client/LeaseRequestState.java index b4cf4415a..7e2a248ac 100644 --- a/router/java/src/net/i2p/router/client/LeaseRequestState.java +++ b/router/java/src/net/i2p/router/client/LeaseRequestState.java @@ -33,7 +33,6 @@ class LeaseRequestState { _onFailed = onFailed; _expiration = expiration; _requestedLeaseSet = requested; - _successful = false; } /** created lease set from client */ diff --git a/router/java/src/net/i2p/router/client/MessageReceivedJob.java b/router/java/src/net/i2p/router/client/MessageReceivedJob.java index d0c767828..82744659a 100644 --- a/router/java/src/net/i2p/router/client/MessageReceivedJob.java +++ b/router/java/src/net/i2p/router/client/MessageReceivedJob.java @@ -47,9 +47,6 @@ class MessageReceivedJob extends JobImpl { /** * Deliver notification to the client that the given message is available. - * This is synchronous and returns true if the notification was sent safely, - * otherwise it returns false - * */ public void messageAvailable(MessageId id, long size) { if (_log.shouldLog(Log.DEBUG)) @@ -59,6 +56,7 @@ class MessageReceivedJob extends JobImpl { msg.setMessageId(id.getMessageId()); msg.setSessionId(_runner.getSessionId().getSessionId()); msg.setSize(size); + // has to be >= 0, it is initialized to -1 msg.setNonce(1); msg.setStatus(MessageStatusMessage.STATUS_AVAILABLE); try { diff --git a/router/java/src/net/i2p/router/client/RequestLeaseSetJob.java b/router/java/src/net/i2p/router/client/RequestLeaseSetJob.java index 51d3a304c..516a652d2 100644 --- a/router/java/src/net/i2p/router/client/RequestLeaseSetJob.java +++ b/router/java/src/net/i2p/router/client/RequestLeaseSetJob.java @@ -44,9 +44,9 @@ class RequestLeaseSetJob extends JobImpl { _onCreate = onCreate; _onFail = onFail; _requestState = state; - ctx.statManager().createRateStat("client.requestLeaseSetSuccess", "How frequently the router requests successfully a new leaseSet?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); - ctx.statManager().createRateStat("client.requestLeaseSetTimeout", "How frequently the router requests a new leaseSet but gets no reply?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); - ctx.statManager().createRateStat("client.requestLeaseSetDropped", "How frequently the router requests a new leaseSet but the client drops?", "ClientMessages", new long[] { 10*60*1000, 60*60*1000, 24*60*60*1000 }); + ctx.statManager().createRateStat("client.requestLeaseSetSuccess", "How frequently the router requests successfully a new leaseSet?", "ClientMessages", new long[] { 60*60*1000 }); + ctx.statManager().createRateStat("client.requestLeaseSetTimeout", "How frequently the router requests a new leaseSet but gets no reply?", "ClientMessages", new long[] { 60*60*1000 }); + ctx.statManager().createRateStat("client.requestLeaseSetDropped", "How frequently the router requests a new leaseSet but the client drops?", "ClientMessages", new long[] { 60*60*1000 }); } public String getName() { return "Request Lease Set"; } From b83184e895edef44085879ef87ef436e10fbdf19 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:16:07 +0000 Subject: [PATCH 25/49] initializer cleanup --- router/java/src/net/i2p/router/message/GarlicConfig.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/router/java/src/net/i2p/router/message/GarlicConfig.java b/router/java/src/net/i2p/router/message/GarlicConfig.java index 1c616f43c..b23d4ac9a 100644 --- a/router/java/src/net/i2p/router/message/GarlicConfig.java +++ b/router/java/src/net/i2p/router/message/GarlicConfig.java @@ -37,17 +37,9 @@ public class GarlicConfig { private long _replyBlockExpiration; public GarlicConfig() { - _recipient = null; - _recipientPublicKey = null; - _cert = null; _id = -1; _expiration = -1; _cloveConfigs = new ArrayList(); - _instructions = null; - _requestAck = false; - _replyThroughRouter = null; - _replyInstructions = null; - _replyBlockCertificate = null; _replyBlockMessageId = -1; _replyBlockExpiration = -1; } From ec0c678cc9be0b9ae42aae410e85b78a40a2d62d Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:43:56 +0000 Subject: [PATCH 26/49] * i2psnark: - Add link to finished torrent in message box - Don't let one bad torrent prevent others from starting or stopping --- .../java/src/org/klomp/snark/Snark.java | 2 +- .../src/org/klomp/snark/SnarkManager.java | 31 +++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/Snark.java b/apps/i2psnark/java/src/org/klomp/snark/Snark.java index c2afec346..9d6cd9460 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Snark.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Snark.java @@ -437,7 +437,7 @@ public class Snark try { storage.close(); } catch (IOException ioee) { ioee.printStackTrace(); } - fatal("Could not create storage", ioe); + fatal("Could not check or create storage", ioe); } } diff --git a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java index 59ab86a12..1ddaa6b03 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java +++ b/apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java @@ -439,7 +439,10 @@ public class SnarkManager implements Snark.CompleteListener { return null; } + /** @throws RuntimeException via Snark.fatal() */ public void addTorrent(String filename) { addTorrent(filename, false); } + + /** @throws RuntimeException via Snark.fatal() */ public void addTorrent(String filename, boolean dontAutoStart) { if ((!dontAutoStart) && !_util.connected()) { addMessage(_("Connecting to I2P")); @@ -724,9 +727,13 @@ public class SnarkManager implements Snark.CompleteListener { /** two listeners */ public void torrentComplete(Snark snark) { - File f = new File(snark.torrent); + StringBuilder buf = new StringBuilder(256); + buf.append("").append(snark.storage.getBaseName()).append(""); long len = snark.meta.getTotalLength(); - addMessage(_("Download finished: \"{0}\"", f.getName()) + " (" + _("size: {0}B", DataHelper.formatSize2(len)) + ')'); + addMessage(_("Download finished: {0}", buf.toString()) + " (" + _("size: {0}B", DataHelper.formatSize2(len)) + ')'); updateStatus(snark); } @@ -736,7 +743,7 @@ public class SnarkManager implements Snark.CompleteListener { private void monitorTorrents(File dir) { String fileNames[] = dir.list(TorrentFilenameFilter.instance()); - List foundNames = new ArrayList(0); + List foundNames = new ArrayList(0); if (fileNames != null) { for (int i = 0; i < fileNames.length; i++) { try { @@ -747,7 +754,7 @@ public class SnarkManager implements Snark.CompleteListener { } } - Set existingNames = listTorrentFiles(); + Set existingNames = listTorrentFiles(); // lets find new ones first... for (int i = 0; i < foundNames.size(); i++) { if (existingNames.contains(foundNames.get(i))) { @@ -755,7 +762,13 @@ public class SnarkManager implements Snark.CompleteListener { } else { if (shouldAutoStart() && !_util.connect()) addMessage(_("Unable to connect to I2P!")); - addTorrent((String)foundNames.get(i), !shouldAutoStart()); + try { + // Snark.fatal() throws a RuntimeException + // don't let one bad torrent kill the whole loop + addTorrent(foundNames.get(i), !shouldAutoStart()); + } catch (Exception e) { + addMessage(_("Unable to add {0}", foundNames.get(i)) + ": " + e); + } } } // now lets see which ones have been removed... @@ -765,7 +778,13 @@ public class SnarkManager implements Snark.CompleteListener { // known and still there. noop } else { // known, but removed. drop it - stopTorrent(name, true); + try { + // Snark.fatal() throws a RuntimeException + // don't let one bad torrent kill the whole loop + stopTorrent(name, true); + } catch (Exception e) { + // don't bother with message + } } } } From 53847dc3ad7dc4bb8222679573bc5a06a64823ee Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:51:48 +0000 Subject: [PATCH 27/49] Sort RouterAddress options on peers.jsp and netdb.jsp --- .../src/net/i2p/router/web/NetDbRenderer.java | 11 +++++-- core/java/src/net/i2p/data/RouterAddress.java | 32 +++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java index 244cc13a0..a2c5632fa 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java @@ -19,6 +19,8 @@ import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.TreeSet; @@ -34,6 +36,7 @@ import net.i2p.router.TunnelPoolSettings; import net.i2p.router.networkdb.kademlia.HashDistance; // debug import net.i2p.util.HexDump; // debug import net.i2p.util.ObjectCounter; +import net.i2p.util.OrderedProperties; import net.i2p.util.VersionComparator; public class NetDbRenderer { @@ -371,9 +374,11 @@ public class NetDbRenderer { int cost = addr.getCost(); if (!((style.equals("SSU") && cost == 5) || (style.equals("NTCP") && cost == 10))) buf.append('[').append(_("cost")).append('=').append("" + cost).append("] "); - for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) { - String name = (String)optIter.next(); - String val = addr.getOptions().getProperty(name); + Properties p = new OrderedProperties(); + p.putAll(addr.getOptions()); + for (Map.Entry e : p.entrySet()) { + String name = (String) e.getKey(); + String val = (String) e.getValue(); buf.append('[').append(_(DataHelper.stripHTML(name))).append('=').append(DataHelper.stripHTML(val)).append("] "); } } diff --git a/core/java/src/net/i2p/data/RouterAddress.java b/core/java/src/net/i2p/data/RouterAddress.java index 9d199e574..1b4d6438f 100644 --- a/core/java/src/net/i2p/data/RouterAddress.java +++ b/core/java/src/net/i2p/data/RouterAddress.java @@ -14,8 +14,11 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.Date; import java.util.Iterator; +import java.util.Map; import java.util.Properties; +import net.i2p.util.OrderedProperties; + /** * Defines a method of communicating with a router * @@ -28,7 +31,7 @@ public class RouterAddress extends DataStructureImpl { private Properties _options; public RouterAddress() { - setCost(-1); + _cost = -1; } /** @@ -134,18 +137,27 @@ public class RouterAddress extends DataStructureImpl { return DataHelper.hashCode(_transportStyle); } + /** + * This is used on peers.jsp so sort options so it looks better. + * We don't just use OrderedProperties for _options because DataHelper.writeProperties() + * sorts also. + */ @Override public String toString() { - StringBuilder buf = new StringBuilder(64); + StringBuilder buf = new StringBuilder(128); buf.append("[RouterAddress: "); - buf.append("\n\tTransportStyle: ").append(getTransportStyle()); - buf.append("\n\tCost: ").append(getCost()); - buf.append("\n\tExpiration: ").append(getExpiration()); - buf.append("\n\tOptions: #: ").append(getOptions().size()); - for (Iterator iter = getOptions().keySet().iterator(); iter.hasNext();) { - String key = (String) iter.next(); - String val = getOptions().getProperty(key); - buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]"); + buf.append("\n\tTransportStyle: ").append(_transportStyle); + buf.append("\n\tCost: ").append(_cost); + buf.append("\n\tExpiration: ").append(_expiration); + if (_options != null) { + buf.append("\n\tOptions: #: ").append(_options.size()); + Properties p = new OrderedProperties(); + p.putAll(_options); + for (Map.Entry e : p.entrySet()) { + String key = (String) e.getKey(); + String val = (String) e.getValue(); + buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]"); + } } buf.append("]"); return buf.toString(); From b1fd835f56d59e4bcb3445eedf8bdcacf221cef9 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 15:55:46 +0000 Subject: [PATCH 28/49] update irc description --- installer/resources/i2ptunnel.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installer/resources/i2ptunnel.config b/installer/resources/i2ptunnel.config index 6f97a1170..ecb45b206 100644 --- a/installer/resources/i2ptunnel.config +++ b/installer/resources/i2ptunnel.config @@ -25,7 +25,7 @@ tunnel.0.startOnLoad=true # irc tunnel.1.name=IRC Proxy -tunnel.1.description=IRC proxy to access the anonymous IRC network +tunnel.1.description=IRC proxy to access anonymous IRC servers tunnel.1.type=ircclient tunnel.1.sharedClient=false tunnel.1.interface=127.0.0.1 From a687180d98468e2ca5753c54a56c250974d4ce82 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 2 Oct 2010 16:30:07 +0000 Subject: [PATCH 29/49] * TransportManager: Convert _transports from a List to a CHM to prevent a rare concurrent exception --- .../router/transport/TransportManager.java | 89 +++++++++---------- 1 file changed, 40 insertions(+), 49 deletions(-) diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index 87a8bc8b2..243c76237 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -21,6 +21,7 @@ import java.util.Properties; import java.util.Set; import java.util.TreeMap; import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; import net.i2p.data.Hash; import net.i2p.data.RouterAddress; @@ -36,7 +37,11 @@ import net.i2p.util.Translate; public class TransportManager implements TransportEventListener { private Log _log; - private List _transports; + /** + * Converted from List to prevent concurrent modification exceptions. + * If we want more than one transport with the same style we will have to change this. + */ + private Map _transports; private RouterContext _context; private UPnPManager _upnpManager; @@ -56,20 +61,20 @@ public class TransportManager implements TransportEventListener { _context.statManager().createRateStat("transport.bidFailSelf", "Could not attempt to bid on message, as it targeted ourselves", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("transport.bidFailNoTransports", "Could not attempt to bid on message, as none of the transports could attempt it", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); _context.statManager().createRateStat("transport.bidFailAllTransports", "Could not attempt to bid on message, as all of the transports had failed", "Transport", new long[] { 60*1000, 10*60*1000, 60*60*1000 }); - _transports = new ArrayList(); + _transports = new ConcurrentHashMap(2); if (Boolean.valueOf(_context.getProperty(PROP_ENABLE_UPNP, "true")).booleanValue()) _upnpManager = new UPnPManager(context, this); } public void addTransport(Transport transport) { if (transport == null) return; - _transports.add(transport); + _transports.put(transport.getStyle(), transport); transport.setListener(this); } public void removeTransport(Transport transport) { if (transport == null) return; - _transports.remove(transport); + _transports.remove(transport.getStyle()); transport.setListener(null); } @@ -140,11 +145,10 @@ public class TransportManager implements TransportEventListener { _upnpManager.start(); configTransports(); _log.debug("Starting up the transport manager"); - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); + for (Transport t : _transports.values()) { RouterAddress addr = t.startListening(); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Transport " + i + " (" + t.getStyle() + ") started"); + _log.debug("Transport " + t.getStyle() + " started"); } // kick UPnP - Do this to get the ports opened even before UDP registers an address transportAddressChanged(); @@ -161,19 +165,14 @@ public class TransportManager implements TransportEventListener { public void stopListening() { if (_upnpManager != null) _upnpManager.stop(); - for (int i = 0; i < _transports.size(); i++) { - _transports.get(i).stopListening(); + for (Transport t : _transports.values()) { + t.stopListening(); } _transports.clear(); } public Transport getTransport(String style) { - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); - if(style.equals(t.getStyle())) - return t; - } - return null; + return _transports.get(style); } int getTransportCount() { return _transports.size(); } @@ -189,16 +188,16 @@ public class TransportManager implements TransportEventListener { public int countActivePeers() { int peers = 0; - for (int i = 0; i < _transports.size(); i++) { - peers += _transports.get(i).countActivePeers(); + for (Transport t : _transports.values()) { + peers += t.countActivePeers(); } return peers; } public int countActiveSendPeers() { int peers = 0; - for (int i = 0; i < _transports.size(); i++) { - peers += _transports.get(i).countActiveSendPeers(); + for (Transport t : _transports.values()) { + peers += t.countActiveSendPeers(); } return peers; } @@ -210,8 +209,8 @@ public class TransportManager implements TransportEventListener { * @param pct percent of limit 0-100 */ public boolean haveOutboundCapacity(int pct) { - for (int i = 0; i < _transports.size(); i++) { - if (_transports.get(i).haveCapacity(pct)) + for (Transport t : _transports.values()) { + if (t.haveCapacity(pct)) return true; } return false; @@ -225,8 +224,8 @@ public class TransportManager implements TransportEventListener { public boolean haveHighOutboundCapacity() { if (_transports.isEmpty()) return false; - for (int i = 0; i < _transports.size(); i++) { - if (!_transports.get(i).haveCapacity(HIGH_CAPACITY_PCT)) + for (Transport t : _transports.values()) { + if (!t.haveCapacity(HIGH_CAPACITY_PCT)) return false; } return true; @@ -239,8 +238,8 @@ public class TransportManager implements TransportEventListener { * @param pct percent of limit 0-100 */ public boolean haveInboundCapacity(int pct) { - for (int i = 0; i < _transports.size(); i++) { - if (_transports.get(i).getCurrentAddress() != null && _transports.get(i).haveCapacity(pct)) + for (Transport t : _transports.values()) { + if (t.getCurrentAddress() != null && t.haveCapacity(pct)) return true; } return false; @@ -253,8 +252,8 @@ public class TransportManager implements TransportEventListener { */ public Vector getClockSkews() { Vector skews = new Vector(); - for (int i = 0; i < _transports.size(); i++) { - Vector tempSkews = _transports.get(i).getClockSkews(); + for (Transport t : _transports.values()) { + Vector tempSkews = t.getClockSkews(); if ((tempSkews == null) || (tempSkews.isEmpty())) continue; skews.addAll(tempSkews); } @@ -266,7 +265,7 @@ public class TransportManager implements TransportEventListener { /** @return the best status of any transport */ public short getReachabilityStatus() { short rv = CommSystemFacade.STATUS_UNKNOWN; - for (Transport t : _transports) { + for (Transport t : _transports.values()) { short s = t.getReachabilityStatus(); if (s < rv) rv = s; @@ -275,13 +274,12 @@ public class TransportManager implements TransportEventListener { } public void recheckReachability() { - for (int i = 0; i < _transports.size(); i++) - _transports.get(i).recheckReachability(); + for (Transport t : _transports.values()) + t.recheckReachability(); } public boolean isBacklogged(Hash dest) { - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); + for (Transport t : _transports.values()) { if (t.isBacklogged(dest)) return true; } @@ -289,8 +287,7 @@ public class TransportManager implements TransportEventListener { } public boolean isEstablished(Hash dest) { - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); + for (Transport t : _transports.values()) { if (t.isEstablished(dest)) return true; } @@ -303,8 +300,7 @@ public class TransportManager implements TransportEventListener { * This is NOT reset if the peer contacts us. */ public boolean wasUnreachable(Hash dest) { - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); + for (Transport t : _transports.values()) { if (!t.wasUnreachable(dest)) return false; } @@ -330,9 +326,9 @@ public class TransportManager implements TransportEventListener { public Map getAddresses() { Map rv = new HashMap(_transports.size()); // do this first since SSU may force a NTCP change - for (Transport t : _transports) + for (Transport t : _transports.values()) t.updateAddress(); - for (Transport t : _transports) { + for (Transport t : _transports.values()) { if (t.getCurrentAddress() != null) rv.put(t.getStyle(), t.getCurrentAddress()); } @@ -345,7 +341,7 @@ public class TransportManager implements TransportEventListener { */ private Map getPorts() { Map rv = new HashMap(_transports.size()); - for (Transport t : _transports) { + for (Transport t : _transports.values()) { int port = t.getRequestedPort(); if (t.getCurrentAddress() != null) { Properties opts = t.getCurrentAddress().getOptions(); @@ -386,8 +382,7 @@ public class TransportManager implements TransportEventListener { List rv = new ArrayList(_transports.size()); Set failedTransports = msg.getFailedTransports(); - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); + for (Transport t : _transports.values()) { if (failedTransports.contains(t.getStyle())) { if (_log.shouldLog(Log.DEBUG)) _log.debug("Skipping transport " + t.getStyle() + " as it already failed"); @@ -415,8 +410,7 @@ public class TransportManager implements TransportEventListener { Hash peer = msg.getTarget().getIdentity().calculateHash(); Set failedTransports = msg.getFailedTransports(); TransportBid rv = null; - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); + for (Transport t : _transports.values()) { if (t.isUnreachable(peer)) { unreachableTransports++; // this keeps GetBids() from shitlisting for "no common transports" @@ -482,8 +476,7 @@ public class TransportManager implements TransportEventListener { public List getMostRecentErrorMessages() { List rv = new ArrayList(16); - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); + for (Transport t : _transports.values()) { rv.addAll(t.getMostRecentErrorMessages()); } return rv; @@ -491,8 +484,7 @@ public class TransportManager implements TransportEventListener { public void renderStatusHTML(Writer out, String urlBase, int sortFlags) throws IOException { TreeMap transports = new TreeMap(); - for (int i = 0; i < _transports.size(); i++) { - Transport t = _transports.get(i); + for (Transport t : _transports.values()) { transports.put(t.getStyle(), t); } for (Iterator iter = transports.values().iterator(); iter.hasNext(); ) { @@ -506,8 +498,7 @@ public class TransportManager implements TransportEventListener { StringBuilder buf = new StringBuilder(4*1024); buf.append("

    ").append(_("Router Transport Addresses")).append("

    \n");
    -        for (int i = 0; i < _transports.size(); i++) {
    -            Transport t = _transports.get(i);
    +        for (Transport t : _transports.values()) {
                 if (t.getCurrentAddress() != null)
                     buf.append(t.getCurrentAddress());
                 else
    
    From 3b2aa946afabd17e1d86529e32ebd40ecd746179 Mon Sep 17 00:00:00 2001
    From: zzz 
    Date: Sat, 2 Oct 2010 16:56:02 +0000
    Subject: [PATCH 30/49]     * I2CP:       - Add new option
     i2cp.messageReliability=none, which prevents the         router from sending
     MessageStatusMessages back in reply to an         outbound
     SendMessageMessage. Since the streaming lib always ignored         the MSMs
     anyway, make it the default for streaming.         This will reduce the I2CP
     traffic significantly.         MSM handling now avoided, but it is still
     fairly broken, see         comments in I2PSessionImpl2.       - Cleanups,
     javadoc, rate reduction
    
    ---
     .../streaming/I2PSocketManagerFactory.java    | 15 ++--
     core/java/src/net/i2p/client/I2PClient.java   |  4 +-
     .../src/net/i2p/client/I2PSessionImpl2.java   | 79 ++++++++++++++-----
     .../router/client/ClientConnectionRunner.java | 32 ++++++--
     4 files changed, 99 insertions(+), 31 deletions(-)
    
    diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
    index 393cf1ae2..34dc8ac59 100644
    --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
    +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java
    @@ -130,16 +130,19 @@ public class I2PSocketManagerFactory {
                 if (!opts.containsKey(name))
                     opts.setProperty(name, System.getProperty(name));
             }
    -        boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
    -        if (oldLib && false) {
    +        //boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
    +        //if (oldLib && false) {
                 // for the old streaming lib
    -            opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
    +        //    opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
                 //opts.setProperty("tunnels.depthInbound", "0");
    -        } else {
    +        //} else {
                 // for new streaming lib:
    -            opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
    +            //opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
    +            // as of 0.8.1 (I2CP default is BestEffort)
    +            if (!opts.containsKey(I2PClient.PROP_RELIABILITY))
    +                opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
                 //p.setProperty("tunnels.depthInbound", "0");
    -        }
    +        //}
     
             if (i2cpHost != null)
                 opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
    diff --git a/core/java/src/net/i2p/client/I2PClient.java b/core/java/src/net/i2p/client/I2PClient.java
    index 9a732c3db..941549d53 100644
    --- a/core/java/src/net/i2p/client/I2PClient.java
    +++ b/core/java/src/net/i2p/client/I2PClient.java
    @@ -34,6 +34,8 @@ public interface I2PClient {
         public final static String PROP_RELIABILITY_BEST_EFFORT = "BestEffort";
         /** Reliability value: guaranteed */
         public final static String PROP_RELIABILITY_GUARANTEED = "Guaranteed";
    +    /** @since 0.8.1 */
    +    public final static String PROP_RELIABILITY_NONE = "none";
     
         /** protocol flag that must be sent when opening the i2cp connection to the router */
         public final static int PROTOCOL_BYTE = 0x2A;
    @@ -64,4 +66,4 @@ public interface I2PClient {
          * @return newly created destination
          */
         public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException;
    -}
    \ No newline at end of file
    +}
    diff --git a/core/java/src/net/i2p/client/I2PSessionImpl2.java b/core/java/src/net/i2p/client/I2PSessionImpl2.java
    index d8d8c8dfc..62b0d3d08 100644
    --- a/core/java/src/net/i2p/client/I2PSessionImpl2.java
    +++ b/core/java/src/net/i2p/client/I2PSessionImpl2.java
    @@ -33,12 +33,14 @@ import net.i2p.util.Log;
     class I2PSessionImpl2 extends I2PSessionImpl {
     
         /** set of MessageState objects, representing all of the messages in the process of being sent */
    -    private /* FIXME final FIXME */ Set _sendingStates;
    +    private /* FIXME final FIXME */ Set _sendingStates;
         /** max # seconds to wait for confirmation of the message send */
         private final static long SEND_TIMEOUT = 60 * 1000; // 60 seconds to send 
         /** should we gzip each payload prior to sending it? */
         private final static boolean SHOULD_COMPRESS = true;
         private final static boolean SHOULD_DECOMPRESS = true;
    +    /** Don't expect any MSMs from the router for outbound traffic @since 0.8.1 */
    +    private boolean _noEffort;
     
         /** for extension */
         public I2PSessionImpl2() {}
    @@ -53,6 +55,8 @@ class I2PSessionImpl2 extends I2PSessionImpl {
             super(ctx, destKeyStream, options);
             _log = ctx.logManager().getLog(I2PSessionImpl2.class);
             _sendingStates = new HashSet(32);
    +        // default is BestEffort
    +        _noEffort = "none".equalsIgnoreCase(options.getProperty(I2PClient.PROP_RELIABILITY));
     
             ctx.statManager().createRateStat("i2cp.sendBestEffortTotalTime", "how long to do the full sendBestEffort call?", "i2cp", new long[] { 10*60*1000 } );
             //ctx.statManager().createRateStat("i2cp.sendBestEffortStage0", "first part of sendBestEffort?", "i2cp", new long[] { 10*60*1000 } );
    @@ -60,15 +64,16 @@ class I2PSessionImpl2 extends I2PSessionImpl {
             //ctx.statManager().createRateStat("i2cp.sendBestEffortStage2", "third part of sendBestEffort?", "i2cp", new long[] { 10*60*1000 } );
             //ctx.statManager().createRateStat("i2cp.sendBestEffortStage3", "fourth part of sendBestEffort?", "i2cp", new long[] { 10*60*1000 } );
             //ctx.statManager().createRateStat("i2cp.sendBestEffortStage4", "fifth part of sendBestEffort?", "i2cp", new long[] { 10*60*1000 } );
    -        _context.statManager().createRateStat("i2cp.receiveStatusTime.0", "How long it took to get status=0 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    -        _context.statManager().createRateStat("i2cp.receiveStatusTime.1", "How long it took to get status=1 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    -        _context.statManager().createRateStat("i2cp.receiveStatusTime.2", "How long it took to get status=2 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    -        _context.statManager().createRateStat("i2cp.receiveStatusTime.3", "How long it took to get status=3 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    -        _context.statManager().createRateStat("i2cp.receiveStatusTime.4", "How long it took to get status=4 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    -        _context.statManager().createRateStat("i2cp.receiveStatusTime.5", "How long it took to get status=5 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    -        _context.statManager().createRateStat("i2cp.receiveStatusTime", "How long it took to get any status", "i2cp", new long[] { 60*1000, 10*60*1000 });
    -        _context.statManager().createRateStat("i2cp.tx.msgCompressed", "compressed size transferred", "i2cp", new long[] { 60*1000, 30*60*1000 });
    -        _context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 60*1000, 30*60*1000 });
    +        //_context.statManager().createRateStat("i2cp.receiveStatusTime.0", "How long it took to get status=0 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    +        _context.statManager().createRateStat("i2cp.receiveStatusTime.1", "How long it took to get status=1 back", "i2cp", new long[] { 10*60*1000 });
    +        // best effort codes unused
    +        //_context.statManager().createRateStat("i2cp.receiveStatusTime.2", "How long it took to get status=2 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    +        //_context.statManager().createRateStat("i2cp.receiveStatusTime.3", "How long it took to get status=3 back", "i2cp", new long[] { 60*1000, 10*60*1000 });
    +        _context.statManager().createRateStat("i2cp.receiveStatusTime.4", "How long it took to get status=4 back", "i2cp", new long[] { 10*60*1000 });
    +        _context.statManager().createRateStat("i2cp.receiveStatusTime.5", "How long it took to get status=5 back", "i2cp", new long[] { 10*60*1000 });
    +        _context.statManager().createRateStat("i2cp.receiveStatusTime", "How long it took to get any status", "i2cp", new long[] { 10*60*1000 });
    +        _context.statManager().createRateStat("i2cp.tx.msgCompressed", "compressed size transferred", "i2cp", new long[] { 30*60*1000 });
    +        _context.statManager().createRateStat("i2cp.tx.msgExpanded", "size before compression", "i2cp", new long[] { 30*60*1000 });
         }
     
         protected long getTimeout() {
    @@ -186,7 +191,10 @@ class I2PSessionImpl2 extends I2PSessionImpl {
             }
             _context.statManager().addRateData("i2cp.tx.msgCompressed", compressed, 0);
             _context.statManager().addRateData("i2cp.tx.msgExpanded", size, 0);
    -        return sendBestEffort(dest, payload, keyUsed, tagsSent, expires);
    +        if (_noEffort)
    +            return sendNoEffort(dest, payload, expires);
    +        else
    +            return sendBestEffort(dest, payload, keyUsed, tagsSent, expires);
         }
     
         /**
    @@ -213,6 +221,9 @@ class I2PSessionImpl2 extends I2PSessionImpl {
         private static final int NUM_TAGS = 50;
     
         /**
    +     * TODO - Don't need to save MessageState since actuallyWait is false...
    +     * But for now just use sendNoEffort() instead.
    +     *
          * @param keyUsed unused - no end-to-end crypto
          * @param tagsSent unused - no end-to-end crypto
          */
    @@ -257,7 +268,7 @@ class I2PSessionImpl2 extends I2PSessionImpl {
                                    + "ms left, " + oldTags + " tags known and " 
                                    + (tag == null ? "no tag" : " a valid tag"));
                 }
    -        
    +
                 if (false) // rekey
                     newKey = _context.keyGenerator().generateSessionKey();
             
    @@ -371,6 +382,37 @@ class I2PSessionImpl2 extends I2PSessionImpl {
             return found;
         }
         
    +    /**
    +     * Same as sendBestEffort(), except we do not expect any MessageStatusMessage responses -
    +     * not for accepted, or success, or failure.
    +     * So we don't create a MessageState and save it on the _sendingStates HashSet
    +     *
    +     * @return true always
    +     * @since 0.8.1
    +     */
    +    protected boolean sendNoEffort(Destination dest, byte payload[], long expires)
    +                    throws I2PSessionException {
    +        // nonce always 0
    +        _producer.sendMessage(this, dest, 0, payload, null, null, null, null, expires);
    +        return true;
    +    }
    +    
    +    /**
    +     *  Only call this with nonzero status, i.e. for outbound messages
    +     *  whose MessageState may be queued on _sendingStates.
    +     *
    +     *  Even when using sendBestEffort(), this is a waste, because the
    +     *  MessageState is removed from _sendingStates immediately and
    +     *  so the lookup here fails.
    +     *  And iterating through the HashSet instead of having a map
    +     *  is bad too.
    +     *
    +     *  This is now pretty much avoided since streaming now sets
    +     *  i2cp.messageReliability = none, which forces sendNoEffort() instead of sendBestEffort(),
    +     *  so the router won't send us any MSM's for outbound traffic.
    +     *
    +     *  @param status != 0
    +     */
         @Override
         public void receiveStatus(int msgId, long nonce, int status) {
             if (_log.shouldLog(Log.DEBUG)) _log.debug(getPrefix() + "Received status " + status + " for msgId " + msgId + " / " + nonce);
    @@ -413,12 +455,13 @@ class I2PSessionImpl2 extends I2PSessionImpl {
                     case 1:
                         _context.statManager().addRateData("i2cp.receiveStatusTime.1", lifetime, 0);
                         break;
    -                case 2:
    -                    _context.statManager().addRateData("i2cp.receiveStatusTime.2", lifetime, 0);
    -                    break;
    -                case 3:
    -                    _context.statManager().addRateData("i2cp.receiveStatusTime.3", lifetime, 0);
    -                    break;
    +                // best effort codes unused
    +                //case 2:
    +                //    _context.statManager().addRateData("i2cp.receiveStatusTime.2", lifetime, 0);
    +                //    break;
    +                //case 3:
    +                //    _context.statManager().addRateData("i2cp.receiveStatusTime.3", lifetime, 0);
    +                //    break;
                     case 4:
                         _context.statManager().addRateData("i2cp.receiveStatusTime.4", lifetime, 0);
                         break;
    diff --git a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
    index e5aa1b5ab..f363bd278 100644
    --- a/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
    +++ b/router/java/src/net/i2p/router/client/ClientConnectionRunner.java
    @@ -17,6 +17,7 @@ import java.util.List;
     import java.util.Map;
     import java.util.Set;
     
    +import net.i2p.client.I2PClient;
     import net.i2p.crypto.SessionKeyManager;
     import net.i2p.crypto.TransientSessionKeyManager;
     import net.i2p.data.Destination;
    @@ -76,11 +77,13 @@ public class ClientConnectionRunner {
          * This contains the last 10 MessageIds that have had their (non-ack) status 
          * delivered to the client (so that we can be sure only to update when necessary)
          */
    -    private final List _alreadyProcessed;
    +    private final List _alreadyProcessed;
         private ClientWriterRunner _writer;
         private Hash _destHashCache;
         /** are we, uh, dead */
         private boolean _dead;
    +    /** For outbound traffic. true if i2cp.messageReliability = "none"; @since 0.8.1 */
    +    private boolean _dontSendMSM;
         
         /**
          * Create a new runner against the given socket
    @@ -91,11 +94,9 @@ public class ClientConnectionRunner {
             _log = _context.logManager().getLog(ClientConnectionRunner.class);
             _manager = manager;
             _socket = socket;
    -        _config = null;
             _messages = new ConcurrentHashMap();
             _alreadyProcessed = new ArrayList();
             _acceptedPending = new ConcurrentHashSet();
    -        _dead = false;
         }
         
         private static volatile int __id = 0;
    @@ -189,6 +190,9 @@ public class ClientConnectionRunner {
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("SessionEstablished called for destination " + _destHashCache.toBase64());
             _config = config;
    +        // This is the only option that is interpreted here, not at the tunnel manager
    +        if (config.getOptions() != null)
    +            _dontSendMSM = "none".equalsIgnoreCase(config.getOptions().getProperty(I2PClient.PROP_RELIABILITY));
             // per-destination session key manager to prevent rather easy correlation
             if (_sessionKeyManager == null)
                 _sessionKeyManager = new TransientSessionKeyManager(_context);
    @@ -197,10 +201,18 @@ public class ClientConnectionRunner {
             _manager.destinationEstablished(this);
         }
         
    +    /** 
    +     * Send a notification to the client that their message (id specified) was
    +     * delivered (or failed delivery)
    +     * Note that this sends the Guaranteed status codes, even though we only support best effort.
    +     * Doesn't do anything if i2cp.messageReliability = "none"
    +     */
         void updateMessageDeliveryStatus(MessageId id, boolean delivered) {
    -        if (_dead) return;
    +        if (_dead || _dontSendMSM)
    +            return;
             _context.jobQueue().addJob(new MessageDeliveryStatusUpdate(id, delivered));
         }
    +
         /** 
          * called after a new leaseSet is granted by the client, the NetworkDb has been
          * updated.  This takes care of all the LeaseRequestState stuff (including firing any jobs)
    @@ -254,7 +266,8 @@ public class ClientConnectionRunner {
             long expiration = 0;
             if (message instanceof SendMessageExpiresMessage)
                 expiration = ((SendMessageExpiresMessage) message).getExpiration().getTime();
    -        _acceptedPending.add(id);
    +        if (!_dontSendMSM)
    +            _acceptedPending.add(id);
     
             if (_log.shouldLog(Log.DEBUG))
                 _log.debug("** Receiving message [" + id.getMessageId() + "] with payload of size [" 
    @@ -276,9 +289,11 @@ public class ClientConnectionRunner {
         /** 
          * Send a notification to the client that their message (id specified) was accepted 
          * for delivery (but not necessarily delivered)
    -     *
    +     * Doesn't do anything if i2cp.messageReliability = "none"
          */
         void ackSendMessage(MessageId id, long nonce) {
    +        if (_dontSendMSM)
    +            return;
             SessionId sid = _sessionId;
             if (sid == null) return;
             if (_log.shouldLog(Log.DEBUG))
    @@ -517,12 +532,17 @@ public class ClientConnectionRunner {
             }
     
             public String getName() { return "Update Delivery Status"; }
    +
    +        /**
    +         * Note that this sends the Guaranteed status codes, even though we only support best effort.
    +         */
             public void runJob() {
                 if (_dead) return;
     
                 MessageStatusMessage msg = new MessageStatusMessage();
                 msg.setMessageId(_messageId.getMessageId());
                 msg.setSessionId(_sessionId.getSessionId());
    +            // has to be >= 0, it is initialized to -1
                 msg.setNonce(2);
                 msg.setSize(0);
                 if (_success) 
    
    From 042cde2952f97e4066d2ef0e24c9847984a967f9 Mon Sep 17 00:00:00 2001
    From: zzz 
    Date: Sat, 2 Oct 2010 17:04:52 +0000
    Subject: [PATCH 31/49]     * UDP:       - Try to avoid running out of
     introducers by relaxing selection criteria         and increasing minimum
     number of potential introducers
    
    ---
     .../router/transport/udp/IntroductionManager.java | 15 ++++++++++++---
     .../i2p/router/transport/udp/UDPTransport.java    |  2 +-
     2 files changed, 13 insertions(+), 4 deletions(-)
    
    diff --git a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
    index 6a2707f60..6985164cb 100644
    --- a/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
    +++ b/router/java/src/net/i2p/router/transport/udp/IntroductionManager.java
    @@ -96,7 +96,10 @@ public class IntroductionManager {
             int sz = peers.size();
             start = start % sz;
             int found = 0;
    -        long inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 2);
    +        long inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 2);    // 15 min
    +        // if not too many to choose from, be less picky
    +        if (sz <= howMany + 2)
    +            inactivityCutoff -= UDPTransport.EXPIRE_TIMEOUT / 4;
             for (int i = 0; i < sz && found < howMany; i++) {
                 PeerState cur = peers.get((start + i) % sz);
                 RouterInfo ri = _context.netDb().lookupRouterInfoLocally(cur.getRemotePeer());
    @@ -119,7 +122,11 @@ public class IntroductionManager {
                     continue;
                 }
                 // Try to pick active peers...
    -            if (cur.getLastReceiveTime() < inactivityCutoff || cur.getLastSendTime() < inactivityCutoff) {
    +            // FIXME this is really strict and causes us to run out of introducers
    +            // We have much less introducers than we used to have because routers don't offer
    +            // if they are approaching max connections (see EstablishmentManager)
    +            // FIXED, was ||, is this OK now?
    +            if (cur.getLastReceiveTime() < inactivityCutoff && cur.getLastSendTime() < inactivityCutoff) {
                     if (_log.shouldLog(Log.INFO))
                         _log.info("Peer is idle too long: " + cur);
                     continue;
    @@ -135,6 +142,8 @@ public class IntroductionManager {
                 found++;
             }
     
    +        // FIXME failsafe if found == 0, relax inactivityCutoff and try again?
    +
             // Try to keep the connection up for two hours after we made anybody an introducer
             long pingCutoff = _context.clock().now() - (2 * 60 * 60 * 1000);
             inactivityCutoff = _context.clock().now() - (UDPTransport.EXPIRE_TIMEOUT / 4);
    @@ -156,7 +165,7 @@ public class IntroductionManager {
          * Not as elaborate as pickInbound() above.
          * Just a quick check to see how many volunteers we know,
          * which the Transport uses to see if we need more.
    -     * @return number of peers that have volunteerd to introduce us
    +     * @return number of peers that have volunteered to introduce us
          */
         int introducerCount() {
                 return _inbound.size();
    diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
    index 0617790a3..390a741f1 100644
    --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
    +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java
    @@ -1017,7 +1017,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
         /** minimum active peers to maintain IP detection, etc. */
         private static final int MIN_PEERS = 3;
         /** minimum peers volunteering to be introducers if we need that */
    -    private static final int MIN_INTRODUCER_POOL = 4;
    +    private static final int MIN_INTRODUCER_POOL = 5;
     
         public TransportBid bid(RouterInfo toAddress, long dataSize) {
             if (dataSize > OutboundMessageState.MAX_MSG_SIZE) {
    
    From 3d9b6061ce516c63fe0eacbe8570251de3c42084 Mon Sep 17 00:00:00 2001
    From: zzz 
    Date: Sat, 2 Oct 2010 17:07:37 +0000
    Subject: [PATCH 32/49]     * NetDB:       - Expire unreachable routers
     quickly, even if they don't have introducers,         so we don't have old
     data on routers that ran out of introducers.       - Fix rare NPEs at
     shutdown
    
    ---
     .../kademlia/KademliaNetworkDatabaseFacade.java        | 10 ++++++++--
     1 file changed, 8 insertions(+), 2 deletions(-)
    
    diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
    index 688b8546d..acede96ed 100644
    --- a/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
    +++ b/router/java/src/net/i2p/router/networkdb/kademlia/KademliaNetworkDatabaseFacade.java
    @@ -194,10 +194,12 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
         
         public void shutdown() {
             _initialized = false;
    -        _kb = null;
    +        // don't null out _kb, it can cause NPEs in concurrent operations
    +        //_kb = null;
             if (_ds != null)
                 _ds.stop();
    -        _ds = null;
    +        // don't null out _ds, it can cause NPEs in concurrent operations
    +        //_ds = null;
             _exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't.
             // _exploreKeys = null;
         }
    @@ -750,6 +752,10 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
             } else if (upLongEnough && !routerInfo.isCurrent(ROUTER_INFO_EXPIRATION_SHORT)) {
                 if (routerInfo.getAddresses().isEmpty())
                     return "Peer " + key.toBase64() + " published > 90m ago with no addresses";
    +            // This should cover the introducers case below too
    +            // And even better, catches the case where the router is unreachable but knows no introducers
    +            if (routerInfo.getCapabilities().indexOf(Router.CAPABILITY_UNREACHABLE) >= 0)
    +                return "Peer " + key.toBase64() + " published > 90m ago and thinks it is unreachable";
                 RouterAddress ra = routerInfo.getTargetAddress("SSU");
                 if (ra != null) {
                     // Introducers change often, introducee will ping introducer for 2 hours
    
    From 6cf7bc7985dd382728bb6061bbb5ac1827de09f6 Mon Sep 17 00:00:00 2001
    From: zzz 
    Date: Sat, 2 Oct 2010 17:20:39 +0000
    Subject: [PATCH 33/49]     * i2ptunnel:       - Add hostname DSA signature
     field, to be used for addkey forms.         Experimental, may be commented
     out later.
    
    ---
     .../src/net/i2p/i2ptunnel/web/EditBean.java   | 30 +++++++++++++++++++
     apps/i2ptunnel/jsp/editServer.jsp             | 11 ++++++-
     2 files changed, 40 insertions(+), 1 deletion(-)
    
    diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
    index 38bc8b4db..f6c76d7f6 100644
    --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
    +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java
    @@ -13,6 +13,11 @@ import java.util.List;
     import java.util.Properties;
     import java.util.StringTokenizer;
     
    +import net.i2p.data.Base64;
    +import net.i2p.data.Destination;
    +import net.i2p.data.PrivateKeyFile;
    +import net.i2p.data.Signature;
    +import net.i2p.data.SigningPrivateKey;
     import net.i2p.i2ptunnel.TunnelController;
     import net.i2p.i2ptunnel.TunnelControllerGroup;
     
    @@ -68,6 +73,31 @@ public class EditBean extends IndexBean {
             return "i2ptunnel" + tunnel + "-privKeys.dat";
         }
         
    +    public String getNameSignature(int tunnel) {
    +        String spoof = getSpoofedHost(tunnel);
    +        if (spoof.length() <= 0)
    +            return "";
    +        TunnelController tun = getController(tunnel);
    +        if (tun == null)
    +            return "";
    +        String keyFile = tun.getPrivKeyFile();
    +        if (keyFile != null && keyFile.trim().length() > 0) {
    +            PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
    +            try {
    +                Destination d = pkf.getDestination();
    +                if (d == null)
    +                    return "";
    +                SigningPrivateKey privKey = pkf.getSigningPrivKey();
    +                if (privKey == null)
    +                    return "";
    +                //System.err.println("Signing " + spoof + " with " + Base64.encode(privKey.getData()));
    +                Signature sig = _context.dsa().sign(spoof.getBytes("UTF-8"), privKey);
    +                return Base64.encode(sig.getData());
    +            } catch (Exception e) {}
    +        }
    +        return "";
    +    }
    +    
         public boolean startAutomatically(int tunnel) {
             TunnelController tun = getController(tunnel);
             if (tun != null)
    diff --git a/apps/i2ptunnel/jsp/editServer.jsp b/apps/i2ptunnel/jsp/editServer.jsp
    index 939ccf520..97a0a93c9 100644
    --- a/apps/i2ptunnel/jsp/editServer.jsp
    +++ b/apps/i2ptunnel/jsp/editServer.jsp
    @@ -196,7 +196,16 @@
                 <%=intl._("Add to local addressbook")%>    
              <% } %>
                 
    -            
    +
    +            <% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
    +          %>
    + + +
    + <% } %> + From 48ccf85e977dfbc6fc584bc4f342b8c0f5261451 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 5 Oct 2010 00:08:59 +0000 Subject: [PATCH 34/49] try again to fix the i2ptunnel nonce problem --- .../src/net/i2p/i2ptunnel/web/IndexBean.java | 68 +++++++++++-------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index 2a77b9be8..d91a7900d 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -9,6 +9,7 @@ package net.i2p.i2ptunnel.web; */ import java.util.concurrent.ConcurrentHashMap; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -41,10 +42,10 @@ public class IndexBean { protected TunnelControllerGroup _group; private String _action; private int _tunnel; - private long _prevNonce; - private long _prevNonce2; - private long _curNonce; - private long _nextNonce; + //private long _prevNonce; + //private long _prevNonce2; + private String _curNonce; + //private long _nextNonce; private String _type; private String _name; @@ -85,10 +86,14 @@ public class IndexBean { /** deprecated unimplemented, now using routerconsole realm */ //public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase"; public static final String PROP_TUNNEL_PASSPHRASE = "consolePassword"; - static final String PROP_NONCE = IndexBean.class.getName() + ".nonce"; - static final String PROP_NONCE_OLD = PROP_NONCE + '2'; + //static final String PROP_NONCE = IndexBean.class.getName() + ".nonce"; + //static final String PROP_NONCE_OLD = PROP_NONCE + '2'; + /** 3 wasn't enough for some browsers. They are reloading the page for some reason - maybe HEAD? @since 0.8.1 */ + private static final int MAX_NONCES = 5; + /** store nonces in a static FIFO instead of in System Properties @since 0.8.1 */ + private static final List _nonces = new ArrayList(MAX_NONCES + 1); + static final String CLIENT_NICKNAME = "shared clients"; - public static final String PROP_THEME_NAME = "routerconsole.theme"; public static final String DEFAULT_THEME = "light"; public static final String PROP_CSS_DISABLED = "routerconsole.css.disabled"; @@ -98,34 +103,39 @@ public class IndexBean { _context = I2PAppContext.getGlobalContext(); _log = _context.logManager().getLog(IndexBean.class); _group = TunnelControllerGroup.getInstance(); - _action = null; _tunnel = -1; - _curNonce = -1; - _prevNonce = -1; - _prevNonce2 = -1; - try { - String nonce2 = System.getProperty(PROP_NONCE_OLD); - if (nonce2 != null) - _prevNonce2 = Long.parseLong(nonce2); - String nonce = System.getProperty(PROP_NONCE); - if (nonce != null) { - _prevNonce = Long.parseLong(nonce); - System.setProperty(PROP_NONCE_OLD, nonce); - } - } catch (NumberFormatException nfe) {} - _nextNonce = _context.random().nextLong(); - System.setProperty(PROP_NONCE, Long.toString(_nextNonce)); + _curNonce = "-1"; + addNonce(); _booleanOptions = new ConcurrentHashSet(4); _otherOptions = new ConcurrentHashMap(4); } - public long getNextNonce() { return _nextNonce; } + public static String getNextNonce() { + synchronized (_nonces) { + return _nonces.get(0); + } + } + public void setNonce(String nonce) { if ( (nonce == null) || (nonce.trim().length() <= 0) ) return; - try { - _curNonce = Long.parseLong(nonce); - } catch (NumberFormatException nfe) { - _curNonce = -1; + _curNonce = nonce; + } + + /** add a random nonce to the head of the queue @since 0.8.1 */ + private void addNonce() { + String nextNonce = Long.toString(_context.random().nextLong()); + synchronized (_nonces) { + _nonces.add(0, nextNonce); + int sz = _nonces.size(); + if (sz > MAX_NONCES) + _nonces.remove(sz - 1); + } + } + + /** do we know this nonce? @since 0.8.1 */ + private static boolean haveNonce(String nonce) { + synchronized (_nonces) { + return _nonces.contains(nonce); } } @@ -155,7 +165,7 @@ public class IndexBean { private String processAction() { if ( (_action == null) || (_action.trim().length() <= 0) || ("Cancel".equals(_action))) return ""; - if ( (_prevNonce != _curNonce) && (_prevNonce2 != _curNonce) && (!validPassphrase()) ) + if ( (!haveNonce(_curNonce)) && (!validPassphrase()) ) return "Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit."; if ("Stop all".equals(_action)) return stopAll(); From ab91d35331e089e5a997f5f020aa9a4ee80fba9c Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 5 Oct 2010 13:15:39 +0000 Subject: [PATCH 35/49] comment --- router/java/src/net/i2p/router/transport/UPnP.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/router/java/src/net/i2p/router/transport/UPnP.java b/router/java/src/net/i2p/router/transport/UPnP.java index aa2261cfc..d13a51cec 100644 --- a/router/java/src/net/i2p/router/transport/UPnP.java +++ b/router/java/src/net/i2p/router/transport/UPnP.java @@ -581,6 +581,8 @@ public class UPnP extends ControlPoint implements DeviceChangeListener, EventLis // If not in portsForwarded, it wasn't successful, try again if(portsForwarded.contains(port)) { // We have forwarded it, and it should be forwarded, cool. + // Big problem here, if firewall resets, we don't know it. + // Do we need to re-forward anyway? or poll the router? } else { // Needs forwarding if(portsToForwardNow == null) portsToForwardNow = new HashSet(); From 895c9a33a9179bf8b36dd7496248cba3a3c17fa3 Mon Sep 17 00:00:00 2001 From: zzz Date: Tue, 5 Oct 2010 14:52:44 +0000 Subject: [PATCH 36/49] pluck build.xml javadoc changes from main branch --- build.xml | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/build.xml b/build.xml index 9768eadd6..c2d26de43 100644 --- a/build.xml +++ b/build.xml @@ -153,16 +153,20 @@ destdir="./build/javadoc" packagenames="*" use="true" - group="SDK net.i2p:net.i2p.*:net.i2p.client:net.i2p.client.*:freenet.support.CPUInformation, - Streaming net.i2p.client.streaming, - BOB net.i2p.BOB, - SAM net.i2p.sam:net.i2p.sam.client, - RouterConsole net.i2p.router.web, - Router net.i2p.router:net.i2p.router.*:net.i2p.data.i2np, - Systray net.i2p.apps.systray, - I2PTunnel net.i2p.i2ptunnel:net.i2p.i2ptunnel.*" splitindex="true" - windowtitle="I2P"> + windowtitle="I2P Anonymous Network - Java Documentation"> + + + + + + + + + + + + @@ -177,6 +181,8 @@ + + From 09d1eb17d49e15b1aae2becbc9f8d60434496f73 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 6 Oct 2010 01:11:21 +0000 Subject: [PATCH 37/49] reset eepget length variable after a redirect --- core/java/src/net/i2p/util/EepGet.java | 9 +++++++++ core/java/src/net/i2p/util/EepHead.java | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java index 3fec768a5..ace31ab72 100644 --- a/core/java/src/net/i2p/util/EepGet.java +++ b/core/java/src/net/i2p/util/EepGet.java @@ -537,6 +537,15 @@ public class EepGet { if (_redirects > 5) throw new IOException("Too many redirects: to " + _redirectLocation); if (_log.shouldLog(Log.INFO)) _log.info("Redirecting to " + _redirectLocation); + + // reset some important variables, we don't want to save the values from the redirect + _bytesRemaining = -1; + _redirectLocation = null; + _etag = null; + _lastModified = null; + _contentType = null; + _encodingChunked = false; + sendRequest(timeout); doFetch(timeout); return; diff --git a/core/java/src/net/i2p/util/EepHead.java b/core/java/src/net/i2p/util/EepHead.java index 938241616..c3a17bbea 100644 --- a/core/java/src/net/i2p/util/EepHead.java +++ b/core/java/src/net/i2p/util/EepHead.java @@ -116,6 +116,7 @@ public class EepHead extends EepGet { else timeout.setInactivityTimeout(60*1000); + // Should we even follow redirects for HEAD? if (_redirectLocation != null) { //try { URL oldURL = new URL(_actualURL); @@ -143,6 +144,15 @@ public class EepHead extends EepGet { if (_redirects > 5) throw new IOException("Too many redirects: to " + _redirectLocation); if (_log.shouldLog(Log.INFO)) _log.info("Redirecting to " + _redirectLocation); + + // reset some important variables, we don't want to save the values from the redirect + _bytesRemaining = -1; + _redirectLocation = null; + _etag = null; + _lastModified = null; + _contentType = null; + _encodingChunked = false; + sendRequest(timeout); doFetch(timeout); return; From e2b7f93d118aa76d1c6c4bfd48c9b7a38a03479b Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 6 Oct 2010 01:27:15 +0000 Subject: [PATCH 38/49] fixup --- core/java/src/net/i2p/util/EepGet.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/java/src/net/i2p/util/EepGet.java b/core/java/src/net/i2p/util/EepGet.java index ace31ab72..5b3f25c19 100644 --- a/core/java/src/net/i2p/util/EepGet.java +++ b/core/java/src/net/i2p/util/EepGet.java @@ -55,11 +55,11 @@ public class EepGet { protected long _bytesTransferred; protected long _bytesRemaining; protected int _currentAttempt; - private String _etag; - private String _lastModified; + protected String _etag; + protected String _lastModified; protected boolean _encodingChunked; protected boolean _notModified; - private String _contentType; + protected String _contentType; protected boolean _transferFailed; protected boolean _headersRead; protected boolean _aborted; From 5389ee056a46b3bfeb84839277f7d24277e033e2 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 6 Oct 2010 02:05:26 +0000 Subject: [PATCH 39/49] sort snark peers by completion % --- .../org/klomp/snark/web/I2PSnarkServlet.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index ea1325e18..3b7c50702 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -8,6 +8,7 @@ import java.io.PrintWriter; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.Iterator; @@ -821,10 +822,9 @@ public class I2PSnarkServlet extends Default { out.write("\n\n"); if(showPeers && isRunning && curPeers > 0) { - List peers = snark.coordinator.peerList(); - Iterator it = peers.iterator(); - while (it.hasNext()) { - Peer peer = (Peer)it.next(); + List peers = snark.coordinator.peerList(); + Collections.sort(peers, new PeerComparator()); + for (Peer peer : peers) { if (!peer.isConnected()) continue; out.write(""); @@ -908,6 +908,19 @@ public class I2PSnarkServlet extends Default { } } + /** + * Sort by completeness (seeds first), then by ID + * @since 0.8.1 + */ + private static class PeerComparator implements Comparator { + public int compare(Peer l, Peer r) { + int diff = r.completed() - l.completed(); // reverse + if (diff != 0) + return diff; + return l.toString().substring(5, 9).compareTo(r.toString().substring(5, 9)); + } + } + private void writeAddForm(PrintWriter out, HttpServletRequest req) throws IOException { String uri = req.getRequestURI(); String newURL = req.getParameter("newURL"); From 5ef9d46d0bb22efe591dd7f9d3a66a6e3bae5e2c Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 6 Oct 2010 02:18:52 +0000 Subject: [PATCH 40/49] dont sort in debug mode --- .../i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java index 3b7c50702..d3a04df1f 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java @@ -823,7 +823,8 @@ public class I2PSnarkServlet extends Default { if(showPeers && isRunning && curPeers > 0) { List peers = snark.coordinator.peerList(); - Collections.sort(peers, new PeerComparator()); + if (!showDebug) + Collections.sort(peers, new PeerComparator()); for (Peer peer : peers) { if (!peer.isConnected()) continue; From 3841e92d534ff3f19b378438283804b296ff37d8 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 6 Oct 2010 14:25:27 +0000 Subject: [PATCH 41/49] remove unused web-*.xml files from wars --- apps/i2ptunnel/java/build.xml | 2 +- apps/routerconsole/java/build.xml | 2 +- apps/susidns/src/build.xml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/i2ptunnel/java/build.xml b/apps/i2ptunnel/java/build.xml index 7b66b6882..b17e8d9f3 100644 --- a/apps/i2ptunnel/java/build.xml +++ b/apps/i2ptunnel/java/build.xml @@ -81,7 +81,7 @@ + basedir="../jsp/" excludes="web.xml, web-fragment.xml, web-out.xml, **/*.java, *.jsp"> diff --git a/apps/routerconsole/java/build.xml b/apps/routerconsole/java/build.xml index 78384113f..ae73734cd 100644 --- a/apps/routerconsole/java/build.xml +++ b/apps/routerconsole/java/build.xml @@ -113,7 +113,7 @@ + basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, *.jsi, web-fragment.xml, web-out.xml"> diff --git a/apps/susidns/src/build.xml b/apps/susidns/src/build.xml index 55b662a9d..8244d37cf 100644 --- a/apps/susidns/src/build.xml +++ b/apps/susidns/src/build.xml @@ -72,7 +72,6 @@ - From 8ac5d5d5fcebad80ad59bca0ce57bedbe0b63efc Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 6 Oct 2010 15:03:33 +0000 Subject: [PATCH 42/49] add some missing mime types --- apps/i2psnark/web.xml | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/apps/i2psnark/web.xml b/apps/i2psnark/web.xml index 71260245f..d2bc14751 100644 --- a/apps/i2psnark/web.xml +++ b/apps/i2psnark/web.xml @@ -22,4 +22,63 @@ 30 + + + + + + mkv + video/x-matroska + + + + wmv + video/x-ms-wmv + + + + flv + video/x-flv + + + + rar + application/rar + + + + 7z + application/x-7z-compressed + + + + iso + application/x-iso9660-image + + + + ico + image/x-icon + + + + exe + application/x-msdos-program + + + + flac + audio/flac + + + + m4a + audio/mpeg + + + + wma + audio/x-ms-wma + + From 373fce2988771b8d693fb4cea77c5fb785bf2a61 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 7 Oct 2010 15:13:54 +0000 Subject: [PATCH 43/49] change perms on append too --- core/java/src/net/i2p/util/SecureFileOutputStream.java | 4 ++-- router/java/src/net/i2p/router/RouterLaunch.java | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/core/java/src/net/i2p/util/SecureFileOutputStream.java b/core/java/src/net/i2p/util/SecureFileOutputStream.java index dd02f2d32..8a891344b 100644 --- a/core/java/src/net/i2p/util/SecureFileOutputStream.java +++ b/core/java/src/net/i2p/util/SecureFileOutputStream.java @@ -30,7 +30,7 @@ public class SecureFileOutputStream extends FileOutputStream { */ public SecureFileOutputStream(String file, boolean append) throws FileNotFoundException { super(file, append); - if (!append) + //if (!append) setPerms(new File(file)); } @@ -48,7 +48,7 @@ public class SecureFileOutputStream extends FileOutputStream { */ public SecureFileOutputStream(File file, boolean append) throws FileNotFoundException { super(file, append); - if (!append) + //if (!append) setPerms(file); } diff --git a/router/java/src/net/i2p/router/RouterLaunch.java b/router/java/src/net/i2p/router/RouterLaunch.java index 13ee2f3cf..069ee3ec5 100644 --- a/router/java/src/net/i2p/router/RouterLaunch.java +++ b/router/java/src/net/i2p/router/RouterLaunch.java @@ -1,10 +1,11 @@ package net.i2p.router; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; +import net.i2p.util.SecureFileOutputStream; + /** * This is the class called by the runplain.sh script on linux * and the i2p.exe launcher on Windows. @@ -33,7 +34,7 @@ public class RouterLaunch { } System.setProperty(PROP_WRAPPER_LOG, logfile.getAbsolutePath()); try { - System.setOut(new PrintStream(new FileOutputStream(logfile, true))); + System.setOut(new PrintStream(new SecureFileOutputStream(logfile, true))); } catch (IOException ioe) { ioe.printStackTrace(); } From 86a7d68f089e58d87bacfd3abdb3fa5c3295ba4d Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 7 Oct 2010 15:14:07 +0000 Subject: [PATCH 44/49] one more mime type --- apps/i2psnark/web.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/i2psnark/web.xml b/apps/i2psnark/web.xml index d2bc14751..2925ff00e 100644 --- a/apps/i2psnark/web.xml +++ b/apps/i2psnark/web.xml @@ -41,6 +41,11 @@ video/x-flv + + mp4 + video/mp4 + + rar application/rar From a23ea5e5f104f9f3fea8c59ff41f085fd8139b1e Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 7 Oct 2010 18:39:03 +0000 Subject: [PATCH 45/49] * configlogging.jsp: - Add easy way to add an override - Make file size specifier more flexible --- .../i2p/router/web/ConfigLoggingHandler.java | 30 +++++-- .../i2p/router/web/ConfigLoggingHelper.java | 86 ++++++++++++++++--- apps/routerconsole/jsp/configlogging.jsp | 2 + core/java/src/net/i2p/util/LogManager.java | 27 +++--- 4 files changed, 118 insertions(+), 27 deletions(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHandler.java index 347cfdab7..69013f60a 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHandler.java @@ -17,6 +17,8 @@ public class ConfigLoggingHandler extends FormHandler { private String _recordFormat; private String _dateFormat; private String _fileSize; + private String _newLogClass; + private String _newLogLevel = "WARN"; @Override protected void processForm() { @@ -26,7 +28,7 @@ public class ConfigLoggingHandler extends FormHandler { // noop } } - + public void setShouldsave(String moo) { _shouldSave = true; } public void setLevels(String levels) { @@ -47,6 +49,18 @@ public class ConfigLoggingHandler extends FormHandler { public void setLogfilesize(String size) { _fileSize = (size != null ? size.trim() : null); } + + /** @since 0.8.1 */ + public void setNewlogclass(String s) { + if (s != null && s.length() > 0) + _newLogClass = s; + } + + /** @since 0.8.1 */ + public void setNewloglevel(String s) { + if (s != null) + _newLogLevel = s; + } /** * The user made changes to the config and wants to save them, so @@ -56,14 +70,18 @@ public class ConfigLoggingHandler extends FormHandler { private void saveChanges() { boolean shouldSave = false; - if (_levels != null) { + if (_levels != null || _newLogClass != null) { try { Properties props = new Properties(); - props.load(new ByteArrayInputStream(_levels.getBytes())); + if (_levels != null) + props.load(new ByteArrayInputStream(_levels.getBytes())); + if (_newLogClass != null) + props.setProperty(_newLogClass, _newLogLevel); _context.logManager().setLimits(props); shouldSave = true; - addFormNotice("Log limits updated"); + addFormNotice(_("Log overrides updated")); } catch (IOException ioe) { + // shouldn't ever happen (BAIS shouldnt cause an IOE) _context.logManager().getLog(ConfigLoggingHandler.class).error("Error reading from the props?", ioe); addFormError("Error updating the log limits - levels not valid"); } @@ -83,7 +101,7 @@ public class ConfigLoggingHandler extends FormHandler { } } - if (_dateFormat != null) { + if (_dateFormat != null && !_dateFormat.equals(_context.logManager().getDateFormatPattern())) { boolean valid = _context.logManager().setDateFormat(_dateFormat); if (valid) { shouldSave = true; @@ -139,7 +157,7 @@ public class ConfigLoggingHandler extends FormHandler { boolean saved = _context.logManager().saveConfig(); if (saved) - addFormNotice("Log configuration saved and applied successfully"); + addFormNotice(_("Log configuration saved")); else addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs"); } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java index bde9b0893..5b6c742d5 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java @@ -1,9 +1,13 @@ package net.i2p.router.web; +import java.util.List; import java.util.Iterator; import java.util.Properties; +import java.util.Set; import java.util.TreeSet; +import net.i2p.data.DataHelper; +import net.i2p.util.Log; public class ConfigLoggingHelper extends HelperBase { public ConfigLoggingHelper() {} @@ -19,18 +23,13 @@ public class ConfigLoggingHelper extends HelperBase { } public String getMaxFileSize() { int bytes = _context.logManager().getFileSize(); - if (bytes == 0) return "1m"; - if (bytes > 1024*1024*1024) - return (bytes/(1024*1024*1024)) + "g"; - else if (bytes > 1024*1024) - return (bytes/(1024*1024)) + "m"; - else - return (bytes/(1024)) + "k"; + if (bytes <= 0) return "1 MB"; + return DataHelper.formatSize2(bytes) + 'B'; } public String getLogLevelTable() { StringBuilder buf = new StringBuilder(32*1024); Properties limits = _context.logManager().getLimits(); - TreeSet sortedLogs = new TreeSet(); + TreeSet sortedLogs = new TreeSet(); for (Iterator iter = limits.keySet().iterator(); iter.hasNext(); ) { String prefix = (String)iter.next(); sortedLogs.add(prefix); @@ -46,6 +45,20 @@ public class ConfigLoggingHelper extends HelperBase { buf.append("" + _("Add additional logging statements above. Example: net.i2p.router.tunnel=WARN") + "
    "); buf.append("" + _("Or put entries in the logger.config file. Example: logger.record.net.i2p.router.tunnel=WARN") + "
    "); buf.append("" + _("Valid levels are DEBUG, INFO, WARN, ERROR, CRIT") + "\n"); + + /**** + // this is too big and ugly + if (limits.size() <= 0) + return ""; + buf.append(""); + for (String prefix : sortedLogs) { + buf.append(""); + } + buf.append("
    ").append(prefix).append(""); + String level = limits.getProperty(prefix); + buf.append(getLogLevelBox("level-" + prefix, level, true)).append("
    "); + ****/ + return buf.toString(); } @@ -53,18 +66,69 @@ public class ConfigLoggingHelper extends HelperBase { public String getDefaultLogLevelBox() { String cur = _context.logManager().getDefaultLimit(); + return getLogLevelBox("defaultloglevel", cur, false); + } + + private String getLogLevelBox(String name, String cur, boolean showRemove) { StringBuilder buf = new StringBuilder(128); - buf.append("\n"); for (int i = 0; i < levels.length; i++) { String l = levels[i]; - buf.append("\n"); } + if (showRemove) + buf.append(""); buf.append("\n"); return buf.toString(); } + + /** + * All the classes the log manager knows about, except ones that + * already have overrides + * @since 0.8.1 + */ + public String getNewClassBox() { + List logs = _context.logManager().getLogs(); + Set limits = _context.logManager().getLimits().keySet(); + TreeSet sortedLogs = new TreeSet(); + + for (Log log : logs) { + String name = log.getName(); + if (!limits.contains(name)) + sortedLogs.add(name); + + // add higher classes of length 3 or more + int dots = 0; + int lastdot = -1; + int nextdot = 0; + while ((nextdot = name.indexOf('.', lastdot + 1)) > 0) { + if (++dots >= 3) { + String subst = name.substring(0, nextdot); + if (!limits.contains(subst)) + sortedLogs.add(subst); + } + lastdot = nextdot; + } + } + + StringBuilder buf = new StringBuilder(65536); + buf.append("\n"); + buf.append(getLogLevelBox("newloglevel", "WARN", false)); + return buf.toString(); + } } diff --git a/apps/routerconsole/jsp/configlogging.jsp b/apps/routerconsole/jsp/configlogging.jsp index 3de8d95be..ca30423dc 100644 --- a/apps/routerconsole/jsp/configlogging.jsp +++ b/apps/routerconsole/jsp/configlogging.jsp @@ -46,6 +46,8 @@ <%=intl._("Log level overrides")%>: + <%=intl._("New override")%>: +
    " > diff --git a/core/java/src/net/i2p/util/LogManager.java b/core/java/src/net/i2p/util/LogManager.java index 8bd344d65..d3ece13c5 100644 --- a/core/java/src/net/i2p/util/LogManager.java +++ b/core/java/src/net/i2p/util/LogManager.java @@ -156,7 +156,7 @@ public class LogManager { return rv; } - /** @deprecated unused */ + /** now used by ConfigLogingHelper */ public List getLogs() { return new ArrayList(_logs.values()); } @@ -415,15 +415,21 @@ public class LogManager { /** * Determine how many bytes are in the given formatted string (5m, 60g, 100k, etc) - * + * Size may be k, m, or g; a trailing b is ignored. Upper-case is allowed. + * Spaces between the number and letter is are allowed. + * The number may be in floating point. + * 4096 min, 2 GB max (returns int) */ - public int getFileSize(String size) { - int sz = -1; + public static int getFileSize(String size) { try { - String v = size; - char mod = size.toUpperCase().charAt(size.length() - 1); - if (!Character.isDigit(mod)) v = size.substring(0, size.length() - 1); - int val = Integer.parseInt(v); + String v = size.trim().toUpperCase(); + if (v.length() < 2) + return -1; + if (v.endsWith("B")) + v = v.substring(0, v.length() - 1); + char mod = v.charAt(v.length() - 1); + if (!Character.isDigit(mod)) v = v.substring(0, v.length() - 1); + double val = Double.parseDouble(v); switch (mod) { case 'K': val *= 1024; @@ -438,10 +444,11 @@ public class LogManager { // blah, noop break; } - return val; + if (val < 4096 || val > Integer.MAX_VALUE) + return -1; + return (int) val; } catch (Throwable t) { System.err.println("Error parsing config for filesize: [" + size + "]"); - t.printStackTrace(); return -1; } } From 500f6cf8969a48d467bb5e768080e0f2f480e483 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 7 Oct 2010 18:42:30 +0000 Subject: [PATCH 46/49] use context field --- apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java | 3 +-- .../java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java index d5b88d4d3..435b30947 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClient.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; -import net.i2p.I2PAppContext; import net.i2p.client.streaming.I2PSocket; import net.i2p.data.DataFormatException; import net.i2p.data.Destination; @@ -110,7 +109,7 @@ public class I2PTunnelClient extends I2PTunnelClientBase { } if (size == 1) // skip the rand in the most common case return dests.get(0); - int index = I2PAppContext.getGlobalContext().random().nextInt(size); + int index = _context.random().nextInt(size); return dests.get(index); } } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java index fe92e94b6..d9afdc5f2 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelConnectClient.java @@ -146,7 +146,7 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna int size = _proxyList.size(); if (size <= 0) return null; - int index = I2PAppContext.getGlobalContext().random().nextInt(size); + int index = _context.random().nextInt(size); return _proxyList.get(index); } } From fbc20da6068c3aaa0ddb35b6722ad7c87d344e0e Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 11 Oct 2010 15:14:55 +0000 Subject: [PATCH 47/49] more header blocking --- .../src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index b6c72821e..f34ceb1a5 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -205,7 +205,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable l.log("Proxy list is empty - no outproxy available"); return null; } - int index = I2PAppContext.getGlobalContext().random().nextInt(size); + int index = _context.random().nextInt(size); String proxy = (String)proxyList.get(index); return proxy; } @@ -626,6 +626,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable //line = "From: i2p"; line = null; continue; // completely strip the line + } else if (lowercaseLine.startsWith("authorization: ntlm ")) { + // Block Windows NTLM after 401 + line = null; + continue; + } else if (lowercaseLine.startsWith("proxy-authorization: ntlm ")) { + // Block Windows NTLM after 407 + line = null; + continue; } } @@ -808,7 +816,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable * @return non-null */ private byte[] getErrorPage(String base, byte[] backup) { - return getErrorPage(getTunnel().getContext(), base, backup); + return getErrorPage(_context, base, backup); } private static byte[] getErrorPage(I2PAppContext ctx, String base, byte[] backup) { From 798bdf32c112a7059f05206aff684b60747b43a4 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 11 Oct 2010 15:17:35 +0000 Subject: [PATCH 48/49] * Streaming: - Make flush() block less, by waiting only for "accept" into the streaming queue rather than "completion" (i.e. ACK from the far end). This prevents complete window stalls when flushing, and should help performance of apps that use flush(), like i2psnark (and SAM?). close() still does a flush that waits for completion, as i2ptunnel doesn't like a fast return from close(). - flush/close javadocs and comments * i2ptunnel: - Now that streaming flush() is fixed, use it in IRCClient, and for initial data in I2PTunnelRunner, to avoid the 250 ms passive flush delay --- .../net/i2p/i2ptunnel/I2PTunnelIRCClient.java | 7 +- .../net/i2p/i2ptunnel/I2PTunnelRunner.java | 9 +- .../net/i2p/client/streaming/Connection.java | 11 ++- .../client/streaming/MessageOutputStream.java | 89 ++++++++++++++++--- .../net/i2p/client/streaming/PacketLocal.java | 4 +- 5 files changed, 100 insertions(+), 20 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java index 4d8b85a81..d0dc227ec 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCClient.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; -import net.i2p.I2PAppContext; import net.i2p.client.streaming.I2PSocket; import net.i2p.data.DataFormatException; import net.i2p.data.Destination; @@ -124,7 +123,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable } if (size == 1) // skip the rand in the most common case return dests.get(0); - int index = I2PAppContext.getGlobalContext().random().nextInt(size); + int index = _context.random().nextInt(size); return dests.get(index); } @@ -182,6 +181,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable } outmsg=outmsg+"\r\n"; // rfc1459 sec. 2.3 output.write(outmsg.getBytes("ISO-8859-1")); + // probably doesn't do much but can't hurt + output.flush(); } else { if (_log.shouldLog(Log.WARN)) _log.warn("inbound BLOCKED: "+inmsg); @@ -257,6 +258,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable } outmsg=outmsg+"\r\n"; // rfc1459 sec. 2.3 output.write(outmsg.getBytes("ISO-8859-1")); + // save 250 ms in streaming + output.flush(); } else { if (_log.shouldLog(Log.WARN)) _log.warn("outbound BLOCKED: "+"\""+inmsg+"\""); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java index c2a014020..0e1c9049f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelRunner.java @@ -129,7 +129,14 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr // do NOT flush here, it will block and then onTimeout.run() won't happen on fail. // But if we don't flush, then we have to wait for the connectDelay timer to fire // in i2p socket? To be researched and/or fixed. - //i2pout.flush(); + // + // AS OF 0.8.1, MessageOutputStream.flush() is fixed to only wait for accept, + // not for "completion" (i.e. an ACK from the far end). + // So we now get a fast return from flush(), and can do it here to save 250 ms. + // To make sure we are under the initial window size and don't hang waiting for accept, + // only flush if it fits in one message. + if (initialI2PData.length <= 1730) // ConnectionOptions.DEFAULT_MAX_MESSAGE_SIZE + i2pout.flush(); } } if (initialSocketData != null) { 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 bc8c92369..e662ecc60 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Connection.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Connection.java @@ -148,16 +148,21 @@ public class Connection { } /** + * This doesn't "send a choke". Rather, it blocks if the outbound window is full, + * thus choking the sender that calls this. + * * Block until there is an open outbound packet slot or the write timeout * expires. + * PacketLocal is the only caller, generally with -1. * - * @param timeoutMs PacketLocal is the only caller, often with -1?????? - * @return true if the packet should be sent + * @param timeoutMs 0 or negative means wait forever, 5 minutes max + * @return true if the packet should be sent, false for a fatal error + * will return false after 5 minutes even if timeoutMs is <= 0. */ boolean packetSendChoke(long timeoutMs) { // if (false) return true; // <--- what the fuck?? long start = _context.clock().now(); - long writeExpire = start + timeoutMs; + long writeExpire = start + timeoutMs; // only used if timeoutMs > 0 boolean started = false; while (true) { long timeLeft = writeExpire - _context.clock().now(); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java index 1a7f0afbe..fd10d679c 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageOutputStream.java @@ -43,6 +43,10 @@ public class MessageOutputStream extends OutputStream { private long _sendPeriodBytes; private int _sendBps; + /** + * Since this is less than i2ptunnel's i2p.streaming.connectDelay default of 1000, + * we only wait 250 at the start. Guess that's ok, 1000 is too long anyway. + */ private static final int DEFAULT_PASSIVE_FLUSH_DELAY = 250; public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver) { @@ -273,8 +277,18 @@ public class MessageOutputStream extends OutputStream { } /** - * Flush the data already queued up, blocking until it has been - * delivered. + * Flush the data already queued up, blocking only if the outbound + * window is full. + * + * Prior to 0.8.1, this blocked until "delivered". + * "Delivered" meant "received an ACK from the far end", + * which is not the commom implementation of flush(), and really hurt the + * performance of i2psnark, which flush()ed frequently. + * Calling flush() would cause a complete window stall. + * + * As of 0.8.1, only wait for accept into the streaming output queue. + * This will speed up snark significantly, and allow us to flush() + * the initial data in I2PTunnelRunner, saving 250 ms. * * @throws IOException if the write fails */ @@ -283,6 +297,14 @@ public class MessageOutputStream extends OutputStream { /* @throws InterruptedIOException if the write times out * Documented here, but doesn't belong in the javadoc. */ + flush(true); + } + + /** + * @param wait_for_accept_only see discussion in close() code + * @@since 0.8.1 + */ + private void flush(boolean wait_for_accept_only) throws IOException { long begin = _context.clock().now(); WriteStatus ws = null; if (_log.shouldLog(Log.INFO) && _valid > 0) @@ -297,14 +319,28 @@ public class MessageOutputStream extends OutputStream { throwAnyError(); return; } - ws = _dataReceiver.writeData(_buf, 0, _valid); - _written += _valid; - _valid = 0; - locked_updateBufferSize(); - _lastFlushed = _context.clock().now(); - _dataLock.notifyAll(); + // if valid == 0 return ??? - no, this could flush a CLOSE packet too. + + // Yes, flush here, inside the data lock, and do all the waitForCompletion() stuff below + // (disabled) + if (!wait_for_accept_only) { + ws = _dataReceiver.writeData(_buf, 0, _valid); + _written += _valid; + _valid = 0; + locked_updateBufferSize(); + _lastFlushed = _context.clock().now(); + _dataLock.notifyAll(); + } } + // Skip all the waitForCompletion() stuff below, which is insanity, as of 0.8.1 + // must do this outside the data lock + if (wait_for_accept_only) { + flushAvailable(_dataReceiver, true); + return; + } + + // Wait a loooooong time, until we have the ACK if (_log.shouldLog(Log.DEBUG)) _log.debug("before waiting " + _writeTimeout + "ms for completion of " + ws); if (_closed && @@ -328,14 +364,28 @@ public class MessageOutputStream extends OutputStream { throwAnyError(); } + /** + * This does a flush, and BLOCKS until + * the CLOSE packet is acked. + */ @Override public void close() throws IOException { if (_closed) { synchronized (_dataLock) { _dataLock.notifyAll(); } return; } + // setting _closed before flush() will force flush() to send a CLOSE packet _closed = true; - flush(); + + // In 0.8.1 we rewrote flush() to only wait for accept into the window, + // not "completion" (i.e. ack from the far end). + // Unfortunately, that broke close(), at least in i2ptunnel HTTPClient. + // Symptom was premature close, i.e. incomplete pages and images. + // Possible cause - I2PTunnelRunner code? or the code here that follows flush()? + // It seems like we shouldn't have to wait for the far-end ACK for a close packet, + // should we? To be researched further. + // false -> wait for completion, not just accept. + flush(false); _log.debug("Output stream closed after writing " + _written); ByteArray ba = null; synchronized (_dataLock) { @@ -351,7 +401,11 @@ public class MessageOutputStream extends OutputStream { _dataCache.release(ba); } } - /** nonblocking close */ + + /** + * nonblocking close - + * Use outside of this package is deprecated, should be made package local + */ public void closeInternal() { _closed = true; if (_streamError == null) @@ -412,6 +466,8 @@ public class MessageOutputStream extends OutputStream { if (_log.shouldLog(Log.INFO) && _valid > 0) _log.info("flushAvailable() valid = " + _valid); synchronized (_dataLock) { + // if valid == 0 return ??? - no, this could flush a CLOSE packet too. + // _buf may be null, but the data receiver can handle that just fine, // deciding whether or not to send a packet ws = target.writeData(_buf, 0, _valid); @@ -457,14 +513,21 @@ public class MessageOutputStream extends OutputStream { /** Define a way to detect the status of a write */ public interface WriteStatus { - /** wait until the data written either fails or succeeds */ + /** + * Wait until the data written either fails or succeeds. + * Success means an ACK FROM THE FAR END. + * @param maxWaitMs -1 = forever + */ public void waitForCompletion(int maxWaitMs); + /** - * wait until the data written is accepted into the outbound pool, + * Wait until the data written is accepted into the outbound pool, + * (i.e. the outbound window is not full) * which we throttle rather than accept arbitrary data and queue - * @param maxWaitMs -1 = forever ? + * @param maxWaitMs -1 = forever */ public void waitForAccept(int maxWaitMs); + /** the write was accepted. aka did the socket not close? */ public boolean writeAccepted(); /** did the write fail? */ diff --git a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java index 674ff6179..cbe913e05 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/PacketLocal.java @@ -194,7 +194,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat } /** - * @param maxWaitMs MessageOutputStream is the only caller, often with -1 ?????? + * Blocks until outbound window is not full. See Connection.packetSendChoke(). + * @param maxWaitMs MessageOutputStream is the only caller, generally with -1 */ public void waitForAccept(int maxWaitMs) { if (_connection == null) @@ -220,6 +221,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat + toString()); } + /** block until the packet is acked from the far end */ public void waitForCompletion(int maxWaitMs) { long expiration = _context.clock().now()+maxWaitMs; while (true) { From 647b8f7fa13ef5f19fb2d227ccfa4a102802f0c0 Mon Sep 17 00:00:00 2001 From: zzz Date: Mon, 11 Oct 2010 15:18:17 +0000 Subject: [PATCH 49/49] default tweak --- .../java/src/net/i2p/router/web/ConfigLoggingHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java index 5b6c742d5..ce41b1e12 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigLoggingHelper.java @@ -23,7 +23,7 @@ public class ConfigLoggingHelper extends HelperBase { } public String getMaxFileSize() { int bytes = _context.logManager().getFileSize(); - if (bytes <= 0) return "1 MB"; + if (bytes <= 0) return "1.00 MB"; return DataHelper.formatSize2(bytes) + 'B'; } public String getLogLevelTable() {