From 07caf2e316c31a2b43f7d2765aad6762e0d85eab Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 13 Jun 2012 19:02:09 +0000 Subject: [PATCH 01/16] * I2PSocketEepGet: Set port to 80 * I2PTunnel: - Pass port through HTTP client proxy - HTTP server proxy sets host header to the value of "spoofedhost.xx" option for port xx - Set client options more efficiently --- .../java/src/net/i2p/i2ptunnel/I2PTunnel.java | 19 +++++++++++++++ .../i2p/i2ptunnel/I2PTunnelHTTPClient.java | 17 ++++++++++---- .../i2p/i2ptunnel/I2PTunnelHTTPServer.java | 23 +++++++++++++++---- .../net/i2p/i2ptunnel/I2PTunnelIRCServer.java | 3 +++ .../net/i2p/i2ptunnel/I2PTunnelServer.java | 3 ++- .../net/i2p/i2ptunnel/TunnelController.java | 17 +++++++------- .../i2p/client/streaming/I2PSocketEepGet.java | 2 ++ 7 files changed, 65 insertions(+), 19 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index d7ca0c163..f169d0573 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -46,6 +46,7 @@ import java.util.ArrayList; 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.StringTokenizer; @@ -330,6 +331,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { * * Sets the event "clientoptions_onResult" = "ok" after completion. * + * Deprecated use setClientOptions() + * * @param args each args[i] is a key=value pair to add to the options * @param l logger to receive events and output */ @@ -347,6 +350,22 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { notifyEvent("clientoptions_onResult", "ok"); } + /** + * A more efficient runClientOptions() + * + * @param opts non-null + * @since 0.9.1 + */ + public void setClientOptions(Properties opts) { + _clientOptions.clear(); + for (Map.Entry e : opts.entrySet()) { + String key = (String) e.getKey(); + String val = (String) e.getValue(); + _clientOptions.setProperty(key, val); + } + notifyEvent("clientoptions_onResult", "ok"); + } + /** * Run the server pointing at the host and port specified using the private i2p * destination loaded from the specified file.

diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 48a6a60b4..4e7f0c257 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -330,6 +330,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn String ahelperKey = null; String userAgent = null; String authorization = null; + int remotePort = 0; while((line = reader.readLine(method)) != null) { line = line.trim(); if(_log.shouldLog(Log.DEBUG)) { @@ -486,10 +487,11 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn // Host becomes the destination's "{b32}.b32.i2p" string, or "i2p" on lookup failure host = getHostName(destination); - if(requestURI.getPort() >= 0) { - // TODO support I2P ports someday - //if (port >= 0) - // host = host + ':' + port; + int rPort = requestURI.getPort(); + if (rPort > 0) { + // Save it to put in the I2PSocketOptions, + // but strip it from the URL + remotePort = rPort; if(_log.shouldLog(Log.WARN)) { _log.warn(getPrefix(requestId) + "Removing port from [" + request + "]"); } @@ -500,6 +502,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn method = null; break; } + } else { + remotePort = 80; } String query = requestURI.getRawQuery(); @@ -964,7 +968,10 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn // 1 == disconnect. see ConnectionOptions in the new streaming lib, which i // dont want to hard link to here //opts.setProperty("i2p.streaming.inactivityTimeoutAction", ""+1); - I2PSocket i2ps = createI2PSocket(clientDest, getDefaultOptions(opts)); + I2PSocketOptions sktOpts = getDefaultOptions(opts); + if (remotePort > 0) + sktOpts.setPort(remotePort); + I2PSocket i2ps = createI2PSocket(clientDest, sktOpts); byte[] data = newRequest.toString().getBytes("ISO-8859-1"); Runnable onTimeout = new OnTimeout(s, s.getOutputStream(), targetRequest, usingWWWProxy, currentProxy, requestId); new I2PTunnelHTTPClientRunner(s, i2ps, sockLock, data, mySockets, onTimeout); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java index 62845e3c6..58f0c2f4b 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPServer.java @@ -77,7 +77,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { } private void setupI2PTunnelHTTPServer(String spoofHost) { - _spoofHost = spoofHost; + _spoofHost = (spoofHost != null && spoofHost.trim().length() > 0) ? spoofHost.trim() : null; getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpserver.blockingHandleTime", "how long the blocking handle takes to complete", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000, 3*60*60*1000 }); getTunnel().getContext().statManager().createRateStat("i2ptunnel.httpNullWorkaround", "How often an http server works around a streaming lib or i2ptunnel bug", "I2PTunnel.HTTPServer", new long[] { 60*1000, 10*60*1000 }); } @@ -96,6 +96,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { */ @Override protected void blockingHandle(I2PSocket socket) { + if (_log.shouldLog(Log.INFO)) + _log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() + + " from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort()); long afterAccept = getTunnel().getContext().clock().now(); long afterSocket = -1; //local is fast, so synchronously. Does not need that many @@ -115,8 +118,21 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { addEntry(headers, DEST32_HEADER, Base32.encode(socket.getPeerDestination().calculateHash().getData()) + ".b32.i2p"); addEntry(headers, DEST64_HEADER, socket.getPeerDestination().toBase64()); - if ( (_spoofHost != null) && (_spoofHost.trim().length() > 0) ) - setEntry(headers, "Host", _spoofHost); + // Port-specific spoofhost + Properties opts = getTunnel().getClientOptions(); + String spoofHost; + int ourPort = socket.getLocalPort(); + if (ourPort != 80 && ourPort > 0 && ourPort < 65535 && opts != null) { + String portSpoof = opts.getProperty("spoofedHost." + ourPort); + if (portSpoof != null) + spoofHost = portSpoof.trim(); + else + spoofHost = _spoofHost; + } else { + spoofHost = _spoofHost; + } + if (spoofHost != null) + setEntry(headers, "Host", spoofHost); setEntry(headers, "Connection", "close"); // we keep the enc sent by the browser before clobbering it, since it may have // been x-i2p-gzip @@ -134,7 +150,6 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer { // request from the socket, modifies the headers, sends the request to the // server, reads the response headers, rewriting to include Content-encoding: x-i2p-gzip // if it was one of the Accept-encoding: values, and gzip the payload - Properties opts = getTunnel().getClientOptions(); boolean allowGZIP = true; if (opts != null) { String val = opts.getProperty("i2ptunnel.gzip"); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java index da063b0d0..b592ebcf8 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelIRCServer.java @@ -111,6 +111,9 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable { @Override protected void blockingHandle(I2PSocket socket) { + if (_log.shouldLog(Log.INFO)) + _log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() + + " from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort()); try { String modifiedRegistration; if(!this.method.equals("webirc")) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java index 7c231add1..60d6abf3f 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -408,7 +408,8 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { protected void blockingHandle(I2PSocket socket) { if (_log.shouldLog(Log.INFO)) - _log.info("Incoming connection to '" + toString() + "' from: " + socket.getPeerDestination().calculateHash().toBase64()); + _log.info("Incoming connection to '" + toString() + "' port " + socket.getLocalPort() + + " from: " + socket.getPeerDestination().calculateHash() + " port " + socket.getPort()); long afterAccept = I2PAppContext.getGlobalContext().clock().now(); long afterSocket = -1; //local is fast, so synchronously. Does not need that many diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index a2dc146d6..2386a9dbe 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -4,8 +4,10 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.Random; @@ -385,19 +387,16 @@ public class TunnelController implements Logging { } private void setSessionOptions() { - List opts = new ArrayList(); - for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) { - String key = (String)iter.next(); - String val = _config.getProperty(key); + Properties opts = new Properties(); + for (Map.Entry e : _config.entrySet()) { + String key = (String) e.getKey(); if (key.startsWith("option.")) { key = key.substring("option.".length()); - opts.add(key + "=" + val); + String val = (String) e.getValue(); + opts.setProperty(key, val); } } - String args[] = new String[opts.size()]; - for (int i = 0; i < opts.size(); i++) - args[i] = (String)opts.get(i); - _tunnel.runClientOptions(args, this); + _tunnel.setClientOptions(opts); } private void setI2CPOptions() { diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java index 8365d07fa..cfc0675c0 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java @@ -143,6 +143,8 @@ public class I2PSocketEepGet extends EepGet { // in the SYN packet, saving one RTT. props.setProperty(PROP_CONNECT_DELAY, CONNECT_DELAY); I2PSocketOptions opts = _socketManager.buildOptions(props); + // TODO pull port out of URL + opts.setPort(80); _socket = _socketManager.connect(dest, opts); } else { throw new IOException("Unsupported protocol: " + _actualURL); From ad1b356879a922894307bd6dfc5efaab64105060 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 13 Jun 2012 19:04:11 +0000 Subject: [PATCH 02/16] * i2psnark: Possible fix for piece-after-choke --- apps/i2psnark/java/src/org/klomp/snark/PeerState.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java index bbe6ff932..d221d978a 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/PeerState.java +++ b/apps/i2psnark/java/src/org/klomp/snark/PeerState.java @@ -39,13 +39,13 @@ class PeerState implements DataLoader // Interesting and choking describes whether we are interested in or // are choking the other side. - boolean interesting = false; - boolean choking = true; + volatile boolean interesting; + volatile boolean choking = true; // Interested and choked describes whether the other side is // interested in us or choked us. - boolean interested = false; - boolean choked = true; + volatile boolean interested; + volatile boolean choked = true; /** the pieces the peer has */ BitField bitfield; From c73044b6b4048c45a5147de8fa8bab4b26353303 Mon Sep 17 00:00:00 2001 From: zzz Date: Wed, 13 Jun 2012 19:08:49 +0000 Subject: [PATCH 03/16] * Streaming: - Channel cleanups and comments - New I2PSocketAddress --- .../client/streaming/AcceptingChannel.java | 10 +- .../client/streaming/I2PSocketAddress.java | 107 ++++++++++++++++++ .../streaming/AcceptingChannelImpl.java | 37 +++--- .../client/streaming/I2PServerSocketFull.java | 7 +- .../i2p/client/streaming/I2PSocketFull.java | 7 +- .../i2p/client/streaming/MessageChannel.java | 25 ++-- .../streaming/StandardServerSocket.java | 3 +- .../i2p/client/streaming/StandardSocket.java | 3 +- history.txt | 14 ++- .../src/net/i2p/router/RouterVersion.java | 2 +- 10 files changed, 182 insertions(+), 33 deletions(-) create mode 100644 apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketAddress.java diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/AcceptingChannel.java b/apps/ministreaming/java/src/net/i2p/client/streaming/AcceptingChannel.java index 16c96cc89..292bb28ea 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/AcceptingChannel.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/AcceptingChannel.java @@ -7,11 +7,19 @@ import java.net.ConnectException; import java.nio.channels.SelectableChannel; /** + * As this does not (yet) extend ServerSocketChannel it cannot be returned by StandardServerSocket.getChannel(), + * until we implement an I2P SocketAddress class. + * + * Warning, this interface and implementation is preliminary and subject to change without notice. + * * @since 0.8.11 */ public abstract class AcceptingChannel extends SelectableChannel { + abstract I2PSocket accept() throws I2PException, ConnectException; - I2PSocketManager _socketManager; + + protected final I2PSocketManager _socketManager; + AcceptingChannel(I2PSocketManager manager) { this._socketManager = manager; } diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketAddress.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketAddress.java new file mode 100644 index 000000000..4ba55bdb1 --- /dev/null +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketAddress.java @@ -0,0 +1,107 @@ +package net.i2p.client.streaming; + +import java.net.SocketAddress; + +import net.i2p.I2PAppContext; +import net.i2p.data.Destination; +import net.i2p.data.DataHelper; + +/** + * A SocketAddress (Destination + port) so we can have SocketChannels. + * Ports are not widely used in I2P, in most cases the port will be zero. + * See InetSocketAddress for javadocs. + * + * Warning, this interface and implementation is preliminary and subject to change without notice. + * + * @since 0.9.1 + */ +public class I2PSocketAddress extends SocketAddress { + + private final int _port; + private final Destination _dest; + private final String _host; + + // no constructor for port-only "wildcard" address + + /** + * Does not do a reverse lookup. Host will be null. + */ + public I2PSocketAddress(Destination dest, int port) { + _port = port; + _dest = dest; + _host = null; + } + + /** + * Does a naming service lookup to resolve the dest. + * May take several seconds for b32. + */ + public I2PSocketAddress(String host, int port) { + _port = port; + _dest = I2PAppContext.getGlobalContext().namingService().lookup(host); + _host = host; + } + + public static I2PSocketAddress createUnresolved(String host, int port) { + return new I2PSocketAddress(port, host); + } + + /** unresolved */ + private I2PSocketAddress(int port, String host) { + _port = port; + _dest = null; + _host = host; + } + + public int getPort() { + return _port; + } + + public Destination getAddress() { + return _dest; + } + + /** + * @return the host only if given in the constructor. Does not do a reverse lookup. + */ + public String getHostName() { + return _host; + } + + public boolean isUnresolved() { + return _dest == null; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + if (_dest != null) + buf.append(_dest.calculateHash().toString()); + else + buf.append(_host); + buf.append(':'); + buf.append(_port); + return buf.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof I2PSocketAddress)) + return false; + I2PSocketAddress o = (I2PSocketAddress) obj; + if (_port != o._port) + return false; + if (_dest != null) + return _dest.equals(o._dest); + if (o._dest != null) + return false; + if (_host != null) + return _host.equals(o._host); + return o._host == null; + } + + @Override + public int hashCode() { + return DataHelper.hashCode(_dest) ^ DataHelper.hashCode(_host) ^ _port; + } +} diff --git a/apps/streaming/java/src/net/i2p/client/streaming/AcceptingChannelImpl.java b/apps/streaming/java/src/net/i2p/client/streaming/AcceptingChannelImpl.java index 89242afbc..61dc90170 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/AcceptingChannelImpl.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/AcceptingChannelImpl.java @@ -13,16 +13,21 @@ import java.nio.channels.spi.AbstractSelectionKey; import java.nio.channels.spi.SelectorProvider; /** + * As this does not (yet) extend ServerSocketChannel it cannot be returned by StandardServerSocket.getChannel(), + * until we implement an I2P SocketAddress class. + * + * Warning, this interface and implementation is preliminary and subject to change without notice. + * * @since 0.8.11 */ -public class AcceptingChannelImpl extends AcceptingChannel { - boolean _isRegistered = false; - SelectionKey whichKey = null; - SelectorProvider provider = null; - Selector sel = null; - Object lock = null; - I2PSocket next = null; - I2PServerSocket socket; +class AcceptingChannelImpl extends AcceptingChannel { + private boolean _isRegistered; + private SelectionKey whichKey; + private SelectorProvider provider; + private Selector sel; + private Object lock; + private volatile I2PSocket next; + private final I2PServerSocket socket; I2PSocket accept() throws I2PException, ConnectException { I2PSocket sock; @@ -31,9 +36,11 @@ public class AcceptingChannelImpl extends AcceptingChannel { } catch(SocketTimeoutException ex) { return null; } - I2PSocket temp = next; - next = sock; - return temp; + synchronized (this) { + I2PSocket temp = next; + next = sock; + return temp; + } } AcceptingChannelImpl(I2PSocketManager manager) { @@ -96,7 +103,7 @@ public class AcceptingChannelImpl extends AcceptingChannel { @Override public int readyOps() { - if((operations & OP_ACCEPT) != 0) + if((operations & OP_ACCEPT) != 0) { if(next != null) { return OP_ACCEPT; } else { @@ -107,6 +114,7 @@ public class AcceptingChannelImpl extends AcceptingChannel { if(next != null) return OP_ACCEPT; } + } return 0; } }; @@ -136,8 +144,9 @@ public class AcceptingChannelImpl extends AcceptingChannel { @Override protected void implCloseChannel() throws IOException { - if(next != null) { - next.close(); + I2PSocket nxt = next; + if(nxt != null) { + nxt.close(); } _socketManager.destroySocketManager(); } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java index 244664bf4..5eddc45c5 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PServerSocketFull.java @@ -9,6 +9,7 @@ import net.i2p.I2PException; */ class I2PServerSocketFull implements I2PServerSocket { private final I2PSocketManagerFull _socketManager; + private volatile AcceptingChannel _channel; public I2PServerSocketFull(I2PSocketManagerFull mgr) { _socketManager = mgr; @@ -28,8 +29,10 @@ class I2PServerSocketFull implements I2PServerSocket { /** * @since 0.8.11 */ - public AcceptingChannel getChannel() { - return new AcceptingChannelImpl(_socketManager); + public synchronized AcceptingChannel getChannel() { + if (_channel == null) + _channel = new AcceptingChannelImpl(_socketManager); + return _channel; } public long getSoTimeout() { diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java index 6f1caeaf0..abca9b82f 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketFull.java @@ -16,6 +16,7 @@ class I2PSocketFull implements I2PSocket { private Connection _connection; private Destination _remotePeer; private Destination _localPeer; + private volatile MessageChannel _channel; public I2PSocketFull(Connection con) { _connection = con; @@ -70,8 +71,10 @@ class I2PSocketFull implements I2PSocket { /** * @since 0.8.9 */ - public SelectableChannel getChannel() { - return new MessageChannel(this); + public synchronized SelectableChannel getChannel() { + if (_channel == null) + _channel = new MessageChannel(this); + return _channel; } /** diff --git a/apps/streaming/java/src/net/i2p/client/streaming/MessageChannel.java b/apps/streaming/java/src/net/i2p/client/streaming/MessageChannel.java index 05ba76bb7..af10cf886 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/MessageChannel.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/MessageChannel.java @@ -15,18 +15,23 @@ import java.util.logging.Level; import java.util.logging.Logger; /** + * As this does not (yet) extend SocketChannel it cannot be returned by StandardSocket.getChannel(), + * until we implement an I2P SocketAddress class. + * + * Warning, this interface and implementation is preliminary and subject to change without notice. + * * @since 0.8.9 */ public class MessageChannel extends SelectableChannel implements ReadableByteChannel, WritableByteChannel { - final MessageInputStream in; - final MessageOutputStream out; - boolean _isRegistered = false; - SelectionKey whichKey = null; - SelectorProvider provider = null; - Selector sel = null; - Object lock = null; - I2PSocket socket; + private final MessageInputStream in; + private final MessageOutputStream out; + private boolean _isRegistered; + private SelectionKey whichKey; + private SelectorProvider provider; + private Selector sel; + private Object lock; + private final I2PSocket socket; MessageChannel(I2PSocket socket) { try { @@ -145,10 +150,10 @@ public class MessageChannel extends SelectableChannel implements ReadableByteCha * returns 0, which happens when there's * no more data available. */ - public int read(ByteBuffer buf) throws IOException { int amount = 0; for (;;) { + // TODO if buf.hasArray() ... getArray() ... getArrayOffset() ... byte[] lbuf = new byte[buf.remaining()]; int samount = in.read(lbuf); if (samount <= 0) { @@ -167,12 +172,12 @@ public class MessageChannel extends SelectableChannel implements ReadableByteCha * already set buffer size. Once it starts to fail * (wait timeout is 0) then put the bytes back and return. */ - public int write(ByteBuffer buf) throws IOException { int written = 0; for (;;) { if(buf.remaining()==0) return written; + // TODO if buf.hasArray() ... getArray() ... getArrayOffset() ... byte[] lbuf = new byte[Math.min(buf.remaining(), 0x1000)]; buf.get(lbuf); try { diff --git a/apps/streaming/java/src/net/i2p/client/streaming/StandardServerSocket.java b/apps/streaming/java/src/net/i2p/client/streaming/StandardServerSocket.java index 324d12687..1429fa606 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/StandardServerSocket.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/StandardServerSocket.java @@ -72,10 +72,11 @@ class StandardServerSocket extends ServerSocket { } /** - * @return null always + * @return null always, see AcceptingChannelImpl for more info */ @Override public ServerSocketChannel getChannel() { + //return _socket.getChannel(); return null; } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/StandardSocket.java b/apps/streaming/java/src/net/i2p/client/streaming/StandardSocket.java index 94bac2642..30de8265a 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/StandardSocket.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/StandardSocket.java @@ -65,10 +65,11 @@ class StandardSocket extends Socket { } /** - * @return null always + * @return null always, see MessageChannel for more info */ @Override public SocketChannel getChannel() { + //return _socket.getChannel(); return null; } diff --git a/history.txt b/history.txt index 74e4e2d12..98ac2da20 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,15 @@ +2012-06-13 zzz + * I2PSocketEepGet: Set port to 80 + * I2PTunnel: + - Pass port through HTTP client proxy + - HTTP server proxy sets host header to + the value of "spoofedhost.xx" option for port xx + - Set client options more efficiently + * i2psnark: Possible fix for piece-after-choke + * Streaming: + - Channel cleanups and comments + - New I2PSocketAddress + 2012-06-11 zzz * i2psnark: - Display torrent file downloads in torrent area @@ -8,7 +20,7 @@ - Reduce delay between peer adds for faster startup - Thread the announces and reduce timeout when stopping * NativeBigInteger: Workaround for Raspberry Pi to load the correct lib - * Router: Don't let shutdown tasks hang the shutdown + * Router: Don't let shutdown tasks hang the shutdown (tickets #564, #566) 2012-06-08 zzz * i2psnark: diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index 7f4edb975..31c35f8c3 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 13; + public final static long BUILD = 14; /** for example "-test" */ public final static String EXTRA = ""; From 64221fb3fb3862fa6dc8f09ba3fd110858fee2e6 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 14 Jun 2012 19:42:07 +0000 Subject: [PATCH 04/16] * I2PSocketEepGet: Use specified port * I2PTunnel: - Don't strip port from an I2P URL --- .../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java | 4 +++- .../java/src/net/i2p/client/streaming/I2PSocketEepGet.java | 7 +++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java index 4e7f0c257..e80695dbc 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java @@ -490,8 +490,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn int rPort = requestURI.getPort(); if (rPort > 0) { // Save it to put in the I2PSocketOptions, - // but strip it from the URL remotePort = rPort; + /******** + // but strip it from the URL if(_log.shouldLog(Log.WARN)) { _log.warn(getPrefix(requestId) + "Removing port from [" + request + "]"); } @@ -502,6 +503,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn method = null; break; } + ******/ } else { remotePort = 80; } diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java index cfc0675c0..e548055c3 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketEepGet.java @@ -111,8 +111,8 @@ public class I2PSocketEepGet extends EepGet { if ("http".equals(url.getProtocol())) { String host = url.getHost(); int port = url.getPort(); - if (port != -1) - throw new IOException("Ports not supported in i2p: " + _actualURL); + if (port <= 0 || port >= 65535) + port = 80; // HTTP Proxy compatibility http://i2p/B64KEY/blah // Rewrite the url to strip out the /i2p/, @@ -143,8 +143,7 @@ public class I2PSocketEepGet extends EepGet { // in the SYN packet, saving one RTT. props.setProperty(PROP_CONNECT_DELAY, CONNECT_DELAY); I2PSocketOptions opts = _socketManager.buildOptions(props); - // TODO pull port out of URL - opts.setPort(80); + opts.setPort(port); _socket = _socketManager.connect(dest, opts); } else { throw new IOException("Unsupported protocol: " + _actualURL); From e522ffad4ebde97258af7691729766b3b9b71316 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 14 Jun 2012 19:44:47 +0000 Subject: [PATCH 05/16] * I2PTunnel: - More client options cleanups - Options changes now propagate to running socket managers and sessions, and through to the router * SocketManager: - Simplify factory, use 4-arg constructor, make fields final, deprecate 0-arg constructor - Improve how options are updated - Javadocs --- .../java/src/net/i2p/i2ptunnel/I2PTunnel.java | 27 ++++-- .../i2p/i2ptunnel/I2PTunnelClientBase.java | 16 ++++ .../net/i2p/i2ptunnel/I2PTunnelServer.java | 13 +++ .../src/net/i2p/i2ptunnel/I2PTunnelTask.java | 9 ++ .../net/i2p/i2ptunnel/TunnelController.java | 58 +++++++++--- .../src/net/i2p/i2ptunnel/web/EditBean.java | 14 +-- .../client/streaming/I2PSocketManager.java | 25 ++++++ .../streaming/I2PSocketManagerFactory.java | 48 +++------- .../client/streaming/ConnectionOptions.java | 88 ++++++++++++++----- .../streaming/I2PSocketManagerFull.java | 72 ++++++++++----- 10 files changed, 255 insertions(+), 115 deletions(-) diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index f169d0573..ddf3fd155 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -193,7 +193,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { } } - /** @return non-null */ + /** @return A copy, non-null */ List getSessions() { return new ArrayList(_sessions); } @@ -208,6 +208,10 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { _sessions.remove(session); } + /** + * Generic options used for clients and servers + * @return not a copy + */ public Properties getClientOptions() { return _clientOptions; } private void addtask(I2PTunnelTask tsk) { @@ -327,11 +331,13 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { /** * Configure the extra I2CP options to use in any subsequent I2CP sessions. + * Generic options used for clients and servers * Usage: "clientoptions[ key=value]*" . * * Sets the event "clientoptions_onResult" = "ok" after completion. * - * Deprecated use setClientOptions() + * Deprecated To be made private, use setClientOptions(). + * This does NOT update a running TunnelTask. * * @param args each args[i] is a key=value pair to add to the options * @param l logger to receive events and output @@ -351,17 +357,22 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { } /** - * A more efficient runClientOptions() + * Generic options used for clients and servers. + * This DOES update a running TunnelTask, but NOT the session. + * A more efficient runClientOptions(). * * @param opts non-null * @since 0.9.1 */ public void setClientOptions(Properties opts) { - _clientOptions.clear(); - for (Map.Entry e : opts.entrySet()) { - String key = (String) e.getKey(); - String val = (String) e.getValue(); - _clientOptions.setProperty(key, val); + for (Iterator iter = _clientOptions.keySet().iterator(); iter.hasNext();) { + Object key = iter.next(); + if (!opts.containsKey(key)) + iter.remove(); + } + _clientOptions.putAll(opts); + for (I2PTunnelTask task : tasks) { + task.optionsUpdated(this); } notifyEvent("clientoptions_onResult", "ok"); } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index aca83e490..916550dcf 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -495,6 +495,22 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna return opts; } + /** + * Update the I2PSocketManager. + * + * @since 0.9.1 + */ + @Override + public void optionsUpdated(I2PTunnel tunnel) { + if (getTunnel() != tunnel) + return; + I2PSocketManager sm = _ownDest ? sockMgr : socketManager; + if (sm == null) + return; + Properties props = tunnel.getClientOptions(); + sm.setDefaultOptions(sockMgr.buildOptions(props)); + } + /** * Create a new I2PSocket towards to the specified destination, * adding it to the list of connections actually managed by this diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java index 60d6abf3f..059823980 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java @@ -295,6 +295,19 @@ public class I2PTunnelServer extends I2PTunnelTask implements Runnable { } } + /** + * Update the I2PSocketManager. + * + * @since 0.9.1 + */ + @Override + public void optionsUpdated(I2PTunnel tunnel) { + if (getTunnel() != tunnel || sockMgr == null) + return; + Properties props = tunnel.getClientOptions(); + sockMgr.setDefaultOptions(sockMgr.buildOptions(props)); + } + protected int getHandlerCount() { int rv = DEFAULT_HANDLER_COUNT; String cnt = getTunnel().getClientOptions().getProperty(PROP_HANDLER_COUNT); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelTask.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelTask.java index 02d384850..4931168b3 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelTask.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelTask.java @@ -58,6 +58,15 @@ public abstract class I2PTunnelTask extends EventDispatcherImpl { public abstract boolean close(boolean forced); + /** + * Notify the task that I2PTunnel's options have been updated. + * Extending classes should override and call I2PTunnel.getClientOptions(), + * then update the I2PSocketManager. + * + * @since 0.9.1 + */ + public void optionsUpdated(I2PTunnel tunnel) {} + /** * For tasks that don't call I2PTunnel.addSession() directly * @since 0.8.13 diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 2386a9dbe..5baef65c2 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -328,6 +328,7 @@ public class TunnelController implements Logging { _log.info("Releasing session " + s); TunnelControllerGroup.getInstance().release(this, s); } + // _sessions.clear() ???? } else { if (_log.shouldLog(Log.WARN)) _log.warn("No sessions to release? for " + getName()); @@ -386,7 +387,13 @@ public class TunnelController implements Logging { } } - private void setSessionOptions() { + /** + * These are the ones stored with a prefix of "option." + * + * @return keys with the "option." prefix stripped + * @since 0.9.1 Much better than getClientOptions() + */ + public Properties getClientOptionProps() { Properties opts = new Properties(); for (Map.Entry e : _config.entrySet()) { String key = (String) e.getKey(); @@ -396,7 +403,11 @@ public class TunnelController implements Logging { opts.setProperty(key, val); } } - _tunnel.setClientOptions(opts); + return opts; + } + + private void setSessionOptions() { + _tunnel.setClientOptions(getClientOptionProps()); } private void setI2CPOptions() { @@ -430,25 +441,37 @@ public class TunnelController implements Logging { startTunnel(); } + /** + * As of 0.9.1, updates the options on an existing session + */ public void setConfig(Properties config, String prefix) { Properties props = new Properties(); - for (Iterator iter = config.keySet().iterator(); iter.hasNext(); ) { - String key = (String)iter.next(); - String val = config.getProperty(key); + for (Map.Entry e : config.entrySet()) { + String key = (String) e.getKey(); if (key.startsWith(prefix)) { key = key.substring(prefix.length()); + String val = (String) e.getValue(); props.setProperty(key, val); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Set prop [" + key + "] to [" + val + "]"); } } _config = props; + // tell i2ptunnel, who will tell the TunnelTask, who will tell the SocketManager + setSessionOptions(); + if (_running && _sessions != null) { + for (I2PSession s : _sessions) { + // tell the router via the session + if (!s.isClosed()) { + s.updateOptions(_tunnel.getClientOptions()); + } + } + } } + public Properties getConfig(String prefix) { Properties rv = new Properties(); - for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) { - String key = (String)iter.next(); - String val = _config.getProperty(key); + for (Map.Entry e : _config.entrySet()) { + String key = (String) e.getKey(); + String val = (String) e.getValue(); rv.setProperty(prefix + key, val); } return rv; @@ -459,19 +482,27 @@ public class TunnelController implements Logging { public String getDescription() { return _config.getProperty("description"); } public String getI2CPHost() { return _config.getProperty("i2cpHost"); } public String getI2CPPort() { return _config.getProperty("i2cpPort"); } + + /** + * These are the ones with a prefix of "option." + * + * @return one big string of "key=val key=val ..." + * @deprecated why would you want this? Use getClientOptionProps() instead + */ public String getClientOptions() { StringBuilder opts = new StringBuilder(64); - for (Iterator iter = _config.keySet().iterator(); iter.hasNext(); ) { - String key = (String)iter.next(); - String val = _config.getProperty(key); + for (Map.Entry e : _config.entrySet()) { + String key = (String) e.getKey(); if (key.startsWith("option.")) { key = key.substring("option.".length()); + String val = (String) e.getValue(); if (opts.length() > 0) opts.append(' '); opts.append(key).append('=').append(val); } } return opts.toString(); } + public String getListenOnInterface() { return _config.getProperty("interface"); } public String getTargetHost() { return _config.getProperty("targetHost"); } public String getTargetPort() { return _config.getProperty("targetPort"); } @@ -485,6 +516,7 @@ public class TunnelController implements Logging { /** default true */ public boolean getStartOnLoad() { return Boolean.valueOf(_config.getProperty("startOnLoad", "true")).booleanValue(); } public boolean getPersistentClientKey() { return Boolean.valueOf(_config.getProperty("option.persistentClientKey")).booleanValue(); } + public String getMyDestination() { if (_tunnel != null) { List sessions = _tunnel.getSessions(); 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 935abdd2f..172e3eb57 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/EditBean.java @@ -376,18 +376,6 @@ public class EditBean extends IndexBean { */ private static Properties getOptions(TunnelController controller) { if (controller == null) return null; - String opts = controller.getClientOptions(); - StringTokenizer tok = new StringTokenizer(opts); - Properties props = new Properties(); - while (tok.hasMoreTokens()) { - String pair = tok.nextToken(); - int eq = pair.indexOf('='); - if ( (eq <= 0) || (eq >= pair.length()) ) - continue; - String key = pair.substring(0, eq); - String val = pair.substring(eq+1); - props.setProperty(key, val); - } - return props; + return controller.getClientOptionProps(); } } diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java index 1651d40d2..eaa5b667f 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java @@ -38,11 +38,32 @@ public interface I2PSocketManager { */ public void setAcceptTimeout(long ms); public long getAcceptTimeout(); + + /** + * Update the options on a running socket manager. + * Parameters in the I2PSocketOptions interface may be changed directly + * with the setters; no need to use this method for those. + * This does NOT update the underlying I2CP or tunnel options; use getSession().updateOptions() for that. + * @param options as created from a call to buildOptions(properties), non-null + */ public void setDefaultOptions(I2PSocketOptions options); + + /** + * Current options, not a copy, setters may be used to make changes. + */ public I2PSocketOptions getDefaultOptions(); + public I2PServerSocket getServerSocket(); + /** + * Create a copy of the current options, to be used in a setDefaultOptions() call. + */ public I2PSocketOptions buildOptions(); + + /** + * Create a modified copy of the current options, to be used in a setDefaultOptions() call. + * @param opts The new options, may be null + */ public I2PSocketOptions buildOptions(Properties opts); /** @@ -102,6 +123,10 @@ public interface I2PSocketManager { public String getName(); public void setName(String name); + /** + * Deprecated - Factory will initialize. + * @throws UnsupportedOperationException always + */ public void init(I2PAppContext context, I2PSession session, Properties opts, String name); public void addDisconnectListener(DisconnectListener lsnr); 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 57f5f7bb4..113d1e7ff 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java @@ -4,6 +4,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.Constructor; import java.util.Iterator; import java.util.Properties; @@ -161,40 +162,19 @@ public class I2PSocketManagerFactory { } private static I2PSocketManager createManager(I2PSession session, Properties opts, String name) { - //if (false) { - //I2PSocketManagerImpl mgr = new I2PSocketManagerImpl(); - //mgr.setSession(session); - //mgr.setDefaultOptions(new I2PSocketOptions()); - //return mgr; - //} else { - String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER); - if (classname != null) { - try { - Class cls = Class.forName(classname); - Object obj = cls.newInstance(); - if (obj instanceof I2PSocketManager) { - I2PSocketManager mgr = (I2PSocketManager)obj; - I2PAppContext context = I2PAppContext.getGlobalContext(); - mgr.init(context, session, opts, name); - return mgr; - } else { - throw new IllegalStateException("Invalid manager class [" + classname + "]"); - } - } catch (ClassNotFoundException cnfe) { - _log.error("Error loading " + classname, cnfe); - throw new IllegalStateException("Invalid manager class [" + classname + "] - not found"); - } catch (InstantiationException ie) { - _log.error("Error loading " + classname, ie); - throw new IllegalStateException("Invalid manager class [" + classname + "] - unable to instantiate"); - } catch (IllegalAccessException iae) { - _log.error("Error loading " + classname, iae); - throw new IllegalStateException("Invalid manager class [" + classname + "] - illegal access"); - } - } else { - throw new IllegalStateException("No manager class specified"); - } - //} - + I2PAppContext context = I2PAppContext.getGlobalContext(); + String classname = opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER); + try { + Class cls = Class.forName(classname); + Constructor con = (Constructor) + cls.getConstructor(new Class[] {I2PAppContext.class, I2PSession.class, Properties.class, String.class}); + I2PSocketManager mgr = con.newInstance(new Object[] {context, session, opts, name}); + return mgr; + } catch (Throwable t) { + _log.log(Log.CRIT, "Error loading " + classname, t); + throw new IllegalStateException(t); + } + } private static String getHost() { 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 e6ec6fa42..7f8616b3e 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/ConnectionOptions.java @@ -237,7 +237,33 @@ class ConnectionOptions extends I2PSocketOptionsImpl { */ public ConnectionOptions(ConnectionOptions opts) { super(opts); - if (opts != null) { + if (opts != null) + update(opts); + } + + /** + * Update everything by copying over from opts + * @param opts non-null + * @since 0.9.1 + */ + public void updateAll(ConnectionOptions opts) { + // user is unlikely to change these 6 between buildOptions() and setDefaultOptions(), + // since they may be updated directly, but just in case... + setConnectTimeout(opts.getConnectTimeout()); + setReadTimeout(opts.getReadTimeout()); + setWriteTimeout(opts.getWriteTimeout()); + setMaxBufferSize(opts.getMaxBufferSize()); + setLocalPort(opts.getLocalPort()); + setPort(opts.getPort()); + update(opts); + } + + /** + * Update everything (except super) by copying over from opts + * @param opts non-null + * @since 0.9.1 + */ + private void update(ConnectionOptions opts) { setMaxWindowSize(opts.getMaxWindowSize()); setConnectDelay(opts.getConnectDelay()); setProfile(opts.getProfile()); @@ -265,7 +291,6 @@ class ConnectionOptions extends I2PSocketOptionsImpl { _maxTotalConnsPerMinute = opts.getMaxTotalConnsPerMinute(); _maxTotalConnsPerHour = opts.getMaxTotalConnsPerHour(); _maxTotalConnsPerDay = opts.getMaxTotalConnsPerDay(); - } } @Override @@ -301,6 +326,9 @@ class ConnectionOptions extends I2PSocketOptionsImpl { _maxTotalConnsPerDay = getInt(opts, PROP_MAX_TOTAL_CONNS_DAY, 0); } + /** + * Note: NOT part of the interface + */ @Override public void setProperties(Properties opts) { super.setProperties(opts); @@ -611,30 +639,35 @@ class ConnectionOptions extends I2PSocketOptionsImpl { private void initLists(Properties opts) { _accessListEnabled = getBool(opts, PROP_ENABLE_ACCESS_LIST, false); _blackListEnabled = getBool(opts, PROP_ENABLE_BLACKLIST, false); + // Don't think these would ever be accessed simultaneously, + // but avoid concurrent modification just in case + Set accessList, blackList; if (_accessListEnabled) - _accessList = new HashSet(); + accessList = new HashSet(); else - _accessList = Collections.EMPTY_SET; + accessList = Collections.EMPTY_SET; if (_blackListEnabled) - _blackList = new HashSet(); + blackList = new HashSet(); else - _blackList = Collections.EMPTY_SET; - if (!(_accessListEnabled || _blackListEnabled)) - return; - String hashes = opts.getProperty(PROP_ACCESS_LIST); - if (hashes == null) - return; - StringTokenizer tok = new StringTokenizer(hashes, ", "); - while (tok.hasMoreTokens()) { - String hashstr = tok.nextToken(); - Hash h = ConvertToHash.getHash(hashstr); - if (h == null) - error("bad list hash: " + hashstr); - else if (_blackListEnabled) - _blackList.add(h); - else - _accessList.add(h); + blackList = Collections.EMPTY_SET; + if (_accessListEnabled || _blackListEnabled) { + String hashes = opts.getProperty(PROP_ACCESS_LIST); + if (hashes == null) + return; + StringTokenizer tok = new StringTokenizer(hashes, ", "); + while (tok.hasMoreTokens()) { + String hashstr = tok.nextToken(); + Hash h = ConvertToHash.getHash(hashstr); + if (h == null) + error("bad list hash: " + hashstr); + else if (_blackListEnabled) + blackList.add(h); + else + accessList.add(h); + } } + _accessList = accessList; + _blackList = blackList; if (_accessListEnabled && _accessList.isEmpty()) error("Connection access list enabled but no valid entries; no peers can connect"); else if (_blackListEnabled && _blackList.isEmpty()) @@ -647,9 +680,10 @@ class ConnectionOptions extends I2PSocketOptionsImpl { log.error(s); } - @Override + /** doesn't include everything */ + @Override public String toString() { - StringBuilder buf = new StringBuilder(128); + StringBuilder buf = new StringBuilder(256); buf.append("conDelay=").append(_connectDelay); buf.append(" maxSize=").append(_maxMessageSize); buf.append(" rtt=").append(_rtt); @@ -663,6 +697,14 @@ class ConnectionOptions extends I2PSocketOptionsImpl { buf.append(" inactivityTimeout=").append(_inactivityTimeout); buf.append(" inboundBuffer=").append(_inboundBufferSize); buf.append(" maxWindowSize=").append(_maxWindowSize); + buf.append(" blacklistSize=").append(_blackList.size()); + buf.append(" whitelistSize=").append(_accessList.size()); + buf.append(" maxConns=").append(_maxConnsPerMinute).append('/') + .append(_maxConnsPerHour).append('/') + .append(_maxConnsPerDay); + buf.append(" maxTotalConns=").append(_maxTotalConnsPerMinute).append('/') + .append(_maxTotalConnsPerHour).append('/') + .append(_maxTotalConnsPerDay); return buf.toString(); } diff --git a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java index eddfec953..cfc0bf783 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/I2PSocketManagerFull.java @@ -28,28 +28,46 @@ import net.i2p.util.Log; * Direct instantiation by others is deprecated. */ public class I2PSocketManagerFull implements I2PSocketManager { - private I2PAppContext _context; - private Log _log; - private I2PSession _session; - private I2PServerSocketFull _serverSocket; + private final I2PAppContext _context; + private final Log _log; + private final I2PSession _session; + private final I2PServerSocketFull _serverSocket; private StandardServerSocket _realServerSocket; - private ConnectionOptions _defaultOptions; + private final ConnectionOptions _defaultOptions; private long _acceptTimeout; private String _name; private int _maxStreams; private static int __managerId = 0; - private ConnectionManager _connectionManager; + private final ConnectionManager _connectionManager; /** * How long to wait for the client app to accept() before sending back CLOSE? * This includes the time waiting in the queue. Currently set to 5 seconds. */ private static final long ACCEPT_TIMEOUT_DEFAULT = 5*1000; - + + /** + * @deprecated use 4-arg constructor + * @throws UnsupportedOperationException always + */ public I2PSocketManagerFull() { + throw new UnsupportedOperationException(); + } + + /** how many streams will we allow at once? */ + public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams"; + + /** + * @deprecated use 4-arg constructor + * @throws UnsupportedOperationException always + */ + public void init(I2PAppContext context, I2PSession session, Properties opts, String name) { + throw new UnsupportedOperationException(); } /** + * This is what I2PSocketManagerFactory.createManager() returns. + * Direct instantiation by others is deprecated. * * @param context * @param session @@ -57,22 +75,6 @@ public class I2PSocketManagerFull implements I2PSocketManager { * @param name */ public I2PSocketManagerFull(I2PAppContext context, I2PSession session, Properties opts, String name) { - this(); - init(context, session, opts, name); - } - - /** how many streams will we allow at once? */ - public static final String PROP_MAX_STREAMS = "i2p.streaming.maxConcurrentStreams"; - - /** - * - * - * @param context - * @param session - * @param opts - * @param name - */ - public void init(I2PAppContext context, I2PSession session, Properties opts, String name) { _context = context; _session = session; _log = _context.logManager().getLog(I2PSocketManagerFull.class); @@ -98,7 +100,15 @@ public class I2PSocketManagerFull implements I2PSocketManager { } } + /** + * Create a copy of the current options, to be used in a setDefaultOptions() call. + */ public I2PSocketOptions buildOptions() { return buildOptions(null); } + + /** + * Create a modified copy of the current options, to be used in a setDefaultOptions() call. + * @param opts The new options, may be null + */ public I2PSocketOptions buildOptions(Properties opts) { ConnectionOptions curOpts = new ConnectionOptions(_defaultOptions); curOpts.setProperties(opts); @@ -159,10 +169,24 @@ public class I2PSocketManagerFull implements I2PSocketManager { public void setAcceptTimeout(long ms) { _acceptTimeout = ms; } public long getAcceptTimeout() { return _acceptTimeout; } + /** + * Update the options on a running socket manager. + * Parameters in the I2PSocketOptions interface may be changed directly + * with the setters; no need to use this method for those. + * This does NOT update the underlying I2CP or tunnel options; use getSession().updateOptions() for that. + * @param options as created from a call to buildOptions(properties), non-null + */ public void setDefaultOptions(I2PSocketOptions options) { - _defaultOptions = new ConnectionOptions((ConnectionOptions) options); + if (!(options instanceof ConnectionOptions)) + throw new IllegalArgumentException(); + if (_log.shouldLog(Log.WARN)) + _log.warn("Changing options from:\n " + _defaultOptions + "\nto:\n " + options); + _defaultOptions.updateAll((ConnectionOptions) options); } + /** + * Current options, not a copy, setters may be used to make changes. + */ public I2PSocketOptions getDefaultOptions() { return _defaultOptions; } From 6c5902837caa328efd9bced406ed526936bb3430 Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 14 Jun 2012 19:48:12 +0000 Subject: [PATCH 06/16] * NetDB: Only publish stats every so often, to improve anonymity while preserving the ability to monitor the network (effective next release) --- router/java/src/net/i2p/router/StatisticsManager.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index 56cd73b5d..5cce9d233 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -30,6 +30,8 @@ public class StatisticsManager implements Service { private final RouterContext _context; public final static String PROP_PUBLISH_RANKINGS = "router.publishPeerRankings"; + /** enhance anonymity by only including build stats one out of this many times */ + private static final int RANDOM_INCLUDE_STATS = 4; private final DecimalFormat _fmt; private final DecimalFormat _pct; @@ -82,11 +84,13 @@ public class StatisticsManager implements Service { stats.setProperty("stat_identities", newlines+""); ***/ - if (_context.getBooleanPropertyDefaultTrue(PROP_PUBLISH_RANKINGS)) { + if (_context.getBooleanPropertyDefaultTrue(PROP_PUBLISH_RANKINGS) && + (CoreVersion.VERSION.equals("0.9") || _context.random().nextInt(RANDOM_INCLUDE_STATS) == 0)) { long publishedUptime = _context.router().getUptime(); // Don't publish these for first hour - if (publishedUptime > 62*60*1000 && CoreVersion.VERSION.equals("0.8.13")) - includeAverageThroughput(stats); + // Disabled in 0.9 + //if (publishedUptime > 62*60*1000) + // includeAverageThroughput(stats); //includeRate("router.invalidMessageTime", stats, new long[] { 10*60*1000 }); //includeRate("router.duplicateMessageId", stats, new long[] { 24*60*60*1000 }); //includeRate("tunnel.duplicateIV", stats, new long[] { 24*60*60*1000 }); From 3e192cc57e2c105950d0d40fedb245a5ace4df9b Mon Sep 17 00:00:00 2001 From: zzz Date: Thu, 14 Jun 2012 22:07:28 +0000 Subject: [PATCH 07/16] remove text about restart --- apps/i2ptunnel/jsp/editClient.jsp | 2 -- apps/i2ptunnel/jsp/editServer.jsp | 2 -- 2 files changed, 4 deletions(-) diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index 147162ca0..7aef0ab31 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -497,8 +497,6 @@