diff --git a/apps/i2psnark/java/src/org/klomp/snark/Storage.java b/apps/i2psnark/java/src/org/klomp/snark/Storage.java index efae9baad00fc11162f3157fba1f4f2e9d1d0781..edcab93cbc7b6b11362628d108b7c8dcab84db07 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Storage.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Storage.java @@ -74,7 +74,7 @@ public class Storage /** The default piece size. */ private static final int DEFAULT_PIECE_SIZE = 256*1024; /** bigger than this will be rejected */ - public static final int MAX_PIECE_SIZE = 4*1024*1024; + public static final int MAX_PIECE_SIZE = 8*1024*1024; /** The maximum number of pieces in a torrent. */ public static final int MAX_PIECES = 10*1024; public static final long MAX_TOTAL_SIZE = MAX_PIECE_SIZE * (long) MAX_PIECES; @@ -601,7 +601,6 @@ public class Storage * Doesn't really reopen the file descriptors for a restart. * Just does an existence check but no length check or data reverification * - * @param rootDir ignored * @throws IOE on fail */ public void reopen() throws IOException diff --git a/apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java b/apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java index 3a9c3088e08c359e37258b43ccf333773e254158..aae95b5150341fc004e084ada910a66f47090aac 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java +++ b/apps/i2psnark/java/src/org/klomp/snark/web/URIUtil.java @@ -56,6 +56,10 @@ class URIUtil } /** Encode a URI path. + * + * Somewhat oddly, this encodes all chars >= 0x80 if buf is null, (strict RFC 2396) + * but only the control, space, and special chars if buf is non-null. + * * @param path The path the encode * @param buf StringBuilder to encode path into (or null) * @return The StringBuilder or null if no substitutions required. @@ -83,7 +87,7 @@ class URIUtil buf=new StringBuilder(path.length()*2); break loop; default: - if (c>127) + if (c >= 0x7f || c <= 0x1f) { bytes = DataHelper.getUTF8(path); buf=new StringBuilder(path.length()*2); @@ -132,12 +136,12 @@ class URIUtil case ' ': buf.append("%20"); continue; + case 0x7f: + buf.append("%7F"); + continue; default: - if (c<0) - { - buf.append('%'); + if (c <= 0x1f) // includes negative toHex(c,buf); - } else buf.append((char)c); continue; @@ -180,7 +184,10 @@ class URIUtil buf.append("%20"); continue; default: - buf.append(c); + if (c <= 0x1f || (c >= 0x7f && c <= 0x9f) || Character.isSpaceChar(c)) + toHex(c,buf); + else + buf.append(c); continue; } } @@ -195,11 +202,27 @@ class URIUtil */ private static void toHex(byte b, StringBuilder buf) { + buf.append('%'); int d=0xf&((0xF0&b)>>4); buf.append((char)((d>9?('A'-10):'0')+d)); d=0xf&b; buf.append((char)((d>9?('A'-10):'0')+d)); } + + /** + * UTF-8 + */ + private static void toHex(char c, StringBuilder buf) + { + if (c > 0x7f) { + byte[] b = DataHelper.getUTF8(Character.toString(c)); + for (int i = 0; i < b.length; i++) { + toHex(b[i], buf); + } + } else { + toHex((byte) c, buf); + } + } } diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java index 5b06e271b6e5461cf337c350ae183c93b6a1ae03..84e803ff6b8a75674e37396857db13e59305f090 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java @@ -24,6 +24,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; +import javax.net.ssl.SSLServerSocketFactory; + import net.i2p.I2PAppContext; import net.i2p.I2PException; import net.i2p.client.I2PSession; @@ -86,6 +88,8 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna private static int _executorThreadCount; private static final Object _executorLock = new Object(); + public static final String PROP_USE_SSL = I2PTunnelServer.PROP_USE_SSL; + /** * This constructor always starts the tunnel (ignoring the i2cp.delayOpen option). * It is used to add a client to an existing socket manager. @@ -599,7 +603,24 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna } return; } - ss = new ServerSocket(localPort, 0, addr); + Properties opts = getTunnel().getClientOptions(); + boolean useSSL = Boolean.parseBoolean(opts.getProperty(PROP_USE_SSL)); + if (useSSL) { + // was already done in web/IndexBean.java when saving the config + boolean wasCreated = SSLClientUtil.verifyKeyStore(opts); + if (wasCreated) { + // From here, we can't save the config. + // We shouldn't get here, as SSL isn't the default, so it would + // be enabled via the GUI only. + // If it was done manually, the keys will be regenerated at every startup, + // which is bad. + _log.logAlways(Log.WARN, "Created new i2ptunnel SSL keys but can't save the config, disable and enable via i2ptunnel GUI"); + } + SSLServerSocketFactory fact = SSLClientUtil.initializeFactory(opts); + ss = fact.createServerSocket(localPort, 0, addr); + } else { + ss = new ServerSocket(localPort, 0, addr); + } // If a free port was requested, find out what we got if (localPort == 0) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/SSLClientUtil.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/SSLClientUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..01e7c4791922f8cdd300a107eb678b696cf1892f --- /dev/null +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/SSLClientUtil.java @@ -0,0 +1,203 @@ +package net.i2p.i2ptunnel; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.GeneralSecurityException; +import java.util.Properties; + +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLServerSocketFactory; +import javax.net.ssl.SSLContext; + +import net.i2p.I2PAppContext; +import net.i2p.crypto.KeyStoreUtil; +import net.i2p.util.Log; +import net.i2p.util.SecureDirectory; + +/** + * Utilities for I2PTunnel client SSL server sockets. + * + * @since 0.9.15 adopted from net.i2p.router.client.SSLClientListenerRunner + */ +public class SSLClientUtil { + + private static final String PROP_KEYSTORE_PASSWORD = "keystorePassword"; + private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit"; + private static final String PROP_KEY_PASSWORD = "keyPassword"; + private static final String PROP_KEY_ALIAS = "keyAlias"; + private static final String ASCII_KEYFILE_SUFFIX = ".local.crt"; + private static final String PROP_KS_NAME = "keystoreFile"; + private static final String KS_DIR = "keystore"; + private static final String PREFIX = "i2ptunnel-"; + private static final String KS_SUFFIX = ".ks"; + private static final String CERT_DIR = "certificates/i2ptunnel"; + + /** + * Create a new selfsigned cert and keystore and pubkey cert if they don't exist. + * May take a while. + * + * @param opts in/out, updated if rv is true + * @return false if it already exists; if true, caller must save opts + * @throws IOException on creation fail + */ + public static boolean verifyKeyStore(Properties opts) throws IOException { + return verifyKeyStore(opts, ""); + } + + /** + * Create a new selfsigned cert and keystore and pubkey cert if they don't exist. + * May take a while. + * + * @param opts in/out, updated if rv is true + * @param optPfx add this prefix when getting/setting options + * @return false if it already exists; if true, caller must save opts + * @throws IOException on creation fail + */ + public static boolean verifyKeyStore(Properties opts, String optPfx) throws IOException { + String name = opts.getProperty(optPfx + PROP_KEY_ALIAS); + if (name == null) { + name = KeyStoreUtil.randomString(); + opts.setProperty(optPfx + PROP_KEY_ALIAS, name); + } + String ksname = opts.getProperty(optPfx + PROP_KS_NAME); + if (ksname == null) { + ksname = PREFIX + name + KS_SUFFIX; + opts.setProperty(optPfx + PROP_KS_NAME, ksname); + } + File ks = new File(ksname); + if (!ks.isAbsolute()) { + ks = new File(I2PAppContext.getGlobalContext().getConfigDir(), KS_DIR); + ks = new File(ks, ksname); + } + if (ks.exists()) + return false; + File dir = ks.getParentFile(); + if (!dir.exists()) { + File sdir = new SecureDirectory(dir.getAbsolutePath()); + if (!sdir.mkdirs()) + throw new IOException("Unable to create keystore " + ks); + } + boolean rv = createKeyStore(ks, name, opts, optPfx); + if (!rv) + throw new IOException("Unable to create keystore " + ks); + + // Now read it back out of the new keystore and save it in ascii form + // where the clients can get to it. + // Failure of this part is not fatal. + exportCert(ks, name, opts, optPfx); + return true; + } + + + /** + * Call out to keytool to create a new keystore with a keypair in it. + * + * @param name used in CNAME + * @param opts in/out, updated if rv is true, must contain PROP_KEY_ALIAS + * @param optPfx add this prefix when getting/setting options + * @return success, if true, opts will have password properties added to be saved + */ + private static boolean createKeyStore(File ks, String name, Properties opts, String optPfx) { + // make a random 48 character password (30 * 8 / 5) + String keyPassword = KeyStoreUtil.randomString(); + // and one for the cname + String cname = name + ".i2ptunnel.i2p.net"; + + String keyName = opts.getProperty(optPfx + PROP_KEY_ALIAS); + boolean success = KeyStoreUtil.createKeys(ks, keyName, cname, "I2PTUNNEL", keyPassword); + if (success) { + success = ks.exists(); + if (success) { + opts.setProperty(optPfx + PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD); + opts.setProperty(optPfx + PROP_KEY_PASSWORD, keyPassword); + } + } + if (success) { + logAlways("Created self-signed certificate for " + cname + " in keystore: " + ks.getAbsolutePath() + "\n" + + "The certificate name was generated randomly, and is not associated with your " + + "IP address, host name, router identity, or destination keys."); + } else { + error("Failed to create I2PTunnel SSL keystore.\n" + + "If you create the keystore manually, you must add " + optPfx + PROP_KEYSTORE_PASSWORD + " and " + optPfx + PROP_KEY_PASSWORD + + " to " + (new File(I2PAppContext.getGlobalContext().getConfigDir(), "i2ptunnel.config")).getAbsolutePath()); + } + return success; + } + + /** + * Pull the cert back OUT of the keystore and save it as ascii + * so the clients can get to it. + * + * @param name used to generate output file name + * @param opts must contain optPfx + PROP_KEY_ALIAS + * @param optPfx add this prefix when getting options + */ + private static void exportCert(File ks, String name, Properties opts, String optPfx) { + File sdir = new SecureDirectory(I2PAppContext.getGlobalContext().getConfigDir(), CERT_DIR); + if (sdir.exists() || sdir.mkdirs()) { + String keyAlias = opts.getProperty(optPfx + PROP_KEY_ALIAS); + String ksPass = opts.getProperty(optPfx + PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD); + File out = new File(sdir, PREFIX + name + ASCII_KEYFILE_SUFFIX); + boolean success = KeyStoreUtil.exportCert(ks, ksPass, keyAlias, out); + if (!success) + error("Error getting SSL cert to save as ASCII"); + } else { + error("Error saving ASCII SSL keys"); + } + } + + /** + * Sets up the SSLContext and sets the socket factory. + * No option prefix allowed. + * + * @throws IOException; GeneralSecurityExceptions are wrapped in IOE for convenience + * @return factory, throws on all errors + */ + public static SSLServerSocketFactory initializeFactory(Properties opts) throws IOException { + String ksPass = opts.getProperty(PROP_KEYSTORE_PASSWORD, DEFAULT_KEYSTORE_PASSWORD); + String keyPass = opts.getProperty(PROP_KEY_PASSWORD); + if (keyPass == null) { + throw new IOException("No key password, set " + PROP_KEY_PASSWORD + " in " + + (new File(I2PAppContext.getGlobalContext().getConfigDir(), "i2ptunnel.config")).getAbsolutePath()); + } + String ksname = opts.getProperty(PROP_KS_NAME); + if (ksname == null) { + throw new IOException("No keystore, set " + PROP_KS_NAME + " in " + + (new File(I2PAppContext.getGlobalContext().getConfigDir(), "i2ptunnel.config")).getAbsolutePath()); + } + File ks = new File(ksname); + if (!ks.isAbsolute()) { + ks = new File(I2PAppContext.getGlobalContext().getConfigDir(), KS_DIR); + ks = new File(ks, ksname); + } + + InputStream fis = null; + try { + SSLContext sslc = SSLContext.getInstance("TLS"); + KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + fis = new FileInputStream(ks); + keyStore.load(fis, ksPass.toCharArray()); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(keyStore, keyPass.toCharArray()); + sslc.init(kmf.getKeyManagers(), null, I2PAppContext.getGlobalContext().random()); + return sslc.getServerSocketFactory(); + } catch (GeneralSecurityException gse) { + IOException ioe = new IOException("keystore error"); + ioe.initCause(gse); + throw ioe; + } finally { + if (fis != null) try { fis.close(); } catch (IOException ioe) {} + } + } + + private static void error(String s) { + I2PAppContext.getGlobalContext().logManager().getLog(SSLClientUtil.class).error(s); + } + + private static void logAlways(String s) { + I2PAppContext.getGlobalContext().logManager().getLog(SSLClientUtil.class).logAlways(Log.INFO, s); + } +} diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Pinger.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Pinger.java index dd7f5642d460e27fa04420d135659a3cb8fdab19..b46765287675c4bd2521d46ad511d8472f6b35dc 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Pinger.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/streamr/Pinger.java @@ -1,6 +1,7 @@ package net.i2p.i2ptunnel.streamr; import net.i2p.i2ptunnel.udp.*; +import net.i2p.util.I2PAppThread; /** * @@ -9,7 +10,7 @@ import net.i2p.i2ptunnel.udp.*; public class Pinger implements Source, Runnable { public Pinger() { - this.thread = new Thread(this); + this.thread = new I2PAppThread(this); } public void setSink(Sink sink) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSource.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSource.java index 68a9d90a27f353f3509723b73074f9b530eb7912..2f59418a526c90239ff204244de4823b13f584c3 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSource.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/I2PSource.java @@ -6,6 +6,7 @@ import java.util.concurrent.BlockingQueue; import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionListener; import net.i2p.client.datagram.I2PDatagramDissector; +import net.i2p.util.I2PAppThread; /** * @@ -33,7 +34,7 @@ public class I2PSource implements Source, Runnable { this.sess.setSessionListener(new Listener()); // create thread - this.thread = new Thread(this); + this.thread = new I2PAppThread(this); } public void setSink(Sink sink) { diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java index 6f1a20e5ffd813c63c6bcafee78caa08f8ed1984..23d4b8048bc49fc6b6bf8749a513b0fe2ebefe2b 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udp/UDPSource.java @@ -3,6 +3,8 @@ package net.i2p.i2ptunnel.udp; import java.net.DatagramSocket; import java.net.DatagramPacket; +import net.i2p.util.I2PAppThread; + /** * * @author welterde @@ -19,13 +21,13 @@ public class UDPSource implements Source, Runnable { } // create thread - this.thread = new Thread(this); + this.thread = new I2PAppThread(this); } /** use socket from UDPSink */ public UDPSource(DatagramSocket sock) { this.sock = sock; - this.thread = new Thread(this); + this.thread = new I2PAppThread(this); } public void setSink(Sink sink) { 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 06558d78e572e23c69bbe9694eab0adf54e00cfe..95c5bea682743f786e337c0a091ccbc0019acb74 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -26,15 +26,18 @@ import net.i2p.app.ClientAppManager; import net.i2p.app.Outproxy; import net.i2p.client.I2PClient; import net.i2p.data.Certificate; +import net.i2p.data.DataHelper; import net.i2p.data.Destination; import net.i2p.data.PrivateKeyFile; import net.i2p.data.SessionKey; +import net.i2p.i2ptunnel.I2PTunnelClientBase; import net.i2p.i2ptunnel.I2PTunnelConnectClient; import net.i2p.i2ptunnel.I2PTunnelHTTPClient; import net.i2p.i2ptunnel.I2PTunnelHTTPClientBase; import net.i2p.i2ptunnel.I2PTunnelHTTPServer; import net.i2p.i2ptunnel.I2PTunnelIRCClient; import net.i2p.i2ptunnel.I2PTunnelServer; +import net.i2p.i2ptunnel.SSLClientUtil; import net.i2p.i2ptunnel.TunnelController; import net.i2p.i2ptunnel.TunnelControllerGroup; import net.i2p.util.Addresses; @@ -255,7 +258,7 @@ public class IndexBean { // give the messages a chance to make it to the window try { Thread.sleep(1000); } catch (InterruptedException ie) {} // and give them something to look at in any case - return _("Starting tunnel") + ' ' + getTunnelName(_tunnel) + "…"; + return _("Starting tunnel") + ' ' + getTunnelName(_tunnel) + "..."; } private String stop() { @@ -268,7 +271,7 @@ public class IndexBean { // give the messages a chance to make it to the window try { Thread.sleep(1000); } catch (InterruptedException ie) {} // and give them something to look at in any case - return _("Stopping tunnel") + ' ' + getTunnelName(_tunnel) + "…"; + return _("Stopping tunnel") + ' ' + getTunnelName(_tunnel) + "..."; } private String saveChanges() { @@ -276,7 +279,27 @@ public class IndexBean { TunnelController cur = getController(_tunnel); Properties config = getConfig(); - + + String ksMsg = null; + String type = config.getProperty(TunnelController.PROP_TYPE); + if (TunnelController.TYPE_STD_CLIENT.equals(type) || TunnelController.TYPE_IRC_CLIENT.equals(type)) { + // + // If we switch to SSL, create the keystore here, so we can store the new properties. + // Down in I2PTunnelClientBase it's very hard to save the config. + // + if (Boolean.parseBoolean(config.getProperty(OPT + I2PTunnelClientBase.PROP_USE_SSL))) { + try { + boolean created = SSLClientUtil.verifyKeyStore(config, OPT); + if (created) { + // config now contains new keystore props + ksMsg = "Created new self-signed certificate for tunnel " + getTunnelName(_tunnel); + } + } catch (IOException ioe) { + ksMsg = "Failed to create new self-signed certificate for tunnel " + + getTunnelName(_tunnel) + ", check logs: " + ioe; + } + } + } if (cur == null) { // creating new cur = new TunnelController(config, "", true); @@ -327,6 +350,8 @@ public class IndexBean { } List<String> msgs = doSave(); + if (ksMsg != null) + msgs.add(ksMsg); return getMessages(msgs); } @@ -397,6 +422,7 @@ public class IndexBean { * Executes any action requested (start/stop/etc) and dump out the * messages. * + * @return HTML escaped */ public String getMessages() { if (_group == null) @@ -405,13 +431,14 @@ public class IndexBean { StringBuilder buf = new StringBuilder(512); if (_action != null) { try { - buf.append(processAction()).append("\n"); + buf.append(processAction()).append('\n'); } catch (Exception e) { _log.log(Log.CRIT, "Error processing " + _action, e); + buf.append("Error: ").append(e.toString()).append('\n'); } } getMessages(_group.clearAllMessages(), buf); - return buf.toString(); + return DataHelper.escapeHTML(buf.toString()); } //// @@ -1293,7 +1320,8 @@ public class IndexBean { I2PTunnelIRCClient.PROP_DCC }; private static final String _booleanClientOpts[] = { - "i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume", "persistentClientKey", "i2cp.delayOpen" + "i2cp.reduceOnIdle", "i2cp.closeOnIdle", "i2cp.newDestOnResume", "persistentClientKey", "i2cp.delayOpen", + I2PTunnelClientBase.PROP_USE_SSL, }; private static final String _booleanProxyOpts[] = { I2PTunnelHTTPClientBase.PROP_OUTPROXY_AUTH, diff --git a/apps/i2ptunnel/jsp/editClient.jsp b/apps/i2ptunnel/jsp/editClient.jsp index cbfc8b713033e156f1c9e6a053b474e777685e9f..05c091b197814638d698df6b3f7abdb14b73c31c 100644 --- a/apps/i2ptunnel/jsp/editClient.jsp +++ b/apps/i2ptunnel/jsp/editClient.jsp @@ -140,6 +140,14 @@ input.default { width: 1px; height: 1px; visibility: hidden; } </select> <% } /* streamrclient */ %> </div> + <% if ("client".equals(tunnelType) || "ircclient".equals(tunnelType)) { + %><div id="portField" class="rowItem"> + <label> + <%=intl._("Use SSL?")%> + </label> + <input value="1" type="checkbox" id="startOnLoad" name="useSSL" title="Clients use SSL to connect" <%=(editBean.isSSLEnabled(curTunnel) ? " checked=\"checked\"" : "")%> class="tickbox" /> + </div> + <% } /* tunnel types */ %> <div class="subdivider"> <hr /> diff --git a/apps/i2ptunnel/jsp/index.jsp b/apps/i2ptunnel/jsp/index.jsp index f1117a272a64938d1ce5d711ba137fb93986e956..0b63e98f31257f070bc8c43817b7b86811c7aa08 100644 --- a/apps/i2ptunnel/jsp/index.jsp +++ b/apps/i2ptunnel/jsp/index.jsp @@ -244,6 +244,8 @@ <% String cPort= indexBean.getClientPort2(curClient); out.write(cPort); + if (indexBean.isSSLEnabled(curClient)) + out.write(" SSL"); %> </span> </div> 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 fbeb0e58676bbe63cfe527344f13dede9dae5da1..e943ff6df154c5135affe57a786f8b2896921a8e 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java @@ -25,7 +25,15 @@ import net.i2p.util.Log; */ public class I2PSocketManagerFactory { + /** + * Ignored since 0.9.12, cannot be changed via properties. + * @deprecated + */ public static final String PROP_MANAGER = "i2p.streaming.manager"; + + /** + * The one and only manager. + */ public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.impl.I2PSocketManagerFull"; /** diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index a962230b97125e4bbd03cf14c1b35b11f73acaa4..f4cb8ead123f62afc84d4806c825e4cbb926981c 100644 --- a/core/java/src/net/i2p/data/DataHelper.java +++ b/core/java/src/net/i2p/data/DataHelper.java @@ -1629,8 +1629,8 @@ public class DataHelper { return rv; } - private static final String escapeChars[] = {"&", "\"", "<", ">", "\"", "'"}; - private static final String escapeCodes[] = {"&", """, "<", ">", """, "'"}; + private static final String escapeChars[] = {"&", "\"", "<", ">", "'"}; + private static final String escapeCodes[] = {"&", """, "<", ">", "'"}; /** * Escape a string for inclusion in HTML diff --git a/core/java/src/net/i2p/util/Clock.java b/core/java/src/net/i2p/util/Clock.java index 294d3ffa6d51ff56974f3f0e54d5842fc3be3a7b..25600259c6b786f60f8ba33932b156b1c4830a8d 100644 --- a/core/java/src/net/i2p/util/Clock.java +++ b/core/java/src/net/i2p/util/Clock.java @@ -97,7 +97,7 @@ public class Clock implements Timestamper.UpdateListener { getLog().info("Updating clock offset to " + offsetMs + "ms from " + _offset + "ms"); if (!_statCreated) { - _context.statManager().createRequiredRateStat("clock.skew", "Clock step adjustment (ms)", "Clock", new long[] { 10*60*1000, 3*60*60*1000, 24*60*60*60 }); + _context.statManager().createRequiredRateStat("clock.skew", "Clock step adjustment (ms)", "Clock", new long[] { 10*60*1000, 3*60*60*1000, 24*60*60*1000 }); _statCreated = true; } _context.statManager().addRateData("clock.skew", delta, 0); diff --git a/history.txt b/history.txt index 68e84e941144cb99920831d73a90434f4932c549..3b9c6fdcaf74666ca6dc21a85e40ec2bbe3397b2 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,9 @@ +2014-08-21 zzz + * i2psnark: + - Escape control chars in encodePath() + - Increase max piece size to 8 MB (ticket #1347) + * i2ptunnel: Add local SSL support for std. and IRC client tunnels (ticket #1107) + 2014-08-19 zzz * i2psnark: - Don't filter create torrent form, and diff --git a/router/java/src/net/i2p/router/RouterClock.java b/router/java/src/net/i2p/router/RouterClock.java index f3778dc56ebc0467e41a41b86f6e052c68509517..051d282cdacc5414caf416fe75e812aac411637f 100644 --- a/router/java/src/net/i2p/router/RouterClock.java +++ b/router/java/src/net/i2p/router/RouterClock.java @@ -161,7 +161,7 @@ public class RouterClock extends Clock { getLog().info("Updating target clock offset to " + offsetMs + "ms from " + _offset + "ms, Stratum " + stratum); if (!_statCreated) { - _context.statManager().createRequiredRateStat("clock.skew", "Clock step adjustment (ms)", "Clock", new long[] { 10*60*1000, 3*60*60*1000, 24*60*60*60 }); + _context.statManager().createRequiredRateStat("clock.skew", "Clock step adjustment (ms)", "Clock", new long[] { 10*60*1000, 3*60*60*1000, 24*60*60*1000 }); _statCreated = true; } _context.statManager().addRateData("clock.skew", delta); diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index fecba78d6973d87e9930d631de787e05fec2a510..a6204817e3d9e640632604f1432a96320e1992ce 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 = 7; + public final static long BUILD = 8; /** for example "-test" */ public final static String EXTRA = ""; diff --git a/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java b/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java index 2e49098742a7e5d569870b3c546eb574643e0766..d363d7a3f540cda01e194c7d836aeac93c6d8c5b 100644 --- a/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/BatchedPreprocessor.java @@ -48,6 +48,8 @@ class BatchedPreprocessor extends TrivialPreprocessor { private long _pendingSince; private final String _name; + private static final boolean DEBUG = false; + public BatchedPreprocessor(RouterContext ctx, String name) { super(ctx); _name = name; @@ -175,7 +177,8 @@ class BatchedPreprocessor extends TrivialPreprocessor { + " len=" + cur.getData().length + " alloc=" + allocated); if (timingBuf != null) timingBuf.append(" sent " + cur); - notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), cur.getData().length, cur.getMessageIds(), "flushed allocated"); + if (DEBUG) + notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), cur.getData().length, cur.getMessageIds(), "flushed allocated"); _context.statManager().addRateData("tunnel.batchFragmentation", cur.getFragmentNumber() + 1); _context.statManager().addRateData("tunnel.writeDelay", cur.getLifetime(), cur.getData().length); } @@ -184,7 +187,8 @@ class BatchedPreprocessor extends TrivialPreprocessor { PendingGatewayMessage cur = pending.remove(0); if (timingBuf != null) timingBuf.append(" sent perfect fit " + cur).append("."); - notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), msg.getData().length, msg.getMessageIds(), "flushed tail, remaining: " + pending); + if (DEBUG) + notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), msg.getData().length, msg.getMessageIds(), "flushed tail, remaining: " + pending); _context.statManager().addRateData("tunnel.batchFragmentation", cur.getFragmentNumber() + 1); _context.statManager().addRateData("tunnel.writeDelay", cur.getLifetime(), cur.getData().length); } @@ -234,7 +238,8 @@ class BatchedPreprocessor extends TrivialPreprocessor { if (cur.getOffset() < cur.getData().length) break; pending.remove(0); - notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), cur.getData().length, cur.getMessageIds(), "flushed remaining"); + if (DEBUG) + notePreprocessing(cur.getMessageId(), cur.getFragmentNumber(), cur.getData().length, cur.getMessageIds(), "flushed remaining"); _context.statManager().addRateData("tunnel.batchFragmentation", cur.getFragmentNumber() + 1); _context.statManager().addRateData("tunnel.writeDelay", cur.getLifetime(), cur.getData().length); } @@ -383,9 +388,12 @@ class BatchedPreprocessor extends TrivialPreprocessor { } long msgId = sender.sendPreprocessed(preprocessed, rec); - for (int i = 0; i < pending.size(); i++) { - PendingGatewayMessage cur = pending.get(i); - cur.addMessageId(msgId); + if (DEBUG) { + // creates a list in PGM + for (int i = 0; i < pending.size(); i++) { + PendingGatewayMessage cur = pending.get(i); + cur.addMessageId(msgId); + } } if (_log.shouldLog(Log.DEBUG)) _log.debug("Sent " + startAt + ":" + sendThrough + " out of " + pending + " in message " + msgId); diff --git a/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java b/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java index 33f446893f99a5b521dc5486bee641c5cf7ebc69..3d62ded4bd777d320aafc9e2eb876d1829b5ebb8 100644 --- a/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/BatchedRouterPreprocessor.java @@ -11,8 +11,8 @@ import net.i2p.router.RouterContext; * */ class BatchedRouterPreprocessor extends BatchedPreprocessor { - private TunnelCreatorConfig _config; - protected HopConfig _hopConfig; + private final TunnelCreatorConfig _config; + protected final HopConfig _hopConfig; private final long _sendDelay; /** @@ -34,12 +34,14 @@ class BatchedRouterPreprocessor extends BatchedPreprocessor { public BatchedRouterPreprocessor(RouterContext ctx, TunnelCreatorConfig cfg) { super(ctx, getName(cfg)); _config = cfg; + _hopConfig = null; _sendDelay = initialSendDelay(); } /** for IBGWs */ public BatchedRouterPreprocessor(RouterContext ctx, HopConfig cfg) { super(ctx, getName(cfg)); + _config = null; _hopConfig = cfg; _sendDelay = initialSendDelay(); } diff --git a/router/java/src/net/i2p/router/tunnel/OutboundSender.java b/router/java/src/net/i2p/router/tunnel/OutboundSender.java index 961c29031f610d2bfe0c9f24fc0fa3d4fb9194ca..6125bf48eda35bde3cb98b8ffe74e172522a4c03 100644 --- a/router/java/src/net/i2p/router/tunnel/OutboundSender.java +++ b/router/java/src/net/i2p/router/tunnel/OutboundSender.java @@ -10,30 +10,30 @@ import net.i2p.util.Log; * */ class OutboundSender implements TunnelGateway.Sender { - private final I2PAppContext _context; - private final Log _log; + //private final I2PAppContext _context; + //private final Log _log; private final TunnelCreatorConfig _config; private final OutboundGatewayProcessor _processor; //static final boolean USE_ENCRYPTION = HopProcessor.USE_ENCRYPTION; public OutboundSender(I2PAppContext ctx, TunnelCreatorConfig config) { - _context = ctx; - _log = ctx.logManager().getLog(OutboundSender.class); + //_context = ctx; + //_log = ctx.logManager().getLog(OutboundSender.class); _config = config; - _processor = new OutboundGatewayProcessor(_context, config); + _processor = new OutboundGatewayProcessor(ctx, config); } public long sendPreprocessed(byte[] preprocessed, TunnelGateway.Receiver receiver) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("preprocessed data going out " + _config + ": " + Base64.encode(preprocessed)); + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("preprocessed data going out " + _config + ": " + Base64.encode(preprocessed)); //if (USE_ENCRYPTION) _processor.process(preprocessed, 0, preprocessed.length); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("after wrapping up the preprocessed data on " + _config); + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("after wrapping up the preprocessed data on " + _config); long rv = receiver.receiveEncrypted(preprocessed); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("after receiving on " + _config + ": receiver = " + receiver); + //if (_log.shouldLog(Log.DEBUG)) + // _log.debug("after receiving on " + _config + ": receiver = " + receiver); return rv; } } diff --git a/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java b/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java index 0d8475ece717d50a1d1b27540ecf39da39e6e7ac..19e84dc04d3cf7390ae1f4322de9cba0b23a1e0f 100644 --- a/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java +++ b/router/java/src/net/i2p/router/tunnel/TrivialPreprocessor.java @@ -17,6 +17,9 @@ import net.i2p.util.SimpleByteCache; * optimal throughput. * * See FragmentHandler Javadoc for tunnel message fragment format + * + * Not instantiated directly except in unit tests; see BatchedPreprocessor + * */ class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { protected final RouterContext _context; @@ -48,7 +51,7 @@ class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { * NOTE: Unused here, see BatchedPreprocessor override, super is not called. */ public boolean preprocessQueue(List<PendingGatewayMessage> pending, TunnelGateway.Sender sender, TunnelGateway.Receiver rec) { - throw new IllegalArgumentException("unused, right?"); + throw new UnsupportedOperationException("unused, right?"); } protected void notePreprocessing(long messageId, int numFragments, int totalLength, List<Long> messageIds, String msg) {} @@ -265,7 +268,7 @@ class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { * Does NOT include 4 for the message ID if the message will be fragmented; * call getInstructionAugmentationSize() for that. */ - protected int getInstructionsSize(PendingGatewayMessage msg) { + protected static int getInstructionsSize(PendingGatewayMessage msg) { if (msg.getFragmentNumber() > 0) return 7; // control byte @@ -283,7 +286,7 @@ class TrivialPreprocessor implements TunnelGateway.QueuePreprocessor { } /** @return 0 or 4 */ - protected int getInstructionAugmentationSize(PendingGatewayMessage msg, int offset, int instructionsSize) { + protected static int getInstructionAugmentationSize(PendingGatewayMessage msg, int offset, int instructionsSize) { int payloadLength = msg.getData().length - msg.getOffset(); if (offset + payloadLength + instructionsSize + IV_SIZE + 1 + 4 > PREPROCESSED_SIZE) { // requires fragmentation, so include the messageId diff --git a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java index 242b3dc036747b58d7d3bfbd533f33b105f3d07c..eb1632224d6e316d9b1dc6b95ed3edc1d0850166 100644 --- a/router/java/src/net/i2p/router/tunnel/TunnelGateway.java +++ b/router/java/src/net/i2p/router/tunnel/TunnelGateway.java @@ -33,7 +33,7 @@ import net.i2p.util.SimpleTimer2; * * Unused directly - see PumpedTunnelGateway, ThrottledPumpedTunnelGateway, and TunnelGatewayZeroHop overrides. */ -class TunnelGateway { +abstract class TunnelGateway { protected final RouterContext _context; protected final Log _log; protected final List<PendingGatewayMessage> _queue; diff --git a/router/java/test/junit/net/i2p/router/tunnel/FragmentTest.java b/router/java/test/junit/net/i2p/router/tunnel/FragmentTest.java index 2c0f5e1b401f401e7a69476c9b34fd34d06e26be..ac6b961072cae3fde4f4cd725213f7906fa24d91 100644 --- a/router/java/test/junit/net/i2p/router/tunnel/FragmentTest.java +++ b/router/java/test/junit/net/i2p/router/tunnel/FragmentTest.java @@ -66,8 +66,8 @@ public class FragmentTest { try { pre.preprocessQueue(messages, new SenderImpl(), receiver); - fail("should have thrown IAE"); - } catch (IllegalArgumentException expected){} + fail("should have thrown UOE"); + } catch (UnsupportedOperationException expected){} } /** @@ -89,8 +89,8 @@ public class FragmentTest { try { pre.preprocessQueue(messages, new SenderImpl(), receiver); - fail("should have thrown IAE"); - } catch (IllegalArgumentException expected){} + fail("should have thrown UOE"); + } catch (UnsupportedOperationException expected){} } /**