diff --git a/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java b/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java index 3fc4977f6..d6a113ce6 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java +++ b/apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java @@ -306,7 +306,7 @@ public class MetaInfo if (piece >= 0 && piece < pieces -1) return piece_length; else if (piece == pieces -1) - return (int)(length - piece * piece_length); + return (int)(length - ((long)piece * piece_length)); else throw new IndexOutOfBoundsException("no piece: " + piece); } diff --git a/apps/i2psnark/java/src/org/klomp/snark/Peer.java b/apps/i2psnark/java/src/org/klomp/snark/Peer.java index b31ba65e2..de8d2b1d0 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/Peer.java +++ b/apps/i2psnark/java/src/org/klomp/snark/Peer.java @@ -94,7 +94,7 @@ public class Peer implements Comparable this.infohash = infohash; this.metainfo = metainfo; _id = ++__id; - //_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating")); + //_log.debug("Creating a new peer with " + peerID.toString(), new Exception("creating")); } /** @@ -120,7 +120,7 @@ public class Peer implements Comparable this.peerID = new PeerID(id, sock.getPeerDestination()); _id = ++__id; if (_log.shouldLog(Log.DEBUG)) - _log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id)); + _log.debug("Creating a new peer with " + peerID.toString(), new Exception("creating " + _id)); } /** @@ -216,7 +216,7 @@ public class Peer implements Comparable throw new IllegalStateException("Peer already started"); if (_log.shouldLog(Log.DEBUG)) - _log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting")); + _log.debug("Running connection to " + peerID.toString(), new Exception("connecting")); try { // Do we need to handshake? diff --git a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java index 720a662b3..719edee69 100644 --- a/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java +++ b/apps/i2psnark/java/src/org/klomp/snark/TrackerClient.java @@ -301,7 +301,7 @@ public class TrackerClient extends I2PAppThread // only delay if we actually make an attempt to add peer if(coordinator.addPeer(cur)) { int delay = DELAY_MUL; - delay *= ((int)cur.getPeerID().getAddress().calculateHash().toBase64().charAt(0)) % 10; + delay *= r.nextInt(10); delay += DELAY_MIN; sleptTime += delay; try { Thread.sleep(delay); } catch (InterruptedException ie) {} diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java index 91ac9726f..9a5d6bbf9 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHandler.java @@ -9,6 +9,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import net.i2p.router.client.ClientManagerFacadeImpl; import net.i2p.router.startup.ClientAppConfig; import net.i2p.router.startup.LoadClientAppsJob; @@ -35,6 +36,10 @@ public class ConfigClientsHandler extends FormHandler { saveClientChanges(); return; } + if (_action.equals(_("Save Interface Configuration"))) { + saveInterfaceChanges(); + return; + } if (_action.equals(_("Save WebApp Configuration"))) { saveWebAppChanges(); return; @@ -193,7 +198,7 @@ public class ConfigClientsHandler extends FormHandler { String[] arr = (String[]) _settings.get(key); if (arr == null) return null; - return arr[0]; + return arr[0].trim(); } private void startClient(int i) { @@ -337,4 +342,37 @@ public class ConfigClientsHandler extends FormHandler { _log.error("Error starting plugin " + app, e); } } + + /** + * Handle interface form + * @since 0.8.3 + */ + private void saveInterfaceChanges() { + String port = getJettyString("port"); + if (port != null) + _context.router().setConfigSetting(ClientManagerFacadeImpl.PROP_CLIENT_PORT, port); + String intfc = getJettyString("interface"); + if (intfc != null) + _context.router().setConfigSetting(ClientManagerFacadeImpl.PROP_CLIENT_HOST, intfc); + String user = getJettyString("user"); + if (user != null) + _context.router().setConfigSetting(ConfigClientsHelper.PROP_USER, user); + String pw = getJettyString("pw"); + if (pw != null) + _context.router().setConfigSetting(ConfigClientsHelper.PROP_PW, pw); + String mode = getJettyString("mode"); + boolean disabled = "0".equals(mode); + boolean ssl = "2".equals(mode); + _context.router().setConfigSetting(ConfigClientsHelper.PROP_DISABLE_EXTERNAL, + Boolean.toString(disabled)); + _context.router().setConfigSetting(ConfigClientsHelper.PROP_ENABLE_SSL, + Boolean.toString(ssl)); + _context.router().setConfigSetting(ConfigClientsHelper.PROP_AUTH, + Boolean.toString((_settings.get("auth") != null))); + boolean all = "0.0.0.0".equals(intfc) || "0:0:0:0:0:0:0:0".equals(intfc) || + "::".equals(intfc); + _context.router().setConfigSetting(ConfigClientsHelper.BIND_ALL_INTERFACES, Boolean.toString(all)); + _context.router().saveConfig(); + addFormNotice(_("Interface configuration saved successfully - restart required to take effect.")); + } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java index 049b5a02a..7cd2c8cb7 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigClientsHelper.java @@ -1,6 +1,9 @@ package net.i2p.router.web; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; @@ -8,13 +11,90 @@ import java.util.Properties; import java.util.Set; import java.util.TreeSet; +import net.i2p.router.client.ClientManagerFacadeImpl; import net.i2p.router.startup.ClientAppConfig; +import net.i2p.router.transport.Addresses; public class ConfigClientsHelper extends HelperBase { private String _edit; + /** from ClientListenerRunner */ + public static final String BIND_ALL_INTERFACES = "i2cp.tcp.bindAllInterfaces"; + /** from ClientManager */ + public static final String PROP_DISABLE_EXTERNAL = "i2cp.disableInterface"; + public static final String PROP_ENABLE_SSL = "i2cp.SSL"; + /** from ClientMessageEventListener */ + public static final String PROP_AUTH = "i2cp.auth"; + public static final String PROP_USER = "i2cp.username"; + public static final String PROP_PW = "i2cp.password"; + public ConfigClientsHelper() {} - + + /** @since 0.8.3 */ + public String getPort() { + return _context.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_PORT, + Integer.toString(ClientManagerFacadeImpl.DEFAULT_PORT)); + } + + /** @since 0.8.3 */ + public String getUser() { + return _context.getProperty(PROP_USER, ""); + } + + /** @since 0.8.3 */ + public String getPw() { + return _context.getProperty(PROP_PW, ""); + } + + /** @since 0.8.3 */ + public String i2cpModeChecked(int mode) { + boolean disabled = _context.getBooleanProperty(PROP_DISABLE_EXTERNAL); + boolean ssl = _context.getBooleanProperty(PROP_ENABLE_SSL); + if ((mode == 0 && disabled) || + (mode == 1 && (!disabled) && (!ssl)) || + (mode == 2 && (!disabled) && ssl)) + return "checked=\"true\""; + return ""; + } + + /** @since 0.8.3 */ + public String getAuth() { + boolean enabled = _context.getBooleanProperty(PROP_AUTH); + if (enabled) + return "checked=\"true\""; + return ""; + } + + /** @since 0.8.3 */ + public String[] intfcAddresses() { + String[] addrs = Addresses.getAllAddresses(); + List aList = new ArrayList(); + aList.addAll(Arrays.asList(addrs)); + boolean ipv6 = false; + for (String a : aList) { + if (a.indexOf(':') >= 0) { + ipv6 = true; + break; + } + } + if (!aList.contains("0.0.0.0")) + aList.add("0.0.0.0"); + if (ipv6 && !aList.contains("0:0:0:0:0:0:0:0")) + aList.add("0:0:0:0:0:0:0:0"); // we could do "::" but all the other ones are probably in long form + Collections.sort(aList); + return aList.toArray(addrs); + } + + /** @since 0.8.3 */ + public boolean isIFSelected(String addr) { + boolean bindAll = _context.getBooleanProperty(BIND_ALL_INTERFACES); + if (bindAll && addr.equals("0.0.0.0") || addr.equals("::")) + return true; + String host = _context.getProperty(ClientManagerFacadeImpl.PROP_CLIENT_HOST, + ClientManagerFacadeImpl.DEFAULT_HOST); + return (host.equals(addr)); + } + public void setEdit(String edit) { if (edit == null) return; @@ -92,9 +172,9 @@ public class ConfigClientsHelper extends HelperBase { continue; StringBuilder desc = new StringBuilder(256); desc.append("") - .append("
").append(_("Version")).append("").append(stripHTML(appProps, "version")) + .append("
").append(_("Version")).append("").append(stripHTML(appProps, "version")) .append("
") - .append(_("Signed by")).append(""); + .append(_("Signed by")).append(""); String s = stripHTML(appProps, "signer"); if (s != null) { if (s.indexOf("@") > 0) @@ -111,13 +191,13 @@ public class ConfigClientsHelper extends HelperBase { if (ms > 0) { String date = (new SimpleDateFormat("yyyy-MM-dd HH:mm")).format(new Date(ms)); desc.append("
") - .append(_("Date")).append("").append(date); + .append(_("Date")).append("").append(date); } } s = stripHTML(appProps, "author"); if (s != null) { desc.append("
") - .append(_("Author")).append(""); + .append(_("Author")).append(""); if (s.indexOf("@") > 0) desc.append("").append(s).append(""); else @@ -128,12 +208,12 @@ public class ConfigClientsHelper extends HelperBase { s = stripHTML(appProps, "description"); if (s != null) { desc.append("
") - .append(_("Description")).append("").append(s); + .append(_("Description")).append("").append(s); } s = stripHTML(appProps, "license"); if (s != null) { desc.append("
") - .append(_("License")).append("").append(s); + .append(_("License")).append("").append(s); } s = stripHTML(appProps, "websiteURL"); if (s != null) { diff --git a/apps/routerconsole/java/src/net/i2p/router/web/ConfigStatsHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/ConfigStatsHelper.java index 07c0f3e0f..254a20728 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/ConfigStatsHelper.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/ConfigStatsHelper.java @@ -139,6 +139,6 @@ public class ConfigStatsHelper extends HelperBase { public boolean getCurrentCanBeGraphed() { return _currentCanBeGraphed; } public String getExplicitFilter() { return _filter; } public boolean getIsFull() { - return _context.getBooleanPropertyDefaultTrue(StatManager.PROP_STAT_FULL); + return _context.getBooleanProperty(StatManager.PROP_STAT_FULL); } } diff --git a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java index 9d9370191..174704ba6 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/UpdateHandler.java @@ -1,8 +1,11 @@ package net.i2p.router.web; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.text.DecimalFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.StringTokenizer; @@ -15,10 +18,12 @@ import net.i2p.router.RouterVersion; import net.i2p.util.EepGet; import net.i2p.util.I2PAppThread; import net.i2p.util.Log; +import net.i2p.util.PartialEepGet; +import net.i2p.util.VersionComparator; /** - *

