2005-11-26 Raccoon23
* Added support for 'dynamic keys' mode, where the router creates a new router identity whenever it detects a substantial change in its public address (read: SSU IP or port). This only offers minimal additional protection against trivial attackers, but should provide functional improvement for people who have periodic IP changes, since their new router address would not be shitlisted while their old one would be. * Added further infrastructure for restricted route operation, but its use is not recommended.
This commit is contained in:
@ -17,6 +17,10 @@ import java.util.Set;
|
||||
|
||||
import net.i2p.time.Timestamper;
|
||||
import net.i2p.router.transport.udp.UDPTransport;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.router.web.ConfigServiceHandler.UpdateWrapperManagerTask;
|
||||
import net.i2p.router.web.ConfigServiceHandler.UpdateWrapperManagerAndRekeyTask;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the main config form and act
|
||||
@ -31,6 +35,8 @@ public class ConfigNetHandler extends FormHandler {
|
||||
private boolean _recheckReachabilityRequested;
|
||||
private boolean _timeSyncEnabled;
|
||||
private boolean _requireIntroductions;
|
||||
private boolean _hiddenMode;
|
||||
private boolean _dynamicKeys;
|
||||
private String _tcpPort;
|
||||
private String _udpPort;
|
||||
private String _inboundRate;
|
||||
@ -62,6 +68,8 @@ public class ConfigNetHandler extends FormHandler {
|
||||
public void setEnabletimesync(String moo) { _timeSyncEnabled = true; }
|
||||
public void setRecheckReachability(String moo) { _recheckReachabilityRequested = true; }
|
||||
public void setRequireIntroductions(String moo) { _requireIntroductions = true; }
|
||||
public void setHiddenMode(String moo) { _hiddenMode = true; }
|
||||
public void setDynamicKeys(String moo) { _dynamicKeys = true; }
|
||||
|
||||
public void setHostname(String hostname) {
|
||||
_hostname = (hostname != null ? hostname.trim() : null);
|
||||
@ -264,6 +272,28 @@ public class ConfigNetHandler extends FormHandler {
|
||||
}
|
||||
}
|
||||
|
||||
// If hidden mode value changes, restart is required
|
||||
if (_hiddenMode && "false".equalsIgnoreCase(_context.getProperty(Router.PROP_HIDDEN, "false"))) {
|
||||
_context.router().setConfigSetting(Router.PROP_HIDDEN, "true");
|
||||
_context.router().getRouterInfo().addCapability(RouterInfo.CAPABILITY_HIDDEN);
|
||||
addFormNotice("Gracefully restarting into Hidden Router Mode. Make sure you have no 0-1 length "
|
||||
+ "<a href=\"configtunnels.jsp\">tunnels!</a>");
|
||||
hiddenSwitch();
|
||||
}
|
||||
|
||||
if (!_hiddenMode && "true".equalsIgnoreCase(_context.getProperty(Router.PROP_HIDDEN, "false"))) {
|
||||
_context.router().removeConfigSetting(Router.PROP_HIDDEN);
|
||||
_context.router().getRouterInfo().delCapability(RouterInfo.CAPABILITY_HIDDEN);
|
||||
addFormNotice("Gracefully restarting to exit Hidden Router Mode");
|
||||
hiddenSwitch();
|
||||
}
|
||||
|
||||
if (_dynamicKeys) {
|
||||
_context.router().setConfigSetting(Router.PROP_DYNAMIC_KEYS, "true");
|
||||
} else {
|
||||
_context.router().removeConfigSetting(Router.PROP_DYNAMIC_KEYS);
|
||||
}
|
||||
|
||||
if (_requireIntroductions) {
|
||||
_context.router().setConfigSetting(UDPTransport.PROP_FORCE_INTRODUCERS, "true");
|
||||
addFormNotice("Requiring SSU introduers");
|
||||
@ -291,6 +321,12 @@ public class ConfigNetHandler extends FormHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void hiddenSwitch() {
|
||||
// Full restart required to generate new keys
|
||||
_context.router().addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL_RESTART));
|
||||
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
|
||||
}
|
||||
|
||||
private void updateRates() {
|
||||
boolean updated = false;
|
||||
if ( (_inboundRate != null) && (_inboundRate.length() > 0) ) {
|
||||
|
@ -6,6 +6,7 @@ import net.i2p.router.CommSystemFacade;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.router.transport.udp.UDPAddress;
|
||||
import net.i2p.router.transport.udp.UDPTransport;
|
||||
import net.i2p.router.Router;
|
||||
|
||||
public class ConfigNetHelper {
|
||||
private RouterContext _context;
|
||||
@ -63,6 +64,22 @@ public class ConfigNetHelper {
|
||||
return " checked ";
|
||||
}
|
||||
|
||||
public String getHiddenModeChecked() {
|
||||
String enabled = _context.getProperty(Router.PROP_HIDDEN, "false");
|
||||
if ( (enabled != null) && ("true".equalsIgnoreCase(enabled)) )
|
||||
return " checked ";
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getDynamicKeysChecked() {
|
||||
String enabled = _context.getProperty(Router.PROP_DYNAMIC_KEYS, "false");
|
||||
if ( (enabled != null) && ("true".equalsIgnoreCase(enabled)) )
|
||||
return " checked ";
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getRequireIntroductionsChecked() {
|
||||
short status = _context.commSystem().getReachabilityStatus();
|
||||
switch (status) {
|
||||
|
@ -34,6 +34,21 @@ public class ConfigServiceHandler extends FormHandler {
|
||||
}
|
||||
}
|
||||
|
||||
public static class UpdateWrapperManagerAndRekeyTask implements Runnable {
|
||||
private int _exitCode;
|
||||
public UpdateWrapperManagerAndRekeyTask(int exitCode) {
|
||||
_exitCode = exitCode;
|
||||
}
|
||||
public void run() {
|
||||
try {
|
||||
Router.killKeys();
|
||||
WrapperManager.signalStopped(_exitCode);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
if (_action == null) return;
|
||||
|
||||
@ -56,6 +71,14 @@ public class ConfigServiceHandler extends FormHandler {
|
||||
_context.router().addShutdownTask(new UpdateWrapperManagerTask(Router.EXIT_HARD_RESTART));
|
||||
_context.router().shutdown(Router.EXIT_HARD_RESTART);
|
||||
addFormNotice("Hard restart requested");
|
||||
} else if ("Rekey and Restart".equals(_action)) {
|
||||
addFormNotice("Rekeying after graceful restart");
|
||||
_context.router().addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL_RESTART));
|
||||
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL_RESTART);
|
||||
} else if ("Rekey and Shutdown".equals(_action)) {
|
||||
addFormNotice("Rekeying after graceful shutdown");
|
||||
_context.router().addShutdownTask(new UpdateWrapperManagerAndRekeyTask(Router.EXIT_GRACEFUL));
|
||||
_context.router().shutdownGracefully(Router.EXIT_GRACEFUL);
|
||||
} else if ("Run I2P on startup".equals(_action)) {
|
||||
installService();
|
||||
} else if ("Don't run I2P on startup".equals(_action)) {
|
||||
|
@ -58,6 +58,23 @@
|
||||
<jsp:getProperty name="nethelper" property="sharePercentageBox" /><br />
|
||||
Sharing a higher percentage will improve your anonymity and help the network
|
||||
<hr />
|
||||
<b>Dynamic Router Keys: </b>
|
||||
<input type="checkbox" name="dynamicKeys" value="true" <jsp:getProperty name="nethelper" property="dynamicKeysChecked" /> /><br />
|
||||
<p>
|
||||
This setting causes your router identity to be regenerated every time your IP address
|
||||
changes. If you have a dynamic IP this option can speed up your reintegration into
|
||||
the network (since people will have shitlisted your old router identity), and, for
|
||||
very weak adversaries, help frustrate trivial
|
||||
<a href="http://www.i2p.net/how_threatmodel#intersection">intersection
|
||||
attacks</a> against the NetDB. Your different router identities would only be
|
||||
'hidden' among other I2P users at your ISP, and further analysis would link
|
||||
the router identities further.</p>
|
||||
<p>Note that when I2P detects an IP address change, it will automatically
|
||||
initiate a restart in order to rekey and to disconnect from peers before they
|
||||
update their profiles - any long lasting client connections will be disconnected,
|
||||
though such would likely already be the case anyway, since the IP address changed.
|
||||
</p>
|
||||
<hr />
|
||||
<input type="submit" name="save" value="Save changes" /> <input type="reset" value="Cancel" /><br />
|
||||
</form>
|
||||
</div>
|
||||
|
@ -51,6 +51,8 @@ public class RouterInfo extends DataStructureImpl {
|
||||
|
||||
public static final String PROP_NETWORK_ID = "netId";
|
||||
public static final String PROP_CAPABILITIES = "caps";
|
||||
public static final char CAPABILITY_HIDDEN = 'H';
|
||||
|
||||
|
||||
public RouterInfo() {
|
||||
setIdentity(null);
|
||||
@ -250,12 +252,21 @@ public class RouterInfo extends DataStructureImpl {
|
||||
try {
|
||||
_identity.writeBytes(out);
|
||||
DataHelper.writeDate(out, new Date(_published));
|
||||
DataHelper.writeLong(out, 1, _addresses.size());
|
||||
List addresses = DataHelper.sortStructures(_addresses);
|
||||
for (Iterator iter = addresses.iterator(); iter.hasNext();) {
|
||||
RouterAddress addr = (RouterAddress) iter.next();
|
||||
addr.writeBytes(out);
|
||||
if (isHidden()) {
|
||||
// Do not send IP address to peers in hidden mode
|
||||
DataHelper.writeLong(out, 1, 0);
|
||||
} else {
|
||||
DataHelper.writeLong(out, 1, _addresses.size());
|
||||
List addresses = DataHelper.sortStructures(_addresses);
|
||||
for (Iterator iter = addresses.iterator(); iter.hasNext();) {
|
||||
RouterAddress addr = (RouterAddress) iter.next();
|
||||
addr.writeBytes(out);
|
||||
}
|
||||
}
|
||||
// XXX: what about peers?
|
||||
// answer: they're always empty... they're a placeholder for one particular
|
||||
// method of trusted links, which isn't implemented in the router
|
||||
// at the moment, and may not be later.
|
||||
DataHelper.writeLong(out, 1, _peers.size());
|
||||
List peers = DataHelper.sortStructures(_peers);
|
||||
for (Iterator iter = peers.iterator(); iter.hasNext();) {
|
||||
@ -316,6 +327,13 @@ public class RouterInfo extends DataStructureImpl {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a hidden node?
|
||||
*/
|
||||
public boolean isHidden() {
|
||||
return (getCapabilities().indexOf(CAPABILITY_HIDDEN) != -1);
|
||||
}
|
||||
|
||||
public void addCapability(char cap) {
|
||||
if (_options == null) _options = new OrderedProperties();
|
||||
synchronized (_options) {
|
||||
@ -327,6 +345,25 @@ public class RouterInfo extends DataStructureImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public void delCapability(char cap) {
|
||||
if (_options == null) return;
|
||||
synchronized (_options) {
|
||||
String caps = _options.getProperty(PROP_CAPABILITIES);
|
||||
int idx;
|
||||
if (caps == null) {
|
||||
return;
|
||||
} else if ((idx = caps.indexOf(cap)) == -1) {
|
||||
return;
|
||||
} else {
|
||||
StringBuffer buf = new StringBuffer(caps);
|
||||
while ( (idx = buf.indexOf(""+cap)) != -1)
|
||||
buf.deleteCharAt(idx);
|
||||
_options.setProperty(PROP_CAPABILITIES, buf.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the routing key for the structure using the current modifier in the RoutingKeyGenerator.
|
||||
* This only calculates a new one when necessary though (if the generator's key modifier changes)
|
||||
|
12
history.txt
12
history.txt
@ -1,4 +1,14 @@
|
||||
$Id: history.txt,v 1.329 2005/11/25 06:06:03 jrandom Exp $
|
||||
$Id: history.txt,v 1.330 2005/11/26 00:05:54 jrandom Exp $
|
||||
|
||||
2005-11-26 Raccoon23
|
||||
* Added support for 'dynamic keys' mode, where the router creates a new
|
||||
router identity whenever it detects a substantial change in its public
|
||||
address (read: SSU IP or port). This only offers minimal additional
|
||||
protection against trivial attackers, but should provide functional
|
||||
improvement for people who have periodic IP changes, since their new
|
||||
router address would not be shitlisted while their old one would be.
|
||||
* Added further infrastructure for restricted route operation, but its use
|
||||
is not recommended.
|
||||
|
||||
2005-11-25 jrandom
|
||||
* Further Syndie UI cleanups
|
||||
|
@ -15,6 +15,7 @@ import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashSet;
|
||||
@ -29,6 +30,7 @@ import net.i2p.CoreVersion;
|
||||
import net.i2p.crypto.DHSessionKeyBuilder;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.RouterAddress;
|
||||
import net.i2p.data.RouterInfo;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.data.i2np.GarlicMessage;
|
||||
@ -36,6 +38,8 @@ import net.i2p.data.i2np.GarlicMessage;
|
||||
import net.i2p.router.message.GarlicMessageHandler;
|
||||
//import net.i2p.router.message.TunnelMessageHandler;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.transport.udp.UDPTransport;
|
||||
import net.i2p.router.transport.udp.UDPAddress;
|
||||
import net.i2p.router.startup.StartupJob;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
@ -73,6 +77,8 @@ public class Router {
|
||||
/** used to differentiate routerInfo files on different networks */
|
||||
public static final int NETWORK_ID = 1;
|
||||
|
||||
public final static String PROP_HIDDEN = "router.hiddenMode";
|
||||
public final static String PROP_DYNAMIC_KEYS = "router.dynamicKeys";
|
||||
public final static String PROP_INFO_FILENAME = "router.info.location";
|
||||
public final static String PROP_INFO_FILENAME_DEFAULT = "router.info";
|
||||
public final static String PROP_KEYS_FILENAME = "router.keys.location";
|
||||
@ -212,6 +218,51 @@ public class Router {
|
||||
_context.jobQueue().addJob(new PersistRouterInfoJob());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when our RouterInfo is loaded by LoadRouterInfoJob
|
||||
* to store our most recently known address to determine if
|
||||
* it has changed while we were down.
|
||||
*/
|
||||
public boolean updateExternalAddress(Collection addrs, boolean reboot) {
|
||||
if ("false".equalsIgnoreCase(_context.getProperty(Router.PROP_DYNAMIC_KEYS, "false")))
|
||||
return false; // no one cares. pretend it didn't change
|
||||
boolean ret = false;
|
||||
for (Iterator i = addrs.iterator(); i.hasNext(); ) {
|
||||
RouterAddress addr = (RouterAddress)i.next();
|
||||
if (UDPTransport.STYLE.equalsIgnoreCase(addr.getTransportStyle()))
|
||||
ret = updateExternalAddress(addr, reboot);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by TransportImpl.replaceAddress to notify the router of an
|
||||
* address change. It is the caller's responsibility to make sure this
|
||||
* really is a substantial change.
|
||||
*
|
||||
*/
|
||||
public boolean updateExternalAddress(RouterAddress addr, boolean rebootRouter) {
|
||||
String newExternal = null;
|
||||
// TCP is often incorrectly initialized to 83.246.74.28 for some
|
||||
// reason. Numerous hosts in the netdb report this address for TCP.
|
||||
// It is also easier to lie over the TCP transport. So only trust UDP.
|
||||
if (!UDPTransport.STYLE.equalsIgnoreCase(addr.getTransportStyle()))
|
||||
return false;
|
||||
|
||||
if ("false".equalsIgnoreCase(_context.getProperty(Router.PROP_DYNAMIC_KEYS, "false")))
|
||||
return false; // no one cares. pretend it didn't change
|
||||
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Rekeying and restarting due to " + addr.getTransportStyle()
|
||||
+ " address update. new address: " + addr);
|
||||
if (rebootRouter) {
|
||||
_context.router().rebuildNewIdentity();
|
||||
} else {
|
||||
_context.router().killKeys();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the router has tried to communicate with another router who is running a higher
|
||||
* incompatible protocol version.
|
||||
@ -237,6 +288,9 @@ public class Router {
|
||||
readConfig();
|
||||
|
||||
setupHandlers();
|
||||
if ("true".equalsIgnoreCase(_context.getProperty(Router.PROP_HIDDEN, "false")))
|
||||
killKeys();
|
||||
|
||||
_context.messageValidator().startup();
|
||||
_context.tunnelDispatcher().startup();
|
||||
_context.inNetMessagePool().startup();
|
||||
@ -319,6 +373,10 @@ public class Router {
|
||||
ri.setAddresses(_context.commSystem().createAddresses());
|
||||
if (FloodfillNetworkDatabaseFacade.floodfillEnabled(_context))
|
||||
ri.addCapability(FloodfillNetworkDatabaseFacade.CAPACITY_FLOODFILL);
|
||||
if("true".equalsIgnoreCase(_context.getProperty(Router.PROP_HIDDEN, "false"))) {
|
||||
ri.addCapability(RouterInfo.CAPABILITY_HIDDEN);
|
||||
}
|
||||
|
||||
addReachabilityCapability(ri);
|
||||
SigningPrivateKey key = _context.keyManager().getSigningPrivateKey();
|
||||
if (key == null) {
|
||||
@ -374,18 +432,15 @@ public class Router {
|
||||
*/
|
||||
private static final String _rebuildFiles[] = new String[] { "router.info",
|
||||
"router.keys",
|
||||
"netDb/my.info",
|
||||
"connectionTag.keys",
|
||||
"keyBackup/privateEncryption.key",
|
||||
"keyBackup/privateSigning.key",
|
||||
"keyBackup/publicEncryption.key",
|
||||
"keyBackup/publicSigning.key",
|
||||
"sessionKeys.dat" };
|
||||
/**
|
||||
* Rebuild a new identity the hard way - delete all of our old identity
|
||||
* files, then reboot the router.
|
||||
*
|
||||
*/
|
||||
public void rebuildNewIdentity() {
|
||||
|
||||
public static void killKeys() {
|
||||
for (int i = 0; i < _rebuildFiles.length; i++) {
|
||||
File f = new File(_rebuildFiles[i]);
|
||||
if (f.exists()) {
|
||||
@ -396,9 +451,16 @@ public class Router {
|
||||
System.out.println("ERROR: Could not remove old identity file: " + _rebuildFiles[i]);
|
||||
}
|
||||
}
|
||||
System.out.println("INFO: Restarting the router after removing any old identity files");
|
||||
}
|
||||
/**
|
||||
* Rebuild a new identity the hard way - delete all of our old identity
|
||||
* files, then reboot the router.
|
||||
*
|
||||
*/
|
||||
public void rebuildNewIdentity() {
|
||||
killKeys();
|
||||
// hard and ugly
|
||||
System.exit(EXIT_HARD_RESTART);
|
||||
finalShutdown(EXIT_HARD_RESTART);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -802,6 +864,10 @@ public class Router {
|
||||
} catch (Throwable t) {
|
||||
_log.log(Log.CRIT, "Error running shutdown task", t);
|
||||
}
|
||||
finalShutdown(exitCode);
|
||||
}
|
||||
|
||||
public void finalShutdown(int exitCode) {
|
||||
_log.log(Log.CRIT, "Shutdown(" + exitCode + ") complete", new Exception("Shutdown"));
|
||||
try { _context.logManager().shutdown(); } catch (Throwable t) { }
|
||||
File f = new File(getPingFile());
|
||||
|
@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
|
||||
*
|
||||
*/
|
||||
public class RouterVersion {
|
||||
public final static String ID = "$Revision: 1.297 $ $Date: 2005/11/25 06:06:02 $";
|
||||
public final static String ID = "$Revision: 1.298 $ $Date: 2005/11/26 00:05:53 $";
|
||||
public final static String VERSION = "0.6.1.5";
|
||||
public final static long BUILD = 8;
|
||||
public final static long BUILD = 9;
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Router version: " + VERSION + "-" + BUILD);
|
||||
System.out.println("Router ID: " + RouterVersion.ID);
|
||||
|
@ -61,7 +61,7 @@ public class Shitlist {
|
||||
}
|
||||
boolean wasAlready = false;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Shitlisting router " + peer.toBase64(), new Exception("Shitlist cause"));
|
||||
_log.info("Shitlisting router " + peer.toBase64(), new Exception("Shitlist cause: " + reason));
|
||||
|
||||
long period = SHITLIST_DURATION_MS + _context.random().nextLong(SHITLIST_DURATION_MS);
|
||||
PeerProfile prof = _context.profileOrganizer().getProfile(peer);
|
||||
|
@ -82,6 +82,15 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
|
||||
+ " (tunnel " + _message.getReplyTunnel() + ")");
|
||||
}
|
||||
|
||||
// If we are hidden we should not get queries, log and return
|
||||
if (getContext().router().getRouterInfo().isHidden()) {
|
||||
if (_log.shouldLog(Log.ERROR)) {
|
||||
_log.error("Uninvited dbLookup received with replies going to " + fromKey
|
||||
+ " (tunnel " + _message.getReplyTunnel() + ")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
LeaseSet ls = getContext().netDb().lookupLeaseSetLocally(_message.getSearchKey());
|
||||
if (ls != null) {
|
||||
boolean publish = getContext().clientManager().shouldPublishLeaseSet(_message.getSearchKey());
|
||||
@ -117,6 +126,14 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
|
||||
Set us = new HashSet(1);
|
||||
us.add(getContext().router().getRouterInfo());
|
||||
sendClosest(_message.getSearchKey(), us, fromKey, _message.getReplyTunnel());
|
||||
//} else if (info.isHidden()) {
|
||||
// // Don't return hidden nodes
|
||||
// ERR: we don't want to explicitly reject lookups for hidden nodes, since they
|
||||
// may have just sent the hidden mode to only us and bundled a lookup with
|
||||
// a payload targetting some hidden destination (and if we refused to answer,
|
||||
// yet answered the bundled data message [e.g. HTTP GET], they'd know that
|
||||
// *we* were hosting that destination). To operate safely,
|
||||
// perhaps we should refuse to honor lookups bundled down client tunnels?
|
||||
} else {
|
||||
// send that routerInfo to the _message.getFromHash peer
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@ -129,6 +146,16 @@ public class HandleDatabaseLookupMessageJob extends JobImpl {
|
||||
Set routerInfoSet = getContext().netDb().findNearestRouters(_message.getSearchKey(),
|
||||
MAX_ROUTERS_RETURNED,
|
||||
_message.getDontIncludePeers());
|
||||
|
||||
// ERR: see above
|
||||
// // Remove hidden nodes from set..
|
||||
// for (Iterator iter = routerInfoSet.iterator(); iter.hasNext();) {
|
||||
// RouterInfo peer = (RouterInfo)iter.next();
|
||||
// if (peer.isHidden()) {
|
||||
// iter.remove();
|
||||
// }
|
||||
// }
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("We do not have key " + _message.getSearchKey().toBase64() +
|
||||
" locally. sending back " + routerInfoSet.size() + " peers to " + fromKey.toBase64());
|
||||
|
@ -47,6 +47,15 @@ public class HandleDatabaseStoreMessageJob extends JobImpl {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Handling database store message");
|
||||
|
||||
// ERR: see comments regarding hidden mode in HandleDatabaseLookupMessageJob.
|
||||
// // If we are a hidden peer, log and return
|
||||
// if (getContext().router().getRouterInfo().isHidden()) {
|
||||
// if (_log.shouldLog(Log.ERROR)) {
|
||||
// _log.error("Uninvited dbStore received (tunnel " + _message.getReplyTunnel() + ")");
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
|
||||
String invalidMessage = null;
|
||||
boolean wasNew = false;
|
||||
if (_message.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET) {
|
||||
|
@ -47,6 +47,10 @@ public class PublishLocalRouterInfoJob extends JobImpl {
|
||||
ri.setAddresses(getContext().commSystem().createAddresses());
|
||||
if (FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext()))
|
||||
ri.addCapability(FloodfillNetworkDatabaseFacade.CAPACITY_FLOODFILL);
|
||||
|
||||
if ("true".equalsIgnoreCase(getContext().getProperty(Router.PROP_HIDDEN, "false")))
|
||||
ri.addCapability(RouterInfo.CAPABILITY_HIDDEN);
|
||||
|
||||
getContext().router().addReachabilityCapability(ri);
|
||||
SigningPrivateKey key = getContext().keyManager().getSigningPrivateKey();
|
||||
if (key == null) {
|
||||
|
@ -31,6 +31,7 @@ public class FloodfillNetworkDatabaseFacade extends KademliaNetworkDatabaseFacad
|
||||
*/
|
||||
public void publish(RouterInfo localRouterInfo) throws IllegalArgumentException {
|
||||
if (localRouterInfo == null) throw new IllegalArgumentException("wtf, null localRouterInfo?");
|
||||
if (localRouterInfo.isHidden()) return; // DE-nied!
|
||||
super.publish(localRouterInfo);
|
||||
sendStore(localRouterInfo.getIdentity().calculateHash(), localRouterInfo, null, null, PUBLISH_TIMEOUT, null);
|
||||
}
|
||||
|
@ -95,6 +95,8 @@ public class HandleFloodfillDatabaseStoreMessageJob extends JobImpl {
|
||||
if (FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext()) && (_message.getReplyToken() > 0) ) {
|
||||
if (_message.getValueType() == DatabaseStoreMessage.KEY_TYPE_LEASESET)
|
||||
_facade.flood(_message.getLeaseSet());
|
||||
// ERR: see comment in HandleDatabaseLookupMessageJob regarding hidden mode
|
||||
//else if (!_message.getRouterInfo().isHidden())
|
||||
else
|
||||
_facade.flood(_message.getRouterInfo());
|
||||
}
|
||||
|
@ -504,12 +504,13 @@ public class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacade {
|
||||
*/
|
||||
public void publish(RouterInfo localRouterInfo) throws IllegalArgumentException {
|
||||
if (!_initialized) return;
|
||||
writeMyInfo(localRouterInfo);
|
||||
if (localRouterInfo.isHidden()) return; // DE-nied!
|
||||
Hash h = localRouterInfo.getIdentity().getHash();
|
||||
store(h, localRouterInfo);
|
||||
synchronized (_explicitSendKeys) {
|
||||
_explicitSendKeys.add(h);
|
||||
}
|
||||
writeMyInfo(localRouterInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -243,7 +243,8 @@ class SearchJob extends JobImpl {
|
||||
+ peer + " : " + (ds == null ? "null" : ds.getClass().getName()));
|
||||
_state.replyTimeout(peer);
|
||||
} else {
|
||||
if (getContext().shitlist().isShitlisted(peer)) {
|
||||
if (((RouterInfo)ds).isHidden() ||
|
||||
getContext().shitlist().isShitlisted(peer)) {
|
||||
// dont bother
|
||||
} else {
|
||||
_state.addPending(peer);
|
||||
|
@ -176,6 +176,10 @@ class StoreJob extends JobImpl {
|
||||
//if (getContext().shitlist().isShitlisted(((RouterInfo)ds).getIdentity().calculateHash())) {
|
||||
// _state.addSkipped(peer);
|
||||
//} else {
|
||||
//
|
||||
// ERR: see hidden mode comments in HandleDatabaseLookupMessageJob
|
||||
// // Do not store to hidden nodes
|
||||
// if (!((RouterInfo)ds).isHidden()) {
|
||||
_state.addPending(peer);
|
||||
sendStore((RouterInfo)ds, peerTimeout);
|
||||
//}
|
||||
|
@ -54,10 +54,12 @@ public class CreateRouterInfoJob extends JobImpl {
|
||||
info.setAddresses(getContext().commSystem().createAddresses());
|
||||
Properties stats = getContext().statPublisher().publishStatistics();
|
||||
stats.setProperty(RouterInfo.PROP_NETWORK_ID, Router.NETWORK_ID+"");
|
||||
if (FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext()))
|
||||
info.addCapability(FloodfillNetworkDatabaseFacade.CAPACITY_FLOODFILL);
|
||||
getContext().router().addReachabilityCapability(info);
|
||||
info.setOptions(stats);
|
||||
if (FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext()))
|
||||
info.addCapability(FloodfillNetworkDatabaseFacade.CAPACITY_FLOODFILL);
|
||||
if ("true".equalsIgnoreCase(getContext().getProperty(Router.PROP_HIDDEN, "false")))
|
||||
info.addCapability(RouterInfo.CAPABILITY_HIDDEN);
|
||||
info.setPeers(new HashSet());
|
||||
info.setPublished(getCurrentPublishDate(getContext()));
|
||||
RouterIdentity ident = new RouterIdentity();
|
||||
|
@ -11,6 +11,8 @@ package net.i2p.router.startup;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.PrivateKey;
|
||||
@ -76,6 +78,7 @@ public class LoadRouterInfoJob extends JobImpl {
|
||||
fis1 = new FileInputStream(rif);
|
||||
info = new RouterInfo();
|
||||
info.readBytes(fis1);
|
||||
getContext().router().updateExternalAddress(info.getAddresses(), false);
|
||||
_log.debug("Reading in routerInfo from " + rif.getAbsolutePath() + " and it has " + info.getAddresses().size() + " addresses");
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,11 @@ public class RebuildRouterInfoJob extends JobImpl {
|
||||
info.setOptions(stats);
|
||||
if (FloodfillNetworkDatabaseFacade.floodfillEnabled(getContext()))
|
||||
info.addCapability(FloodfillNetworkDatabaseFacade.CAPACITY_FLOODFILL);
|
||||
|
||||
// Set caps=H for hidden mode routers
|
||||
if ("true".equalsIgnoreCase(getContext().getProperty(Router.PROP_HIDDEN, "false")))
|
||||
info.addCapability(RouterInfo.CAPABILITY_HIDDEN);
|
||||
|
||||
getContext().router().addReachabilityCapability(info);
|
||||
// info.setPeers(new HashSet()); // this would have the trusted peers
|
||||
info.setPublished(CreateRouterInfoJob.getCurrentPublishDate(getContext()));
|
||||
|
@ -815,14 +815,30 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
boolean wantsRebuild = false;
|
||||
if ( (_externalAddress == null) || !(_externalAddress.equals(addr)) )
|
||||
wantsRebuild = true;
|
||||
RouterAddress oldAddress = _externalAddress;
|
||||
_externalAddress = addr;
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Address rebuilt: " + addr);
|
||||
replaceAddress(addr);
|
||||
replaceAddress(addr, oldAddress);
|
||||
if (allowRebuildRouterInfo)
|
||||
_context.router().rebuildRouterInfo();
|
||||
}
|
||||
|
||||
protected void replaceAddress(RouterAddress address, RouterAddress oldAddress) {
|
||||
replaceAddress(address);
|
||||
if (oldAddress != null) {
|
||||
// fire a router.updateExternalAddress only if the address /really/ changed.
|
||||
// updating the introducers doesn't require a real change, only updating the
|
||||
// IP or port does.
|
||||
UDPAddress old = new UDPAddress(oldAddress);
|
||||
InetAddress oldHost = old.getHostAddress();
|
||||
UDPAddress newAddr = new UDPAddress(address);
|
||||
InetAddress newHost = newAddr.getHostAddress();
|
||||
if ( (old.getPort() != newAddr.getPort()) || (!oldHost.equals(newHost)) )
|
||||
_context.router().updateExternalAddress(address, true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean introducersRequired() {
|
||||
String forceIntroducers = _context.getProperty(PROP_FORCE_INTRODUCERS);
|
||||
if ( (forceIntroducers != null) && (Boolean.valueOf(forceIntroducers).booleanValue()) )
|
||||
|
@ -16,6 +16,7 @@ import net.i2p.router.HandlerJobBuilder;
|
||||
import net.i2p.router.Job;
|
||||
import net.i2p.router.JobImpl;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.message.GarlicMessageBuilder;
|
||||
import net.i2p.router.message.PayloadGarlicConfig;
|
||||
import net.i2p.router.message.SendMessageDirectJob;
|
||||
@ -76,6 +77,11 @@ public class HandleTunnelCreateMessageJob extends JobImpl {
|
||||
public static final int MAX_DURATION_SECONDS = 15*60;
|
||||
|
||||
private int shouldAccept() {
|
||||
// Should not see any initiation requests in hidden mode
|
||||
if ("true".equalsIgnoreCase(getContext().getProperty(Router.PROP_HIDDEN, "false"))) {
|
||||
return TunnelHistory.TUNNEL_REJECT_CRIT;
|
||||
}
|
||||
|
||||
if (_request.getDurationSeconds() >= MAX_DURATION_SECONDS)
|
||||
return TunnelHistory.TUNNEL_REJECT_CRIT;
|
||||
Hash nextRouter = _request.getNextRouter();
|
||||
|
@ -116,7 +116,12 @@ abstract class TunnelPeerSelector {
|
||||
* Pick peers that we want to avoid
|
||||
*/
|
||||
public Set getExclude(RouterContext ctx, boolean isInbound, boolean isExploratory) {
|
||||
if (filterUnreachable(ctx, isInbound, isExploratory)) {
|
||||
// we may want to update this to skip 'hidden' or 'unreachable' peers, but that
|
||||
// isn't safe, since they may publish one set of routerInfo to us and another to
|
||||
// other peers. the defaults for filterUnreachable has always been to return false,
|
||||
// but might as well make it explicit with a "false &&"
|
||||
|
||||
if (false && filterUnreachable(ctx, isInbound, isExploratory)) {
|
||||
List caps = ctx.peerManager().getPeersByCapability(Router.CAPABILITY_UNREACHABLE);
|
||||
if (caps == null) return new HashSet(0);
|
||||
HashSet rv = new HashSet(caps);
|
||||
|
Reference in New Issue
Block a user