propagate from branch 'i2p.i2p' (head d289b6cafae6b23ce699dca11dbb3e993c8f827f)

to branch 'i2p.i2p.zzz.test' (head e2c6210696c78c54650ff345f18ad62b4543a18b)
This commit is contained in:
zzz
2012-01-02 14:39:17 +00:00
17 changed files with 323 additions and 175 deletions

View File

@ -12,7 +12,19 @@ public class LogsHelper extends HelperBase {
/** @since 0.8.11 */ /** @since 0.8.11 */
public String getJettyVersion() { public String getJettyVersion() {
return Version.getImplVersion(); return jettyVersion();
}
/** @since 0.8.13 */
static String jettyVersion() {
try {
String rv = Version.getImplVersion();
if (rv.startsWith("Jetty/"))
rv = rv.substring(6);
return rv;
} catch (Throwable t) {
return "unknown";
}
} }
public String getLogs() { public String getLogs() {

View File

@ -376,9 +376,8 @@ public class NetDbRenderer {
int cost = addr.getCost(); int cost = addr.getCost();
if (!((style.equals("SSU") && cost == 5) || (style.equals("NTCP") && cost == 10))) if (!((style.equals("SSU") && cost == 5) || (style.equals("NTCP") && cost == 10)))
buf.append('[').append(_("cost")).append('=').append("" + cost).append("] "); buf.append('[').append(_("cost")).append('=').append("" + cost).append("] ");
Properties p = new OrderedProperties(); Map p = addr.getOptionsMap();
p.putAll(addr.getOptions()); for (Map.Entry e : (Set<Map.Entry>) p.entrySet()) {
for (Map.Entry e : p.entrySet()) {
String name = (String) e.getKey(); String name = (String) e.getKey();
String val = (String) e.getValue(); String val = (String) e.getValue();
buf.append('[').append(_(DataHelper.stripHTML(name))).append('=').append(DataHelper.stripHTML(val)).append("] "); buf.append('[').append(_(DataHelper.stripHTML(name))).append('=').append(DataHelper.stripHTML(val)).append("] ");
@ -387,9 +386,10 @@ public class NetDbRenderer {
buf.append("</td></tr>\n"); buf.append("</td></tr>\n");
if (full) { if (full) {
buf.append("<tr><td>" + _("Stats") + ": <br><code>"); buf.append("<tr><td>" + _("Stats") + ": <br><code>");
for (Iterator iter = info.getOptions().keySet().iterator(); iter.hasNext(); ) { Map p = info.getOptionsMap();
String key = (String)iter.next(); for (Map.Entry e : (Set<Map.Entry>) p.entrySet()) {
String val = info.getOption(key); String key = (String) e.getKey();
String val = (String) e.getValue();
buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br>\n"); buf.append(DataHelper.stripHTML(key)).append(" = ").append(DataHelper.stripHTML(val)).append("<br>\n");
} }
buf.append("</code></td></tr>\n"); buf.append("</code></td></tr>\n");
@ -412,7 +412,7 @@ public class NetDbRenderer {
if (style.equals("NTCP")) { if (style.equals("NTCP")) {
rv |= NTCP; rv |= NTCP;
} else if (style.equals("SSU")) { } else if (style.equals("SSU")) {
if (addr.getOptions().getProperty("iport0") != null) if (addr.getOption("iport0") != null)
rv |= SSUI; rv |= SSUI;
else else
rv |= SSU; rv |= SSU;

View File

@ -17,6 +17,7 @@ import java.util.Properties;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import net.i2p.CoreVersion;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.router.Job; import net.i2p.router.Job;
@ -27,6 +28,7 @@ import net.i2p.util.ConcurrentHashSet;
import net.i2p.util.FileUtil; import net.i2p.util.FileUtil;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.util.Translate; import net.i2p.util.Translate;
import net.i2p.util.VersionComparator;
import org.mortbay.jetty.Server; import org.mortbay.jetty.Server;
@ -95,6 +97,41 @@ public class PluginStarter implements Runnable {
log.error("Cannot start nonexistent plugin: " + appName); log.error("Cannot start nonexistent plugin: " + appName);
return false; return false;
} }
Properties props = pluginProperties(ctx, appName);
String minVersion = ConfigClientsHelper.stripHTML(props, "min-i2p-version");
if (minVersion != null &&
(new VersionComparator()).compare(CoreVersion.VERSION, minVersion) < 0) {
String foo = "Plugin " + appName + " requires I2P version " + minVersion + " or higher";
log.error(foo);
throw new Exception(foo);
}
minVersion = ConfigClientsHelper.stripHTML(props, "min-java-version");
if (minVersion != null &&
(new VersionComparator()).compare(System.getProperty("java.version"), minVersion) < 0) {
String foo = "Plugin " + appName + " requires Java version " + minVersion + " or higher";
log.error(foo);
throw new Exception(foo);
}
String jVersion = LogsHelper.jettyVersion();
minVersion = ConfigClientsHelper.stripHTML(props, "min-jetty-version");
if (minVersion != null &&
(new VersionComparator()).compare(minVersion, jVersion) > 0) {
String foo = "Plugin " + appName + " requires Jetty version " + minVersion + " or higher";
log.error(foo);
throw new Exception(foo);
}
String maxVersion = ConfigClientsHelper.stripHTML(props, "max-jetty-version");
if (maxVersion != null &&
(new VersionComparator()).compare(maxVersion, jVersion) < 0) {
String foo = "Plugin " + appName + " requires Jetty version " + maxVersion + " or lower";
log.error(foo);
throw new Exception(foo);
}
if (log.shouldLog(Log.INFO)) if (log.shouldLog(Log.INFO))
log.info("Starting plugin: " + appName); log.info("Starting plugin: " + appName);
@ -113,8 +150,8 @@ public class PluginStarter implements Runnable {
// load and start things in clients.config // load and start things in clients.config
File clientConfig = new File(pluginDir, "clients.config"); File clientConfig = new File(pluginDir, "clients.config");
if (clientConfig.exists()) { if (clientConfig.exists()) {
Properties props = new Properties(); Properties cprops = new Properties();
DataHelper.loadProps(props, clientConfig); DataHelper.loadProps(cprops, clientConfig);
List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig); List<ClientAppConfig> clients = ClientAppConfig.getClientApps(clientConfig);
runClientApps(ctx, pluginDir, clients, "start"); runClientApps(ctx, pluginDir, clients, "start");
} }
@ -123,7 +160,7 @@ public class PluginStarter implements Runnable {
Server server = WebAppStarter.getConsoleServer(); Server server = WebAppStarter.getConsoleServer();
if (server != null) { if (server != null) {
File consoleDir = new File(pluginDir, "console"); File consoleDir = new File(pluginDir, "console");
Properties props = RouterConsoleRunner.webAppProperties(consoleDir.getAbsolutePath()); Properties wprops = RouterConsoleRunner.webAppProperties(consoleDir.getAbsolutePath());
File webappDir = new File(consoleDir, "webapps"); File webappDir = new File(consoleDir, "webapps");
String fileNames[] = webappDir.list(RouterConsoleRunner.WarFilenameFilter.instance()); String fileNames[] = webappDir.list(RouterConsoleRunner.WarFilenameFilter.instance());
if (fileNames != null) { if (fileNames != null) {
@ -138,7 +175,7 @@ public class PluginStarter implements Runnable {
log.error("Skipping duplicate webapp " + warName + " in plugin " + appName); log.error("Skipping duplicate webapp " + warName + " in plugin " + appName);
continue; continue;
} }
String enabled = props.getProperty(RouterConsoleRunner.PREFIX + warName + ENABLED); String enabled = wprops.getProperty(RouterConsoleRunner.PREFIX + warName + ENABLED);
if (! "false".equals(enabled)) { if (! "false".equals(enabled)) {
if (log.shouldLog(Log.INFO)) if (log.shouldLog(Log.INFO))
log.info("Starting webapp: " + warName); log.info("Starting webapp: " + warName);
@ -181,7 +218,6 @@ public class PluginStarter implements Runnable {
} }
// add summary bar link // add summary bar link
Properties props = pluginProperties(ctx, appName);
String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx)); String name = ConfigClientsHelper.stripHTML(props, "consoleLinkName_" + Messages.getLanguage(ctx));
if (name == null) if (name == null)
name = ConfigClientsHelper.stripHTML(props, "consoleLinkName"); name = ConfigClientsHelper.stripHTML(props, "consoleLinkName");

View File

@ -334,6 +334,21 @@ public class PluginUpdateHandler extends UpdateHandler {
statusDone("<b>" + _("Plugin update requires installed plugin version {0} or lower", maxVersion) + "</b>"); statusDone("<b>" + _("Plugin update requires installed plugin version {0} or lower", maxVersion) + "</b>");
return; return;
} }
oldVersion = LogsHelper.jettyVersion();
minVersion = ConfigClientsHelper.stripHTML(props, "min-jetty-version");
if (minVersion != null &&
(new VersionComparator()).compare(minVersion, oldVersion) > 0) {
to.delete();
statusDone("<b>" + _("Plugin requires Jetty version {0} or higher", minVersion) + "</b>");
return;
}
maxVersion = ConfigClientsHelper.stripHTML(props, "max-jetty-version");
if (maxVersion != null &&
(new VersionComparator()).compare(maxVersion, oldVersion) < 0) {
to.delete();
statusDone("<b>" + _("Plugin requires Jetty version {0} or lower", maxVersion) + "</b>");
return;
}
// check if it is running first? // check if it is running first?
try { try {

View File

@ -118,6 +118,17 @@ public class DataHelper {
public static Properties readProperties(InputStream rawStream) public static Properties readProperties(InputStream rawStream)
throws DataFormatException, IOException { throws DataFormatException, IOException {
Properties props = new OrderedProperties(); Properties props = new OrderedProperties();
readProperties(rawStream, props);
return props;
}
/**
* Ditto, load into an existing properties
* @param props the Properties to load into
* @since 0.8.13
*/
public static Properties readProperties(InputStream rawStream, Properties props)
throws DataFormatException, IOException {
long size = readLong(rawStream, 2); long size = readLong(rawStream, 2);
byte data[] = new byte[(int) size]; byte data[] = new byte[(int) size];
int read = read(rawStream, data); int read = read(rawStream, data);
@ -1268,6 +1279,8 @@ public class DataHelper {
* Why? Just because it has to be consistent so signing will work. * Why? Just because it has to be consistent so signing will work.
* How to spec as returning the same type as the param? * How to spec as returning the same type as the param?
* DEPRECATED - Only used by RouterInfo. * DEPRECATED - Only used by RouterInfo.
*
* @return a new list
*/ */
public static List<? extends DataStructure> sortStructures(Collection<? extends DataStructure> dataStructures) { public static List<? extends DataStructure> sortStructures(Collection<? extends DataStructure> dataStructures) {
if (dataStructures == null) return Collections.EMPTY_LIST; if (dataStructures == null) return Collections.EMPTY_LIST;

View File

@ -124,16 +124,22 @@ public abstract class DatabaseEntry extends DataStructureImpl {
/** /**
* Configure the proof that the entity stands behind the info here * Configure the proof that the entity stands behind the info here
* *
* @throws IllegalStateException if already signed
*/ */
public void setSignature(Signature signature) { public void setSignature(Signature signature) {
if (_signature != null)
throw new IllegalStateException();
_signature = signature; _signature = signature;
} }
/** /**
* Sign the structure using the supplied signing key * Sign the structure using the supplied signing key
* *
* @throws IllegalStateException if already signed
*/ */
public void sign(SigningPrivateKey key) throws DataFormatException { public void sign(SigningPrivateKey key) throws DataFormatException {
if (_signature != null)
throw new IllegalStateException();
byte[] bytes = getBytes(); byte[] bytes = getBytes();
if (bytes == null) throw new DataFormatException("Not enough data to sign"); if (bytes == null) throw new DataFormatException("Not enough data to sign");
// now sign with the key // now sign with the key

View File

@ -12,6 +12,7 @@ package net.i2p.data;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
@ -22,16 +23,24 @@ import net.i2p.util.OrderedProperties;
/** /**
* Defines a method of communicating with a router * Defines a method of communicating with a router
* *
* For efficiency, the options methods and structures here are unsynchronized.
* Initialize the structure with readBytes(), or call the setOptions().
* Don't change it after that.
*
* To ensure integrity of the RouterInfo, methods that change an element of the
* RouterInfo will throw an IllegalStateException after the RouterInfo is signed.
*
* @author jrandom * @author jrandom
*/ */
public class RouterAddress extends DataStructureImpl { public class RouterAddress extends DataStructureImpl {
private int _cost; private int _cost;
private Date _expiration; private Date _expiration;
private String _transportStyle; private String _transportStyle;
private Properties _options; private final Properties _options;
public RouterAddress() { public RouterAddress() {
_cost = -1; _cost = -1;
_options = new OrderedProperties();
} }
/** /**
@ -85,28 +94,59 @@ public class RouterAddress extends DataStructureImpl {
/** /**
* Configure the type of transport that must be used to communicate on this address * Configure the type of transport that must be used to communicate on this address
* *
* @throws IllegalStateException if was already set
*/ */
public void setTransportStyle(String transportStyle) { public void setTransportStyle(String transportStyle) {
if (_transportStyle != null)
throw new IllegalStateException();
_transportStyle = transportStyle; _transportStyle = transportStyle;
} }
/** /**
* Retrieve the transport specific options necessary for communication * Retrieve the transport specific options necessary for communication
* *
* @deprecated use getOptionsMap()
* @return sorted, non-null, NOT a copy, do not modify
*/ */
public Properties getOptions() { public Properties getOptions() {
return _options; return _options;
} }
/** /**
* Specify the transport specific options necessary for communication * Retrieve the transport specific options necessary for communication
* *
* @return an unmodifiable view, non-null, sorted
* @since 0.8.13
*/
public Map getOptionsMap() {
return Collections.unmodifiableMap(_options);
}
/**
* @since 0.8.13
*/
public String getOption(String opt) {
return _options.getProperty(opt);
}
/**
* Specify the transport specific options necessary for communication.
* Makes a copy.
* @param options non-null
* @throws IllegalStateException if was already set
*/ */
public void setOptions(Properties options) { public void setOptions(Properties options) {
_options = options; if (!_options.isEmpty())
throw new IllegalStateException();
_options.putAll(options);
} }
/**
* @throws IllegalStateException if was already read in
*/
public void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_transportStyle != null)
throw new IllegalStateException();
_cost = (int) DataHelper.readLong(in, 1); _cost = (int) DataHelper.readLong(in, 1);
_expiration = DataHelper.readDate(in); _expiration = DataHelper.readDate(in);
_transportStyle = DataHelper.readString(in); _transportStyle = DataHelper.readString(in);
@ -115,11 +155,11 @@ public class RouterAddress extends DataStructureImpl {
_transportStyle = "SSU"; _transportStyle = "SSU";
else if (_transportStyle.equals("NTCP")) else if (_transportStyle.equals("NTCP"))
_transportStyle = "NTCP"; _transportStyle = "NTCP";
_options = DataHelper.readProperties(in); DataHelper.readProperties(in, _options);
} }
public void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if ((_cost < 0) || (_transportStyle == null) || (_options == null)) if ((_cost < 0) || (_transportStyle == null))
throw new DataFormatException("Not enough data to write a router address"); throw new DataFormatException("Not enough data to write a router address");
DataHelper.writeLong(out, 1, _cost); DataHelper.writeLong(out, 1, _cost);
DataHelper.writeDate(out, _expiration); DataHelper.writeDate(out, _expiration);
@ -131,11 +171,12 @@ public class RouterAddress extends DataStructureImpl {
public boolean equals(Object object) { public boolean equals(Object object) {
if ((object == null) || !(object instanceof RouterAddress)) return false; if ((object == null) || !(object instanceof RouterAddress)) return false;
RouterAddress addr = (RouterAddress) object; RouterAddress addr = (RouterAddress) object;
// let's keep this fast as we are putting an address into the RouterInfo set frequently
return return
_cost == addr._cost && _cost == addr._cost &&
DataHelper.eq(_transportStyle, addr._transportStyle) && DataHelper.eq(_transportStyle, addr._transportStyle);
DataHelper.eq(_options, addr._options) && //DataHelper.eq(_options, addr._options) &&
DataHelper.eq(_expiration, addr._expiration); //DataHelper.eq(_expiration, addr._expiration);
} }
/** /**
@ -161,9 +202,7 @@ public class RouterAddress extends DataStructureImpl {
buf.append("\n\tExpiration: ").append(_expiration); buf.append("\n\tExpiration: ").append(_expiration);
if (_options != null) { if (_options != null) {
buf.append("\n\tOptions: #: ").append(_options.size()); buf.append("\n\tOptions: #: ").append(_options.size());
Properties p = new OrderedProperties(); for (Map.Entry e : _options.entrySet()) {
p.putAll(_options);
for (Map.Entry e : p.entrySet()) {
String key = (String) e.getKey(); String key = (String) e.getKey();
String val = (String) e.getValue(); String val = (String) e.getValue();
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]"); buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");

View File

@ -19,6 +19,7 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.Vector; import java.util.Vector;
@ -32,6 +33,13 @@ import net.i2p.util.OrderedProperties;
* Defines the data that a router either publishes to the global routing table or * Defines the data that a router either publishes to the global routing table or
* provides to trusted peers. * provides to trusted peers.
* *
* For efficiency, the methods and structures here are now unsynchronized.
* Initialize the RI with readBytes(), or call the setters and then sign() in a single thread.
* Don't change it after that.
*
* To ensure integrity of the RouterInfo, methods that change an element of the
* RouterInfo will throw an IllegalStateException after the RouterInfo is signed.
*
* @author jrandom * @author jrandom
*/ */
public class RouterInfo extends DatabaseEntry { public class RouterInfo extends DatabaseEntry {
@ -41,7 +49,7 @@ public class RouterInfo extends DatabaseEntry {
private final Set<RouterAddress> _addresses; private final Set<RouterAddress> _addresses;
/** may be null to save memory, no longer final */ /** may be null to save memory, no longer final */
private Set<Hash> _peers; private Set<Hash> _peers;
private /* FIXME final FIXME */ Properties _options; private final Properties _options;
private volatile boolean _validated; private volatile boolean _validated;
private volatile boolean _isValid; private volatile boolean _isValid;
private volatile String _stringified; private volatile String _stringified;
@ -67,14 +75,19 @@ public class RouterInfo extends DatabaseEntry {
_options = new OrderedProperties(); _options = new OrderedProperties();
} }
/**
* Used only by Router and PublishLocalRouterInfoJob.
* Copies ONLY the identity and peers.
* Does not copy published, addresses, options, or signature.
*/
public RouterInfo(RouterInfo old) { public RouterInfo(RouterInfo old) {
this(); this();
setIdentity(old.getIdentity()); setIdentity(old.getIdentity());
setPublished(old.getPublished()); //setPublished(old.getPublished());
setAddresses(old.getAddresses()); //setAddresses(old.getAddresses());
setPeers(old.getPeers()); setPeers(old.getPeers());
setOptions(old.getOptions()); //setOptions(old.getOptions());
setSignature(old.getSignature()); //setSignature(old.getSignature());
// copy over _byteified? // copy over _byteified?
} }
@ -90,12 +103,6 @@ public class RouterInfo extends DatabaseEntry {
return KEY_TYPE_ROUTERINFO; return KEY_TYPE_ROUTERINFO;
} }
private void resetCache() {
_stringified = null;
_byteified = null;
_hashCodeInitialized = false;
}
/** /**
* Retrieve the identity of the router represented * Retrieve the identity of the router represented
* *
@ -107,10 +114,12 @@ public class RouterInfo extends DatabaseEntry {
/** /**
* Configure the identity of the router represented * Configure the identity of the router represented
* *
* @throws IllegalStateException if RouterInfo is already signed
*/ */
public void setIdentity(RouterIdentity ident) { public void setIdentity(RouterIdentity ident) {
if (_signature != null)
throw new IllegalStateException();
_identity = ident; _identity = ident;
resetCache();
// We only want to cache the bytes for our own RI, which is frequently written. // We only want to cache the bytes for our own RI, which is frequently written.
// To cache for all RIs doubles the RI memory usage. // To cache for all RIs doubles the RI memory usage.
// setIdentity() is only called when we are creating our own RI. // setIdentity() is only called when we are creating our own RI.
@ -133,34 +142,35 @@ public class RouterInfo extends DatabaseEntry {
/** /**
* Date on which it was published, in milliseconds since Midnight GMT on Jan 01, 1970 * Date on which it was published, in milliseconds since Midnight GMT on Jan 01, 1970
* *
* @throws IllegalStateException if RouterInfo is already signed
*/ */
public void setPublished(long published) { public void setPublished(long published) {
if (_signature != null)
throw new IllegalStateException();
_published = published; _published = published;
resetCache();
} }
/** /**
* Retrieve the set of RouterAddress structures at which this * Retrieve the set of RouterAddress structures at which this
* router can be contacted. * router can be contacted.
* *
* @return unmodifiable view, non-null
*/ */
public Set<RouterAddress> getAddresses() { public Set<RouterAddress> getAddresses() {
synchronized (_addresses) { return Collections.unmodifiableSet(_addresses);
return new HashSet(_addresses);
}
} }
/** /**
* Specify a set of RouterAddress structures at which this router * Specify a set of RouterAddress structures at which this router
* can be contacted. * can be contacted.
* *
* @throws IllegalStateException if RouterInfo is already signed
*/ */
public void setAddresses(Set<RouterAddress> addresses) { public void setAddresses(Set<RouterAddress> addresses) {
synchronized (_addresses) { if (_signature != null)
_addresses.clear(); throw new IllegalStateException();
if (addresses != null) _addresses.addAll(addresses); _addresses.clear();
} if (addresses != null) _addresses.addAll(addresses);
resetCache();
} }
/** /**
@ -180,8 +190,11 @@ public class RouterInfo extends DatabaseEntry {
* this router can be reached through. * this router can be reached through.
* *
* @deprecated Implemented here but unused elsewhere * @deprecated Implemented here but unused elsewhere
* @throws IllegalStateException if RouterInfo is already signed
*/ */
public void setPeers(Set<Hash> peers) { public void setPeers(Set<Hash> peers) {
if (_signature != null)
throw new IllegalStateException();
if (peers == null || peers.isEmpty()) { if (peers == null || peers.isEmpty()) {
_peers = null; _peers = null;
return; return;
@ -192,37 +205,46 @@ public class RouterInfo extends DatabaseEntry {
_peers.clear(); _peers.clear();
_peers.addAll(peers); _peers.addAll(peers);
} }
resetCache();
} }
/** /**
* Retrieve a set of options or statistics that the router can expose * Retrieve a set of options or statistics that the router can expose.
* *
* @deprecated use getOptionsMap()
* @return sorted, non-null, NOT a copy, do not modify!!!
*/ */
public Properties getOptions() { public Properties getOptions() {
if (_options == null) return new Properties(); return _options;
synchronized (_options) {
return (Properties) _options.clone();
}
}
public String getOption(String opt) {
if (_options == null) return null;
synchronized (_options) {
return _options.getProperty(opt);
}
} }
/** /**
* Configure a set of options or statistics that the router can expose * Retrieve a set of options or statistics that the router can expose.
*
* @return an unmodifiable view, non-null, sorted
* @since 0.8.13
*/
public Map getOptionsMap() {
return Collections.unmodifiableMap(_options);
}
public String getOption(String opt) {
return _options.getProperty(opt);
}
/**
* Configure a set of options or statistics that the router can expose.
* Makes a copy.
*
* @param options if null, clears current options * @param options if null, clears current options
* @throws IllegalStateException if RouterInfo is already signed
*/ */
public void setOptions(Properties options) { public void setOptions(Properties options) {
synchronized (_options) { if (_signature != null)
_options.clear(); throw new IllegalStateException();
if (options != null)
_options.putAll(options); _options.clear();
} if (options != null)
resetCache(); _options.putAll(options);
} }
/** /**
@ -234,14 +256,14 @@ public class RouterInfo extends DatabaseEntry {
protected byte[] getBytes() throws DataFormatException { protected byte[] getBytes() throws DataFormatException {
if (_byteified != null) return _byteified; if (_byteified != null) return _byteified;
if (_identity == null) throw new DataFormatException("Router identity isn't set? wtf!"); if (_identity == null) throw new DataFormatException("Router identity isn't set? wtf!");
if (_addresses == null) throw new DataFormatException("Router addressess isn't set? wtf!");
if (_options == null) throw new DataFormatException("Router options isn't set? wtf!");
//long before = Clock.getInstance().now(); //long before = Clock.getInstance().now();
ByteArrayOutputStream out = new ByteArrayOutputStream(6*1024); ByteArrayOutputStream out = new ByteArrayOutputStream(2*1024);
try { try {
_identity.writeBytes(out); _identity.writeBytes(out);
DataHelper.writeDate(out, new Date(_published)); // avoid thrashing objects
//DataHelper.writeDate(out, new Date(_published));
DataHelper.writeLong(out, 8, _published);
int sz = _addresses.size(); int sz = _addresses.size();
if (sz <= 0 || isHidden()) { if (sz <= 0 || isHidden()) {
// Do not send IP address to peers in hidden mode // Do not send IP address to peers in hidden mode
@ -249,11 +271,12 @@ public class RouterInfo extends DatabaseEntry {
} else { } else {
DataHelper.writeLong(out, 1, sz); DataHelper.writeLong(out, 1, sz);
Collection<RouterAddress> addresses = _addresses; Collection<RouterAddress> addresses = _addresses;
if (sz > 1) if (sz > 1) {
// WARNING this sort algorithm cannot be changed, as it must be consistent // WARNING this sort algorithm cannot be changed, as it must be consistent
// network-wide. The signature is not checked at readin time, but only // network-wide. The signature is not checked at readin time, but only
// later, and the addresses are stored in a Set, not a List. // later, and the addresses are stored in a Set, not a List.
addresses = (Collection<RouterAddress>) DataHelper.sortStructures(addresses); addresses = (Collection<RouterAddress>) DataHelper.sortStructures(addresses);
}
for (RouterAddress addr : addresses) { for (RouterAddress addr : addresses) {
addr.writeBytes(out); addr.writeBytes(out);
} }
@ -293,7 +316,7 @@ public class RouterInfo extends DatabaseEntry {
* Determine whether this router info is authorized with a valid signature * Determine whether this router info is authorized with a valid signature
* *
*/ */
public synchronized boolean isValid() { public boolean isValid() {
if (!_validated) doValidate(); if (!_validated) doValidate();
return _isValid; return _isValid;
} }
@ -304,11 +327,7 @@ public class RouterInfo extends DatabaseEntry {
* @return -1 if unknown * @return -1 if unknown
*/ */
public int getNetworkId() { public int getNetworkId() {
if (_options == null) return -1; String id = _options.getProperty(PROP_NETWORK_ID);
String id = null;
synchronized (_options) {
id = _options.getProperty(PROP_NETWORK_ID);
}
if (id != null) { if (id != null) {
try { try {
return Integer.parseInt(id); return Integer.parseInt(id);
@ -322,11 +341,7 @@ public class RouterInfo extends DatabaseEntry {
* @return non-null, empty string if none * @return non-null, empty string if none
*/ */
public String getCapabilities() { public String getCapabilities() {
if (_options == null) return ""; String capabilities = _options.getProperty(PROP_CAPABILITIES);
String capabilities = null;
synchronized (_options) {
capabilities = _options.getProperty(PROP_CAPABILITIES);
}
if (capabilities != null) if (capabilities != null)
return capabilities; return capabilities;
else else
@ -358,20 +373,27 @@ public class RouterInfo extends DatabaseEntry {
return (bwTier); return (bwTier);
} }
/**
* @throws IllegalStateException if RouterInfo is already signed
*/
public void addCapability(char cap) { public void addCapability(char cap) {
if (_options == null) _options = new OrderedProperties(); if (_signature != null)
synchronized (_options) { throw new IllegalStateException();
String caps = _options.getProperty(PROP_CAPABILITIES); String caps = _options.getProperty(PROP_CAPABILITIES);
if (caps == null) if (caps == null)
_options.setProperty(PROP_CAPABILITIES, ""+cap); _options.setProperty(PROP_CAPABILITIES, ""+cap);
else if (caps.indexOf(cap) == -1) else if (caps.indexOf(cap) == -1)
_options.setProperty(PROP_CAPABILITIES, caps + cap); _options.setProperty(PROP_CAPABILITIES, caps + cap);
}
} }
/**
* @throws IllegalStateException if RouterInfo is already signed
*/
public void delCapability(char cap) { public void delCapability(char cap) {
if (_options == null) return; if (_signature != null)
synchronized (_options) { throw new IllegalStateException();
String caps = _options.getProperty(PROP_CAPABILITIES); String caps = _options.getProperty(PROP_CAPABILITIES);
int idx; int idx;
if (caps == null) { if (caps == null) {
@ -384,7 +406,6 @@ public class RouterInfo extends DatabaseEntry {
buf.deleteCharAt(idx); buf.deleteCharAt(idx);
_options.setProperty(PROP_CAPABILITIES, buf.toString()); _options.setProperty(PROP_CAPABILITIES, buf.toString());
} }
}
} }
/** /**
@ -409,12 +430,9 @@ public class RouterInfo extends DatabaseEntry {
* *
*/ */
public RouterAddress getTargetAddress(String transportStyle) { public RouterAddress getTargetAddress(String transportStyle) {
synchronized (_addresses) { for (RouterAddress addr : _addresses) {
for (Iterator iter = _addresses.iterator(); iter.hasNext(); ) { if (addr.getTransportStyle().equals(transportStyle))
RouterAddress addr = (RouterAddress)iter.next(); return addr;
if (addr.getTransportStyle().equals(transportStyle))
return addr;
}
} }
return null; return null;
} }
@ -425,12 +443,9 @@ public class RouterInfo extends DatabaseEntry {
*/ */
public List<RouterAddress> getTargetAddresses(String transportStyle) { public List<RouterAddress> getTargetAddresses(String transportStyle) {
List<RouterAddress> ret = new Vector<RouterAddress>(); List<RouterAddress> ret = new Vector<RouterAddress>();
synchronized(this._addresses) { for (RouterAddress addr : _addresses) {
for(Object o : this._addresses) { if(addr.getTransportStyle().equals(transportStyle))
RouterAddress addr = (RouterAddress)o; ret.add(addr);
if(addr.getTransportStyle().equals(transportStyle))
ret.add(addr);
}
} }
return ret; return ret;
} }
@ -438,9 +453,9 @@ public class RouterInfo extends DatabaseEntry {
/** /**
* Actually validate the signature * Actually validate the signature
*/ */
private synchronized void doValidate() { private void doValidate() {
_validated = true;
_isValid = super.verifySignature(); _isValid = super.verifySignature();
_validated = true;
if (!_isValid) { if (!_isValid) {
byte data[] = null; byte data[] = null;
@ -459,15 +474,21 @@ public class RouterInfo extends DatabaseEntry {
/** /**
* This does NOT validate the signature * This does NOT validate the signature
*
* @throws IllegalStateException if RouterInfo was already read in
*/ */
public synchronized void readBytes(InputStream in) throws DataFormatException, IOException { public void readBytes(InputStream in) throws DataFormatException, IOException {
if (_signature != null)
throw new IllegalStateException();
_identity = new RouterIdentity(); _identity = new RouterIdentity();
_identity.readBytes(in); _identity.readBytes(in);
Date when = DataHelper.readDate(in); // avoid thrashing objects
if (when == null) //Date when = DataHelper.readDate(in);
_published = 0; //if (when == null)
else // _published = 0;
_published = when.getTime(); //else
// _published = when.getTime();
_published = DataHelper.readLong(in, 8);
int numAddresses = (int) DataHelper.readLong(in, 1); int numAddresses = (int) DataHelper.readLong(in, 1);
for (int i = 0; i < numAddresses; i++) { for (int i = 0; i < numAddresses; i++) {
RouterAddress address = new RouterAddress(); RouterAddress address = new RouterAddress();
@ -485,11 +506,10 @@ public class RouterInfo extends DatabaseEntry {
_peers.add(peerIdentityHash); _peers.add(peerIdentityHash);
} }
} }
_options = DataHelper.readProperties(in); DataHelper.readProperties(in, _options);
_signature = new Signature(); _signature = new Signature();
_signature.readBytes(in); _signature.readBytes(in);
resetCache();
//_log.debug("Read routerInfo: " + toString()); //_log.debug("Read routerInfo: " + toString());
} }
@ -497,13 +517,13 @@ public class RouterInfo extends DatabaseEntry {
/** /**
* This does NOT validate the signature * This does NOT validate the signature
*/ */
public synchronized void writeBytes(OutputStream out) throws DataFormatException, IOException { public void writeBytes(OutputStream out) throws DataFormatException, IOException {
if (_identity == null) throw new DataFormatException("Missing identity"); if (_identity == null) throw new DataFormatException("Missing identity");
if (_published < 0) throw new DataFormatException("Invalid published date: " + _published); if (_published < 0) throw new DataFormatException("Invalid published date: " + _published);
if (_signature == null) throw new DataFormatException("Signature is null"); if (_signature == null) throw new DataFormatException("Signature is null");
//if (!isValid()) //if (!isValid())
// throw new DataFormatException("Data is not valid"); // throw new DataFormatException("Data is not valid");
ByteArrayOutputStream baos = new ByteArrayOutputStream(512); ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
baos.write(getBytes()); baos.write(getBytes());
_signature.writeBytes(baos); _signature.writeBytes(baos);
@ -518,10 +538,11 @@ public class RouterInfo extends DatabaseEntry {
RouterInfo info = (RouterInfo) object; RouterInfo info = (RouterInfo) object;
return DataHelper.eq(_identity, info.getIdentity()) return DataHelper.eq(_identity, info.getIdentity())
&& DataHelper.eq(_signature, info.getSignature()) && DataHelper.eq(_signature, info.getSignature())
&& _published == info.getPublished() && _published == info.getPublished();
&& DataHelper.eq(_addresses, info.getAddresses()) // Let's speed up the NetDB
&& DataHelper.eq(_options, info.getOptions()) //&& DataHelper.eq(_addresses, info.getAddresses())
&& DataHelper.eq(getPeers(), info.getPeers()); //&& DataHelper.eq(_options, info.getOptions())
//&& DataHelper.eq(getPeers(), info.getPeers());
} }
@Override @Override
@ -541,23 +562,19 @@ public class RouterInfo extends DatabaseEntry {
buf.append("\n\tIdentity: ").append(_identity); buf.append("\n\tIdentity: ").append(_identity);
buf.append("\n\tSignature: ").append(_signature); buf.append("\n\tSignature: ").append(_signature);
buf.append("\n\tPublished on: ").append(new Date(_published)); buf.append("\n\tPublished on: ").append(new Date(_published));
Set addresses = _addresses; // getAddresses() buf.append("\n\tAddresses: #: ").append(_addresses.size());
buf.append("\n\tAddresses: #: ").append(addresses.size()); for (RouterAddress addr : _addresses) {
for (Iterator iter = addresses.iterator(); iter.hasNext();) {
RouterAddress addr = (RouterAddress) iter.next();
buf.append("\n\t\tAddress: ").append(addr); buf.append("\n\t\tAddress: ").append(addr);
} }
Set peers = getPeers(); Set<Hash> peers = getPeers();
buf.append("\n\tPeers: #: ").append(peers.size()); buf.append("\n\tPeers: #: ").append(peers.size());
for (Iterator iter = peers.iterator(); iter.hasNext();) { for (Hash hash : peers) {
Hash hash = (Hash) iter.next();
buf.append("\n\t\tPeer hash: ").append(hash); buf.append("\n\t\tPeer hash: ").append(hash);
} }
Properties options = _options; // getOptions(); buf.append("\n\tOptions: #: ").append(_options.size());
buf.append("\n\tOptions: #: ").append(options.size()); for (Map.Entry e : _options.entrySet()) {
for (Iterator iter = options.keySet().iterator(); iter.hasNext();) { String key = (String) e.getKey();
String key = (String) iter.next(); String val = (String) e.getValue();
String val = options.getProperty(key);
buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]"); buf.append("\n\t\t[").append(key).append("] = [").append(val).append("]");
} }
buf.append("]"); buf.append("]");

View File

@ -488,9 +488,7 @@ public class Blocklist {
for (int j = 0; j < paddr.size(); j++) { for (int j = 0; j < paddr.size(); j++) {
RouterAddress pa = (RouterAddress) pladdr.get(j); RouterAddress pa = (RouterAddress) pladdr.get(j);
if (pa == null) continue; if (pa == null) continue;
Properties pprops = pa.getOptions(); String phost = pa.getOption("host");
if (pprops == null) continue;
String phost = pprops.getProperty("host");
if (phost == null) continue; if (phost == null) continue;
if (oldphost != null && oldphost.equals(phost)) continue; if (oldphost != null && oldphost.equals(phost)) continue;
oldphost = phost; oldphost = phost;

View File

@ -60,7 +60,7 @@ import net.i2p.util.SimpleScheduler;
* *
*/ */
public class Router implements RouterClock.ClockShiftListener { public class Router implements RouterClock.ClockShiftListener {
private final Log _log; private Log _log;
private final RouterContext _context; private final RouterContext _context;
private final Map<String, String> _config; private final Map<String, String> _config;
/** full path */ /** full path */
@ -77,9 +77,9 @@ public class Router implements RouterClock.ClockShiftListener {
private ShutdownHook _shutdownHook; private ShutdownHook _shutdownHook;
/** non-cancellable shutdown has begun */ /** non-cancellable shutdown has begun */
private volatile boolean _shutdownInProgress; private volatile boolean _shutdownInProgress;
private final I2PThread _gracefulShutdownDetector; private I2PThread _gracefulShutdownDetector;
private final RouterWatchdog _watchdog; private RouterWatchdog _watchdog;
private final Thread _watchdogThread; private Thread _watchdogThread;
public final static String PROP_CONFIG_FILE = "router.configLocation"; public final static String PROP_CONFIG_FILE = "router.configLocation";
@ -128,9 +128,17 @@ public class Router implements RouterClock.ClockShiftListener {
System.setProperty("Dorg.mortbay.util.FileResource.checkAliases", "true"); System.setProperty("Dorg.mortbay.util.FileResource.checkAliases", "true");
} }
/**
* Instantiation only. Starts no threads. Does not install updates.
* RouterContext is created but not initialized.
* You must call runRouter() after any constructor to start things up.
*/
public Router() { this(null, null); } public Router() { this(null, null); }
public Router(Properties envProps) { this(null, envProps); } public Router(Properties envProps) { this(null, envProps); }
public Router(String configFilename) { this(configFilename, null); } public Router(String configFilename) { this(configFilename, null); }
public Router(String configFilename, Properties envProps) { public Router(String configFilename, Properties envProps) {
_gracefulExitCode = -1; _gracefulExitCode = -1;
_config = new ConcurrentHashMap(); _config = new ConcurrentHashMap();
@ -235,14 +243,16 @@ public class Router implements RouterClock.ClockShiftListener {
_config.put("router.updateLastInstalled", now); _config.put("router.updateLastInstalled", now);
saveConfig(); saveConfig();
} }
// ********* Start no threads before here ********* //
}
// This is here so that we can get the directory location from the context /**
// for the zip file and the base location to unzip to. * Initializes the RouterContext.
// If it does an update, it never returns. * Starts some threads. Does not install updates.
// I guess it's better to have the other-router check above this, we don't want to * All this was in the constructor.
// overwrite an existing running router's jar files. Other than ours. * @since 0.8.12
installUpdates(); */
private void startupStuff() {
// ********* Start no threads before here ********* // // ********* Start no threads before here ********* //
// //
// NOW we can start the ping file thread. // NOW we can start the ping file thread.
@ -372,7 +382,14 @@ public class Router implements RouterClock.ClockShiftListener {
public RouterContext getContext() { return _context; } public RouterContext getContext() { return _context; }
/**
* Initializes the RouterContext.
* Starts the threads. Does not install updates.
*/
void runRouter() { void runRouter() {
if (_isAlive)
throw new IllegalStateException();
startupStuff();
_isAlive = true; _isAlive = true;
_started = _context.clock().now(); _started = _context.clock().now();
try { try {
@ -1266,13 +1283,18 @@ public class Router implements RouterClock.ClockShiftListener {
public static void main(String args[]) { public static void main(String args[]) {
System.out.println("Starting I2P " + RouterVersion.FULL_VERSION); System.out.println("Starting I2P " + RouterVersion.FULL_VERSION);
// installUpdates() moved to constructor so we can get file locations from the context
// installUpdates();
//verifyWrapperConfig(); //verifyWrapperConfig();
Router r = new Router(); Router r = new Router();
if ( (args != null) && (args.length == 1) && ("rebuild".equals(args[0])) ) { if ( (args != null) && (args.length == 1) && ("rebuild".equals(args[0])) ) {
r.rebuildNewIdentity(); r.rebuildNewIdentity();
} else { } else {
// This is here so that we can get the directory location from the context
// for the zip file and the base location to unzip to.
// If it does an update, it never returns.
// I guess it's better to have the other-router check above this, we don't want to
// overwrite an existing running router's jar files. Other than ours.
r.installUpdates();
// ********* Start no threads before here ********* //
r.runRouter(); r.runRouter();
} }
} }
@ -1281,6 +1303,7 @@ public class Router implements RouterClock.ClockShiftListener {
private static final String DELETE_FILE = "deletelist.txt"; private static final String DELETE_FILE = "deletelist.txt";
/** /**
* Context must be available.
* Unzip update file found in the router dir OR base dir, to the base dir * Unzip update file found in the router dir OR base dir, to the base dir
* *
* If we can't write to the base dir, complain. * If we can't write to the base dir, complain.

View File

@ -41,7 +41,7 @@ public class PublishLocalRouterInfoJob extends JobImpl {
RouterInfo ri = new RouterInfo(getContext().router().getRouterInfo()); RouterInfo ri = new RouterInfo(getContext().router().getRouterInfo());
if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
_log.debug("Old routerInfo contains " + ri.getAddresses().size() _log.debug("Old routerInfo contains " + ri.getAddresses().size()
+ " addresses and " + ri.getOptions().size() + " options"); + " addresses and " + ri.getOptionsMap().size() + " options");
Properties stats = getContext().statPublisher().publishStatistics(); Properties stats = getContext().statPublisher().publishStatistics();
stats.setProperty(RouterInfo.PROP_NETWORK_ID, ""+Router.NETWORK_ID); stats.setProperty(RouterInfo.PROP_NETWORK_ID, ""+Router.NETWORK_ID);
try { try {
@ -60,7 +60,7 @@ public class PublishLocalRouterInfoJob extends JobImpl {
getContext().router().setRouterInfo(ri); getContext().router().setRouterInfo(ri);
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info("Newly updated routerInfo is published with " + stats.size() _log.info("Newly updated routerInfo is published with " + stats.size()
+ "/" + ri.getOptions().size() + " options on " + "/" + ri.getOptionsMap().size() + " options on "
+ new Date(ri.getPublished())); + new Date(ri.getPublished()));
try { try {
getContext().netDb().publish(ri); getContext().netDb().publish(ri);

View File

@ -133,8 +133,7 @@ class FloodfillMonitorJob extends JobImpl {
if (ra == null) if (ra == null)
happy = false; happy = false;
else { else {
Properties props = ra.getOptions(); if (ra.getOption("ihost0") != null)
if (props == null || props.getProperty("ihost0") != null)
happy = false; happy = false;
} }
} }

View File

@ -786,8 +786,7 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
RouterAddress ra = routerInfo.getTargetAddress("SSU"); RouterAddress ra = routerInfo.getTargetAddress("SSU");
if (ra != null) { if (ra != null) {
// Introducers change often, introducee will ping introducer for 2 hours // Introducers change often, introducee will ping introducer for 2 hours
Properties props = ra.getOptions(); if (ra.getOption("ihost0") != null)
if (props != null && props.getProperty("ihost0") != null)
return "Peer " + key.toBase64() + " published > 75m ago with SSU Introducers"; return "Peer " + key.toBase64() + " published > 75m ago with SSU Introducers";
if (routerInfo.getTargetAddress("NTCP") == null) if (routerInfo.getTargetAddress("NTCP") == null)
return "Peer " + key.toBase64() + " published > 75m ago, SSU only without introducers"; return "Peer " + key.toBase64() + " published > 75m ago, SSU only without introducers";
@ -822,10 +821,10 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
if (err != null) if (err != null)
throw new IllegalArgumentException("Invalid store attempt - " + err); throw new IllegalArgumentException("Invalid store attempt - " + err);
//if (_log.shouldLog(Log.DEBUG)) if (_log.shouldLog(Log.DEBUG))
// _log.debug("RouterInfo " + key.toBase64() + " is stored with " _log.debug("RouterInfo " + key.toBase64() + " is stored with "
// + routerInfo.getOptions().size() + " options on " + routerInfo.getOptionsMap().size() + " options on "
// + new Date(routerInfo.getPublished())); + new Date(routerInfo.getPublished()));
_context.peerManager().setCapabilities(key, routerInfo.getCapabilities()); _context.peerManager().setCapabilities(key, routerInfo.getCapabilities());
_ds.put(key, routerInfo, persist); _ds.put(key, routerInfo, persist);

View File

@ -701,8 +701,7 @@ public class ProfileOrganizer {
continue; continue;
} }
// This is the quick way of doing UDPAddress.getIntroducerCount() > 0 // This is the quick way of doing UDPAddress.getIntroducerCount() > 0
Properties props = ra.getOptions(); if (ra.getOption("ihost0") != null)
if (props != null && props.getProperty("ihost0") != null)
l.add(peer); l.add(peer);
} }
} }
@ -1263,9 +1262,7 @@ public class ProfileOrganizer {
if (paddr == null) if (paddr == null)
return rv; return rv;
for (RouterAddress pa : paddr) { for (RouterAddress pa : paddr) {
Properties pprops = pa.getOptions(); String phost = pa.getOption("host");
if (pprops == null) continue;
String phost = pprops.getProperty("host");
if (phost == null) continue; if (phost == null) continue;
InetAddress pi; InetAddress pi;
try { try {

View File

@ -343,15 +343,12 @@ public class TransportManager implements TransportEventListener {
for (Transport t : _transports.values()) { for (Transport t : _transports.values()) {
int port = t.getRequestedPort(); int port = t.getRequestedPort();
if (t.getCurrentAddress() != null) { if (t.getCurrentAddress() != null) {
Properties opts = t.getCurrentAddress().getOptions(); String s = t.getCurrentAddress().getOption("port");
if (opts != null) {
String s = opts.getProperty("port");
if (s != null) { if (s != null) {
try { try {
port = Integer.parseInt(s); port = Integer.parseInt(s);
} catch (NumberFormatException nfe) {} } catch (NumberFormatException nfe) {}
} }
}
} }
// Use UDP port for NTCP too - see comment in NTCPTransport.getRequestedPort() for why this is here // Use UDP port for NTCP too - see comment in NTCPTransport.getRequestedPort() for why this is here
if (t.getStyle().equals(NTCPTransport.STYLE) && port <= 0 && if (t.getStyle().equals(NTCPTransport.STYLE) && port <= 0 &&

View File

@ -57,13 +57,13 @@ public class NTCPAddress {
_port = -1; _port = -1;
return; return;
} }
String host = addr.getOptions().getProperty(PROP_HOST); String host = addr.getOption(PROP_HOST);
if (host == null) { if (host == null) {
_host = null; _host = null;
_port = -1; _port = -1;
} else { } else {
_host = host.trim(); _host = host.trim();
String port = addr.getOptions().getProperty(PROP_PORT); String port = addr.getOption(PROP_PORT);
if ( (port != null) && (port.trim().length() > 0) && !("null".equals(port)) ) { if ( (port != null) && (port.trim().length() > 0) && !("null".equals(port)) ) {
try { try {
_port = Integer.parseInt(port.trim()); _port = Integer.parseInt(port.trim());
@ -156,9 +156,7 @@ public class NTCPAddress {
public boolean equals(RouterAddress addr) { public boolean equals(RouterAddress addr) {
if (addr == null) return false; if (addr == null) return false;
Properties opts = addr.getOptions(); return ( (_host.equals(addr.getOption(PROP_HOST))) &&
if (opts == null) return false; (Integer.toString(_port).equals(addr.getOption(PROP_PORT))) );
return ( (_host.equals(opts.getProperty(PROP_HOST))) &&
(Integer.toString(_port).equals(opts.getProperty(PROP_PORT))) );
} }
} }

View File

@ -64,31 +64,30 @@ public class UDPAddress {
private void parse(RouterAddress addr) { private void parse(RouterAddress addr) {
if (addr == null) return; if (addr == null) return;
Properties opts = addr.getOptions(); _host = addr.getOption(PROP_HOST);
_host = opts.getProperty(PROP_HOST);
if (_host != null) _host = _host.trim(); if (_host != null) _host = _host.trim();
try { try {
String port = opts.getProperty(PROP_PORT); String port = addr.getOption(PROP_PORT);
if (port != null) if (port != null)
_port = Integer.parseInt(port); _port = Integer.parseInt(port);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
_port = -1; _port = -1;
} }
String key = opts.getProperty(PROP_INTRO_KEY); String key = addr.getOption(PROP_INTRO_KEY);
if (key != null) if (key != null)
_introKey = Base64.decode(key.trim()); _introKey = Base64.decode(key.trim());
for (int i = MAX_INTRODUCERS; i >= 0; i--) { for (int i = MAX_INTRODUCERS; i >= 0; i--) {
String host = opts.getProperty(PROP_INTRO_HOST_PREFIX + i); String host = addr.getOption(PROP_INTRO_HOST_PREFIX + i);
if (host == null) continue; if (host == null) continue;
String port = opts.getProperty(PROP_INTRO_PORT_PREFIX + i); String port = addr.getOption(PROP_INTRO_PORT_PREFIX + i);
if (port == null) continue; if (port == null) continue;
String k = opts.getProperty(PROP_INTRO_KEY_PREFIX + i); String k = addr.getOption(PROP_INTRO_KEY_PREFIX + i);
if (k == null) continue; if (k == null) continue;
byte ikey[] = Base64.decode(k); byte ikey[] = Base64.decode(k);
if ( (ikey == null) || (ikey.length != SessionKey.KEYSIZE_BYTES) ) if ( (ikey == null) || (ikey.length != SessionKey.KEYSIZE_BYTES) )
continue; continue;
String t = opts.getProperty(PROP_INTRO_TAG_PREFIX + i); String t = addr.getOption(PROP_INTRO_TAG_PREFIX + i);
if (t == null) continue; if (t == null) continue;
int p = -1; int p = -1;
try { try {