Handles the request to update the router by firing off an - * {@link net.i2p.util.EepGet} call to download the latest signed update file + *

Handles the request to update the router by firing one or more + * {@link net.i2p.util.EepGet} calls to download the latest signed update file * and displaying the status to anyone who asks. *

*

After the download completes the signed update file is verified with @@ -125,6 +130,11 @@ public class UpdateHandler { protected boolean done; protected EepGet _get; protected final DecimalFormat _pct = new DecimalFormat("0.0%"); + /** tells the listeners what mode we are in */ + private boolean _isPartial; + /** set by the listeners on completion */ + private boolean _isNewer; + private ByteArrayOutputStream _baos; public UpdateRunner() { _isRunning = false; @@ -141,39 +151,88 @@ public class UpdateHandler { System.setProperty(PROP_UPDATE_IN_PROGRESS, "false"); _isRunning = false; } + + /** + * Loop through the entire list of update URLs. + * For each one, first get the version from the first 56 bytes and see if + * it is newer than what we are running now. + * If it is, get the whole thing. + */ protected void update() { - updateStatus("" + _("Updating") + ""); // TODO: // Do a PartialEepGet on the selected URL, check for version we expect, // and loop if it isn't what we want. // This will allow us to do a release without waiting for the last host to install the update. // Alternative: In bytesTransferred(), Check the data in the output file after // we've received at least 56 bytes. Need a cancel() method in EepGet ? - String updateURL = selectUpdateURL(); - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Selected update URL: " + updateURL); + boolean shouldProxy = Boolean.valueOf(_context.getProperty(ConfigUpdateHandler.PROP_SHOULD_PROXY, ConfigUpdateHandler.DEFAULT_SHOULD_PROXY)).booleanValue(); String proxyHost = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_HOST, ConfigUpdateHandler.DEFAULT_PROXY_HOST); int proxyPort = _context.getProperty(ConfigUpdateHandler.PROP_PROXY_PORT, ConfigUpdateHandler.DEFAULT_PROXY_PORT_INT); - try { - if (shouldProxy) - // 40 retries!! - _get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false); - else - _get = new EepGet(_context, 1, _updateFile, updateURL, false); - _get.addStatusListener(UpdateRunner.this); - _get.fetch(); - } catch (Throwable t) { - _log.error("Error updating", t); + + List urls = getUpdateURLs(); + if (urls.isEmpty()) { + // not likely, don't bother translating + updateStatus("Update source list is empty, cannot download update"); + _log.log(Log.CRIT, "Update source list is empty - cannot download update"); + return; + } + + if (shouldProxy) + _baos = new ByteArrayOutputStream(TrustedUpdate.HEADER_BYTES); + for (String updateURL : urls) { + updateStatus("" + _("Updating from {0}", linkify(updateURL)) + ""); + if (_log.shouldLog(Log.DEBUG)) + _log.debug("Selected update URL: " + updateURL); + + // Check the first 56 bytes for the version + if (shouldProxy) { + _isPartial = true; + _isNewer = false; + _baos.reset(); + try { + // no retries + _get = new PartialEepGet(_context, proxyHost, proxyPort, _baos, updateURL, TrustedUpdate.HEADER_BYTES); + _get.addStatusListener(UpdateRunner.this); + _get.fetch(); + } catch (Throwable t) { + _isNewer = false; + } + _isPartial = false; + if (!_isNewer) + continue; + } + + // Now get the whole thing + try { + if (shouldProxy) + // 40 retries!! + _get = new EepGet(_context, proxyHost, proxyPort, 40, _updateFile, updateURL, false); + else + _get = new EepGet(_context, 1, _updateFile, updateURL, false); + _get.addStatusListener(UpdateRunner.this); + _get.fetch(); + } catch (Throwable t) { + _log.error("Error updating", t); + } + if (this.done) + break; } } + // EepGet Listeners below. + // We use the same for both the partial and the full EepGet, + // with a couple of adjustments depending on which mode. + public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) { + _isNewer = false; if (_log.shouldLog(Log.DEBUG)) _log.debug("Attempt failed on " + url, cause); // ignored } public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) { + if (_isPartial) + return; StringBuilder buf = new StringBuilder(64); buf.append("").append(_("Updating")).append(" "); double pct = ((double)alreadyTransferred + (double)currentWrite) / @@ -186,6 +245,19 @@ public class UpdateHandler { updateStatus(buf.toString()); } public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) { + if (_isPartial) { + // Compare version with what we have now + String newVersion = TrustedUpdate.getVersionString(new ByteArrayInputStream(_baos.toByteArray())); + boolean newer = (new VersionComparator()).compare(newVersion, RouterVersion.VERSION) > 0; + if (!newer) { + updateStatus("" + _("No new version found at {0}", linkify(url)) + ""); + if (_log.shouldLog(Log.WARN)) + _log.warn("Found old version \"" + newVersion + "\" at " + url); + } + _isNewer = newer; + return; + } + // Process the .sud/.su2 file updateStatus("" + _("Update downloaded") + ""); TrustedUpdate up = new TrustedUpdate(_context); File f = new File(_updateFile); @@ -223,15 +295,16 @@ public class UpdateHandler { } } else { _log.log(Log.CRIT, err + " from " + url); - updateStatus("" + err + ' ' + _("from {0}", url) + " "); + updateStatus("" + err + ' ' + _("from {0}", linkify(url)) + " "); } } public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) { + _isNewer = false; // don't display bytesTransferred as it is meaningless - _log.log(Log.CRIT, "Update from " + url + " did not download completely (" + + _log.error("Update from " + url + " did not download completely (" + bytesRemaining + " remaining after " + currentAttempt + " tries)"); - updateStatus("" + _("Transfer failed") + ""); + updateStatus("" + _("Transfer failed from {0}", linkify(url)) + ""); } public void headerReceived(String url, int attemptNum, String key, String val) {} public void attempting(String url) {} @@ -242,27 +315,24 @@ public class UpdateHandler { _context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART); } - private String selectUpdateURL() { + private List getUpdateURLs() { String URLs = _context.getProperty(ConfigUpdateHandler.PROP_UPDATE_URL, ConfigUpdateHandler.DEFAULT_UPDATE_URL); StringTokenizer tok = new StringTokenizer(URLs, " ,\r\n"); - List URLList = new ArrayList(); + List URLList = new ArrayList(); while (tok.hasMoreTokens()) URLList.add(tok.nextToken().trim()); - int size = URLList.size(); - //_log.log(Log.DEBUG, "Picking update source from " + size + " candidates."); - if (size <= 0) { - _log.log(Log.CRIT, "Update source list is empty - cannot download update"); - return null; - } - int index = I2PAppContext.getGlobalContext().random().nextInt(size); - _log.log(Log.DEBUG, "Picked update source " + index + "."); - return (String) URLList.get(index); + Collections.shuffle(URLList, _context.random()); + return URLList; } protected void updateStatus(String s) { _status = s; } + protected static String linkify(String url) { + return "" + url + ""; + } + /** translate a string */ protected String _(String s) { return Messages.getString(s, _context); diff --git a/apps/routerconsole/jsp/configclients.jsp b/apps/routerconsole/jsp/configclients.jsp index 4f633c894..a4d670295 100644 --- a/apps/routerconsole/jsp/configclients.jsp +++ b/apps/routerconsole/jsp/configclients.jsp @@ -47,7 +47,51 @@ button span.hide{ " /> <% } %> " /> -

<%=intl._("WebApp Configuration")%>

+ + +

<%=intl._("Advanced Client Interface Configuration")%>

+<%=intl._("External I2CP (I2P Client Protocol) Interface Configuration")%>
+ > +<%=intl._("Enabled without SSL")%>
+ > +<%=intl._("Enabled with SSL required")%>
+ > +<%=intl._("Disabled - Clients outside this Java process may not connect")%>
+<%=intl._("I2CP Port")%>: +" >
+<%=intl._("I2CP Interface")%>: +
+<%=intl._("Authorization")%>
+ > +<%=intl._("Requre username and password")%>
+<%=intl._("Username")%>: +" >
+<%=intl._("Password")%>: +" >
+

<%=intl._("The default settings will work for most people.")%> +<%=intl._("Any changes made here must also be configured in the external client.")%> +<%=intl._("Many clients do not support SSL or authorization.")%> +<%=intl._("All changes require restart to take effect.")%> +


+" /> +" /> +
+ +

<%=intl._("WebApp Configuration")%>

<%=intl._("The Java web applications listed below are started by the webConsole client and run in the same JVM as the router. They are usually web applications accessible through the router console. They may be complete applications (e.g. i2psnark),front-ends to another client or application which must be separately enabled (e.g. susidns, i2ptunnel), or have no web interface at all (e.g. addressbook).")%>

<%=intl._("A web app may also be disabled by removing the .war file from the webapps directory; however the .war file and web app will reappear when you update your router to a newer version, so disabling the web app here is the preferred method.")%> diff --git a/core/java/src/net/i2p/data/Certificate.java b/core/java/src/net/i2p/data/Certificate.java index 855d474ca..d7ceabaeb 100644 --- a/core/java/src/net/i2p/data/Certificate.java +++ b/core/java/src/net/i2p/data/Certificate.java @@ -26,8 +26,10 @@ import java.io.OutputStream; * @author jrandom */ public class Certificate extends DataStructureImpl { - private int _type; - private byte[] _payload; + public final static Certificate NULL_CERT = new NullCert(); + + protected int _type; + protected byte[] _payload; /** Specifies a null certificate type with no payload */ public final static int CERTIFICATE_TYPE_NULL = 0; @@ -41,6 +43,25 @@ public class Certificate extends DataStructureImpl { /** Contains multiple certs */ public final static int CERTIFICATE_TYPE_MULTIPLE = 4; + /** + * If null cert, return immutable static instance, else create new + * @since 0.8.3 + */ + public static Certificate create(InputStream in) throws DataFormatException, IOException { + int type = (int) DataHelper.readLong(in, 1); + int length = (int) DataHelper.readLong(in, 2); + if (type == 0 && length == 0) + return NULL_CERT; + // from here down roughly the same as readBytes() below + if (length == 0) + return new Certificate(type, null); + byte[] payload = new byte[length]; + int read = DataHelper.read(in, payload); + if (read != length) + throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')'); + return new Certificate(type, payload); + } + public Certificate() { } @@ -90,8 +111,10 @@ public class Certificate extends DataStructureImpl { DataHelper.writeLong(out, 2, 0L); } } - - + + /** + * @return the written length (NOT the new offset) + */ public int writeBytes(byte target[], int offset) { int cur = offset; DataHelper.toLong(target, cur, 1, _type); @@ -140,10 +163,12 @@ public class Certificate extends DataStructureImpl { Certificate cert = (Certificate) object; return _type == cert.getCertificateType() && DataHelper.eq(_payload, cert.getPayload()); } + @Override public int hashCode() { return _type + DataHelper.hashCode(_payload); } + @Override public String toString() { StringBuilder buf = new StringBuilder(64); @@ -177,4 +202,67 @@ public class Certificate extends DataStructureImpl { buf.append("]"); return buf.toString(); } + + /** + * An immutable null certificate. + * @since 0.8.3 + */ + private static final class NullCert extends Certificate { + private static final int NULL_LENGTH = 1 + 2; + private static final byte[] NULL_DATA = new byte[NULL_LENGTH]; + + public NullCert() { + // zero already + //_type = CERTIFICATE_TYPE_NULL; + } + + /** @throws RuntimeException always */ + @Override + public void setCertificateType(int type) { + throw new RuntimeException("Data already set"); + } + + /** @throws RuntimeException always */ + @Override + public void setPayload(byte[] payload) { + throw new RuntimeException("Data already set"); + } + + /** @throws RuntimeException always */ + @Override + public void readBytes(InputStream in) throws DataFormatException, IOException { + throw new RuntimeException("Data already set"); + } + + /** Overridden for efficiency */ + @Override + public void writeBytes(OutputStream out) throws IOException { + out.write(NULL_DATA); + } + + /** Overridden for efficiency */ + @Override + public int writeBytes(byte target[], int offset) { + System.arraycopy(NULL_DATA, 0, target, offset, NULL_LENGTH); + return NULL_LENGTH; + } + + /** @throws RuntimeException always */ + @Override + public int readBytes(byte source[], int offset) throws DataFormatException { + throw new RuntimeException("Data already set"); + } + + /** Overridden for efficiency */ + @Override + public int size() { + return NULL_LENGTH; + } + + /** Overridden for efficiency */ + @Override + public int hashCode() { + return 99999; + } + } } diff --git a/core/java/src/net/i2p/data/DataHelper.java b/core/java/src/net/i2p/data/DataHelper.java index 60ba86f1c..bd6bf260d 100644 --- a/core/java/src/net/i2p/data/DataHelper.java +++ b/core/java/src/net/i2p/data/DataHelper.java @@ -914,9 +914,20 @@ public class DataHelper { return c; } + /** + * This is different than InputStream.read(target), in that it + * does repeated reads until the full data is received. + */ public static int read(InputStream in, byte target[]) throws IOException { return read(in, target, 0, target.length); } + + /** + * This is different than InputStream.read(target, offset, length), in that it + * returns the new offset (== old offset + bytes read). + * It also does repeated reads until the full data is received. + * @return the new offset (== old offset + bytes read) + */ public static int read(InputStream in, byte target[], int offset, int length) throws IOException { int cur = offset; while (cur < length) { diff --git a/core/java/src/net/i2p/data/Destination.java b/core/java/src/net/i2p/data/Destination.java index 41478275b..a8c176df6 100644 --- a/core/java/src/net/i2p/data/Destination.java +++ b/core/java/src/net/i2p/data/Destination.java @@ -28,7 +28,10 @@ public class Destination extends KeysAndCert { fromBase64(s); } - /** deprecated, used only by Packet.java in streaming */ + /** + * deprecated, used only by Packet.java in streaming + * @return the written length (NOT the new offset) + */ public int writeBytes(byte target[], int offset) { int cur = offset; System.arraycopy(_publicKey.getData(), 0, target, cur, PublicKey.KEYSIZE_BYTES); diff --git a/core/java/src/net/i2p/data/KeysAndCert.java b/core/java/src/net/i2p/data/KeysAndCert.java index 046d428a3..d27d4bcf4 100644 --- a/core/java/src/net/i2p/data/KeysAndCert.java +++ b/core/java/src/net/i2p/data/KeysAndCert.java @@ -65,8 +65,9 @@ public class KeysAndCert extends DataStructureImpl { _publicKey.readBytes(in); _signingKey = new SigningPublicKey(); _signingKey.readBytes(in); - _certificate = new Certificate(); - _certificate.readBytes(in); + //_certificate = new Certificate(); + //_certificate.readBytes(in); + _certificate = Certificate.create(in); __calculatedHash = null; } diff --git a/core/java/src/net/i2p/data/Payload.java b/core/java/src/net/i2p/data/Payload.java index 0371ed73f..bdaac7d4c 100644 --- a/core/java/src/net/i2p/data/Payload.java +++ b/core/java/src/net/i2p/data/Payload.java @@ -93,6 +93,10 @@ public class Payload extends DataStructureImpl { if (_log.shouldLog(Log.DEBUG)) _log.debug("wrote payload: " + _encryptedData.length); } + + /** + * @return the written length (NOT the new offset) + */ public int writeBytes(byte target[], int offset) { if (_encryptedData == null) throw new IllegalStateException("Not yet encrypted. Please set the encrypted data"); DataHelper.toLong(target, offset, 4, _encryptedData.length); diff --git a/core/java/src/net/i2p/data/SimpleDataStructure.java b/core/java/src/net/i2p/data/SimpleDataStructure.java index 96c1585c5..4754c29ec 100644 --- a/core/java/src/net/i2p/data/SimpleDataStructure.java +++ b/core/java/src/net/i2p/data/SimpleDataStructure.java @@ -20,6 +20,12 @@ import net.i2p.crypto.SHA256Generator; * * Implemented in 0.8.2 and retrofitted over several of the classes in this package. * + * As of 0.8.3, SDS objects may be cached. An SDS may be instantiated with null data, + * and setData(null) is also OK. However, + * once non-null data is set, the data reference is immutable; + * subsequent attempts to set the data via setData(), readBytes(), + * or fromBase64() will throw a RuntimeException. + * * @since 0.8.2 * @author zzz */ @@ -57,14 +63,24 @@ public abstract class SimpleDataStructure extends DataStructureImpl { * Sets the data. * @param data of correct length, or null * @throws IllegalArgumentException if data is not the legal number of bytes (but null is ok) + * @throws RuntimeException if data already set. */ public void setData(byte[] data) { + if (_data != null) + throw new RuntimeException("Data already set"); if (data != null && data.length != _length) throw new IllegalArgumentException("Bad data length"); _data = data; } + /** + * Sets the data. + * @param data of correct length, or null + * @throws RuntimeException if data already set. + */ public void readBytes(InputStream in) throws DataFormatException, IOException { + if (_data != null) + throw new RuntimeException("Data already set"); _data = new byte[_length]; int read = read(in, _data); if (read != _length) throw new DataFormatException("Not enough bytes to read the data"); @@ -85,6 +101,7 @@ public abstract class SimpleDataStructure extends DataStructureImpl { /** * Sets the data. * @throws DataFormatException if decoded data is not the legal number of bytes or on decoding error + * @throws RuntimeException if data already set. */ @Override public void fromBase64(String data) throws DataFormatException { @@ -162,5 +179,4 @@ public abstract class SimpleDataStructure extends DataStructureImpl { if ((obj == null) || !(obj instanceof SimpleDataStructure)) return false; return DataHelper.eq(_data, ((SimpleDataStructure) obj)._data); } - } diff --git a/core/java/src/net/i2p/stat/StatManager.java b/core/java/src/net/i2p/stat/StatManager.java index 5af634067..531138d1a 100644 --- a/core/java/src/net/i2p/stat/StatManager.java +++ b/core/java/src/net/i2p/stat/StatManager.java @@ -206,7 +206,7 @@ public class StatManager { * @return true if the stat should be ignored. */ public boolean ignoreStat(String statName) { - if (_context.getBooleanPropertyDefaultTrue(PROP_STAT_FULL)) + if (_context.getBooleanProperty(PROP_STAT_FULL)) return false; String required = _context.getProperty(PROP_STAT_REQUIRED, DEFAULT_STAT_REQUIRED); String req[] = required.split(","); diff --git a/core/java/src/net/i2p/util/FortunaRandomSource.java b/core/java/src/net/i2p/util/FortunaRandomSource.java index 8f499935b..9061d3a57 100644 --- a/core/java/src/net/i2p/util/FortunaRandomSource.java +++ b/core/java/src/net/i2p/util/FortunaRandomSource.java @@ -23,7 +23,7 @@ import net.i2p.crypto.EntropyHarvester; * */ public class FortunaRandomSource extends RandomSource implements EntropyHarvester { - private AsyncFortunaStandalone _fortuna; + private final AsyncFortunaStandalone _fortuna; private double _nextGaussian; private boolean _haveNextGaussian; @@ -210,6 +210,7 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste _fortuna.addRandomBytes(data, offset, len); } +/***** public static void main(String args[]) { try { RandomSource rand = I2PAppContext.getGlobalContext().random(); @@ -231,4 +232,5 @@ public class FortunaRandomSource extends RandomSource implements EntropyHarveste System.out.println("Compressed size of 1MB: " + compressed.length); } catch (Exception e) { e.printStackTrace(); } } +*****/ } diff --git a/core/java/src/net/i2p/util/RandomSource.java b/core/java/src/net/i2p/util/RandomSource.java index c37c581dc..c7c87239c 100644 --- a/core/java/src/net/i2p/util/RandomSource.java +++ b/core/java/src/net/i2p/util/RandomSource.java @@ -25,18 +25,23 @@ import net.i2p.data.Base64; * @author jrandom */ public class RandomSource extends SecureRandom implements EntropyHarvester { - private Log _log; - private EntropyHarvester _entropyHarvester; - protected I2PAppContext _context; + private final EntropyHarvester _entropyHarvester; + protected final I2PAppContext _context; public RandomSource(I2PAppContext context) { super(); _context = context; - _log = context.logManager().getLog(RandomSource.class); // when we replace to have hooks for fortuna (etc), replace with // a factory (or just a factory method) _entropyHarvester = this; } + + /** + * Singleton for whatever PRNG i2p uses. + * Same as I2PAppContext.getGlobalContext().random(); + * use context.random() if you have a context already. + * @return I2PAppContext.getGlobalContext().random() + */ public static RandomSource getInstance() { return I2PAppContext.getGlobalContext().random(); } diff --git a/installer/resources/themes/console/classic/console.css b/installer/resources/themes/console/classic/console.css index 3aa0c1424..2c2768053 100644 --- a/installer/resources/themes/console/classic/console.css +++ b/installer/resources/themes/console/classic/console.css @@ -714,7 +714,7 @@ input { vertical-align: middle; } -input[type=text] { +input[type=text], input[type=password] { margin: 3px 5px 3px 5px; vertical-align: middle; } @@ -910,4 +910,4 @@ div.footnote hr{ margin-top: -8px; margin-bottom: -5px; margin-right: 5px; -} \ No newline at end of file +} diff --git a/installer/resources/themes/console/dark/console.css b/installer/resources/themes/console/dark/console.css index c2a16245c..827002d53 100644 --- a/installer/resources/themes/console/dark/console.css +++ b/installer/resources/themes/console/dark/console.css @@ -843,7 +843,7 @@ input:active { color: #EE9; } -input[type=text] { +input[type=text], input[type=password] { background: #000; color: #EE9; margin: 5px 10px; @@ -859,7 +859,7 @@ input[type=text] { box-shadow: inset 1px 1px 1px 0px #000; } -input[type=text]:active, input[type=text]:hover { +input[type=text]:active, input[type=text]:hover, input[type=password]:active, input[type=password]:hover { background: #000; } @@ -1058,4 +1058,4 @@ div.footnote hr{ margin-top: -5px; margin-bottom: -5px; margin-right: 5px; -} \ No newline at end of file +} diff --git a/installer/resources/themes/console/light/console.css b/installer/resources/themes/console/light/console.css index 94dc14f3f..67cef2734 100644 --- a/installer/resources/themes/console/light/console.css +++ b/installer/resources/themes/console/light/console.css @@ -911,7 +911,7 @@ input:active { -moz-box-shadow: inset 0px 0px 0px 1px #f60; } -input[type=text] { +input[type=text], input[type=password] { background: #ffe; color: #001; margin: 5px 10px 5px 10px; @@ -1166,4 +1166,4 @@ div.footnote hr{ margin-top: 0px; margin-bottom: -18px; margin-right: 5px; -} \ No newline at end of file +} diff --git a/installer/resources/themes/console/midnight/console.css b/installer/resources/themes/console/midnight/console.css index 996259149..372364b74 100644 --- a/installer/resources/themes/console/midnight/console.css +++ b/installer/resources/themes/console/midnight/console.css @@ -750,7 +750,7 @@ input { vertical-align: middle; } -input[type=text] { +input[type=text], input[type=password] { margin: 3px 5px 3px 5px; vertical-align: middle; } @@ -760,7 +760,7 @@ select { vertical-align: middle; } -input[type=text], select { +input[type=text], input[type=password] select { background: #001; color: #eef; border: 1px solid #99f; @@ -959,4 +959,4 @@ div.footnote hr{ margin-top: -5px; margin-bottom: -10px; margin-right: 5px; -} \ No newline at end of file +} diff --git a/router/java/src/net/i2p/data/i2np/GarlicClove.java b/router/java/src/net/i2p/data/i2np/GarlicClove.java index 1380dfd60..1067fb640 100644 --- a/router/java/src/net/i2p/data/i2np/GarlicClove.java +++ b/router/java/src/net/i2p/data/i2np/GarlicClove.java @@ -68,8 +68,9 @@ public class GarlicClove extends DataStructureImpl { _expiration = DataHelper.readDate(in); if (_log.shouldLog(Log.DEBUG)) _log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration); - _certificate = new Certificate(); - _certificate.readBytes(in); + //_certificate = new Certificate(); + //_certificate.readBytes(in); + _certificate = Certificate.create(in); if (_log.shouldLog(Log.DEBUG)) _log.debug("Read cert: " + _certificate); } diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java index 9d045c510..9025fd22b 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java @@ -115,7 +115,7 @@ class OutboundClientMessageJobHelper { instructions.setRouter(null); instructions.setTunnelId(null); - config.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + config.setCertificate(Certificate.NULL_CERT); config.setDeliveryInstructions(instructions); config.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); config.setExpiration(expiration); // +2*Router.CLOCK_FUDGE_FACTOR); @@ -165,7 +165,7 @@ class OutboundClientMessageJobHelper { if (log.shouldLog(Log.DEBUG)) log.debug("Delivery status message key: " + replyToken + " arrival: " + msg.getArrival()); - ackClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + ackClove.setCertificate(Certificate.NULL_CERT); ackClove.setDeliveryInstructions(ackInstructions); ackClove.setExpiration(expiration); ackClove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); @@ -196,7 +196,7 @@ class OutboundClientMessageJobHelper { instructions.setDelaySeconds(0); instructions.setEncrypted(false); - clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + clove.setCertificate(Certificate.NULL_CERT); clove.setDeliveryInstructions(instructions); clove.setExpiration(expiration); clove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); @@ -222,7 +222,7 @@ class OutboundClientMessageJobHelper { instructions.setDelaySeconds(0); instructions.setEncrypted(false); - clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + clove.setCertificate(Certificate.NULL_CERT); clove.setDeliveryInstructions(instructions); clove.setExpiration(expiration); clove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index 45622d117..ce1f36159 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -872,7 +872,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { instructions.setDelaySeconds(0); instructions.setEncrypted(false); - clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + clove.setCertificate(Certificate.NULL_CERT); clove.setDeliveryInstructions(instructions); clove.setExpiration(OVERALL_TIMEOUT_MS_DEFAULT+getContext().clock().now()); clove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE)); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java b/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java index 1f521455e..b53269e52 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java @@ -46,7 +46,7 @@ class MessageWrapper { instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL); PayloadGarlicConfig payload = new PayloadGarlicConfig(); - payload.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + payload.setCertificate(Certificate.NULL_CERT); payload.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); payload.setPayload(m); payload.setRecipient(to); diff --git a/router/java/src/net/i2p/router/transport/Addresses.java b/router/java/src/net/i2p/router/transport/Addresses.java index dab8cfc6d..5200e1173 100644 --- a/router/java/src/net/i2p/router/transport/Addresses.java +++ b/router/java/src/net/i2p/router/transport/Addresses.java @@ -31,17 +31,40 @@ public class Addresses { } /** - * @return an array of all addresses, excluding + * @return a sorted array of all addresses, excluding * IPv6, local, broadcast, multicast, etc. */ public static String[] getAddresses() { + return getAddresses(false); + } + + /** + * @return a sorted array of all addresses, excluding + * only link local and multicast + * @since 0.8.3 + */ + public static String[] getAllAddresses() { + return getAddresses(true); + } + + /** + * @return a sorted array of all addresses + * @param whether to exclude IPV6 and local + * @return an array of all addresses + * @since 0.8.3 + */ + public static String[] getAddresses(boolean all) { Set rv = new HashSet(4); try { InetAddress localhost = InetAddress.getLocalHost(); InetAddress[] allMyIps = InetAddress.getAllByName(localhost.getCanonicalHostName()); if (allMyIps != null) { - for (int i = 0; i < allMyIps.length; i++) - add(rv, allMyIps[i]); + for (int i = 0; i < allMyIps.length; i++) { + if (all) + addAll(rv, allMyIps[i]); + else + add(rv, allMyIps[i]); + } } } catch (UnknownHostException e) {} @@ -50,7 +73,10 @@ public class Addresses { NetworkInterface ifc = ifcs.nextElement(); for(Enumeration addrs = ifc.getInetAddresses(); addrs.hasMoreElements();) { InetAddress addr = addrs.nextElement(); - add(rv, addr); + if (all) + addAll(rv, addr); + else + add(rv, addr); } } } catch (SocketException e) {} @@ -79,8 +105,16 @@ public class Addresses { set.add(ip); } + private static void addAll(Set set, InetAddress ia) { + if (ia.isLinkLocalAddress() || + ia.isMulticastAddress()) + return; + String ip = ia.getHostAddress(); + set.add(ip); + } + public static void main(String[] args) { - String[] a = getAddresses(); + String[] a = getAddresses(true); for (String s : a) System.err.println("Address: " + s); }