use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class UDPTransport method externalAddressReceived.
/**
* Someone we tried to contact gave us what they think our IP address is.
* Right now, we just blindly trust them, changing our IP and port on a
* whim. this is not good ;)
*
* Slight enhancement - require two different peers in a row to agree
*
* Todo:
* - Much better tracking of troublemakers
* - Disable if we have good local address or UPnP
* - This gets harder if and when we publish multiple addresses, or IPv6
*
* @param from Hash of inbound destination
* @param ourIP publicly routable IPv4 only
* @param ourPort >= 1024
*/
void externalAddressReceived(Hash from, byte[] ourIP, int ourPort) {
boolean isValid = isValid(ourIP) && TransportUtil.isValidPort(ourPort);
boolean explicitSpecified = explicitAddressSpecified();
boolean inboundRecent = _lastInboundReceivedOn + ALLOW_IP_CHANGE_INTERVAL > System.currentTimeMillis();
if (_log.shouldLog(Log.INFO))
_log.info("External address received: " + Addresses.toString(ourIP, ourPort) + " from " + from + ", isValid? " + isValid + ", explicitSpecified? " + explicitSpecified + ", receivedInboundRecent? " + inboundRecent + " status " + _reachabilityStatus);
if (ourIP.length != 4) {
return;
}
if (explicitSpecified)
return;
String sources = _context.getProperty(PROP_SOURCES, DEFAULT_SOURCES);
if (!sources.contains("ssu"))
return;
if (!isValid) {
// ignore them
if (_log.shouldLog(Log.ERROR))
_log.error("The router " + from + " told us we have an invalid IP - " + Addresses.toString(ourIP, ourPort) + ". Lets throw tomatoes at them");
markUnreachable(from);
// _context.banlist().banlistRouter(from, "They said we had an invalid IP", STYLE);
return;
}
RouterAddress addr = getCurrentExternalAddress(false);
if (inboundRecent && addr != null && addr.getPort() > 0 && addr.getHost() != null) {
// leaving us thinking the second IP is still good.
if (_log.shouldLog(Log.INFO))
_log.info("Ignoring IP address suggestion, since we have received an inbound con recently");
} else {
// New IP
boolean changeIt = false;
synchronized (this) {
if (from.equals(_lastFrom) || !eq(_lastOurIP, _lastOurPort, ourIP, ourPort)) {
_lastFrom = from;
_lastOurIP = ourIP;
_lastOurPort = ourPort;
if (_log.shouldLog(Log.INFO))
_log.info("The router " + from + " told us we have a new IP - " + Addresses.toString(ourIP, ourPort) + ". Wait until somebody else tells us the same thing.");
} else {
_lastFrom = from;
_lastOurIP = ourIP;
_lastOurPort = ourPort;
changeIt = true;
}
}
if (changeIt) {
if (_log.shouldLog(Log.INFO))
_log.info(from + " and " + _lastFrom + " agree we have a new IP - " + Addresses.toString(ourIP, ourPort) + ". Changing address.");
changeAddress(ourIP, ourPort);
}
}
}
use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class UDPTransport method locked_needsRebuild.
private boolean locked_needsRebuild() {
// simple enough
if (_needsRebuild)
return true;
if (_context.router().isHidden())
return false;
boolean v6Only = getIPv6Config() == IPV6_ONLY;
RouterAddress addr = getCurrentAddress(v6Only);
if (!v6Only && introducersRequired()) {
UDPAddress ua = new UDPAddress(addr);
long now = _context.clock().now();
int valid = 0;
for (int i = 0; i < ua.getIntroducerCount(); i++) {
// warning: this is only valid as long as we use the ident hash as their key.
byte[] key = ua.getIntroducerKey(i);
if (key.length != Hash.HASH_LENGTH)
continue;
long exp = ua.getIntroducerExpiration(i);
if (exp > 0 && exp < now + INTRODUCER_EXPIRATION_MARGIN)
continue;
PeerState peer = getPeerState(new Hash(key));
if (peer != null)
valid++;
}
long sinceSelected = now - _introducersSelectedOn;
if (valid >= PUBLIC_RELAY_COUNT) {
// try to shift 'em around every 10 minutes or so
if (sinceSelected > 17 * 60 * 1000) {
if (_log.shouldLog(Log.WARN))
_log.warn("Our introducers are valid, but haven't changed in " + DataHelper.formatDuration(sinceSelected) + ", so lets rechoose");
return true;
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Our introducers are valid and were selected " + DataHelper.formatDuration(sinceSelected) + " ago");
return false;
}
} else if (sinceSelected > 2 * 60 * 1000) {
// Rate limit to prevent rapid churn after transition to firewalled or at startup
if (_log.shouldLog(Log.INFO))
_log.info("Need more introducers (have " + valid + " need " + PUBLIC_RELAY_COUNT + ')');
return true;
} else {
if (_log.shouldLog(Log.INFO))
_log.info("Need more introducers (have " + valid + " need " + PUBLIC_RELAY_COUNT + ')' + " but we just chose them " + DataHelper.formatDuration(sinceSelected) + " ago so wait");
// TODO also check to see if we actually have more available
return false;
}
} else {
byte[] externalListenHost = addr != null ? addr.getIP() : null;
int externalListenPort = addr != null ? addr.getPort() : -1;
boolean rv = (externalListenHost == null) || (externalListenPort <= 0);
if (!rv) {
// shortcut to determine if introducers are present
if (addr.getOption("ihost0") != null)
// status == ok and we don't actually need introducers, so rebuild
rv = true;
}
if (rv) {
if (_log.shouldLog(Log.INFO))
_log.info("Need to initialize our direct SSU info (" + Addresses.toString(externalListenHost, externalListenPort) + ')');
} else if (addr.getPort() <= 0 || addr.getHost() == null) {
if (_log.shouldLog(Log.INFO))
_log.info("Our direct SSU info is initialized, but not used in our address yet");
rv = true;
} else {
// _log.info("Our direct SSU info is initialized");
}
return rv;
}
}
use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class UDPTransport method changeAddress.
/**
* Possibly change our external address to the IP/port.
* IP/port are already validated, but not yet compared to current IP/port.
* We compare here.
*
* @param ourIP MUST have been previously validated with isValid()
* IPv4 or IPv6 OK
* @param ourPort >= 1024 or 0 for no change
*/
private boolean changeAddress(byte[] ourIP, int ourPort) {
// this defaults to true when we are firewalled and false otherwise.
boolean fixedPort = getIsPortFixed();
boolean updated = false;
boolean fireTest = false;
boolean isIPv6 = ourIP.length == 16;
RouterAddress current = getCurrentExternalAddress(isIPv6);
byte[] externalListenHost = current != null ? current.getIP() : null;
int externalListenPort = current != null ? current.getPort() : getRequestedPort(isIPv6);
if (_log.shouldLog(Log.INFO))
_log.info("Change address? status = " + _reachabilityStatus + " diff = " + (_context.clock().now() - _reachabilityStatusLastUpdated) + " old = " + Addresses.toString(externalListenHost, externalListenPort) + " new = " + Addresses.toString(ourIP, ourPort));
if ((fixedPort && externalListenPort > 0) || ourPort <= 0)
ourPort = externalListenPort;
synchronized (this) {
if (ourPort > 0 && !eq(externalListenHost, externalListenPort, ourIP, ourPort)) {
// they told us something different and our tests are either old or failing
if (_log.shouldLog(Log.WARN))
_log.warn("Trying to change our external address to " + Addresses.toString(ourIP, ourPort));
RouterAddress newAddr = rebuildExternalAddress(ourIP, ourPort, true);
updated = newAddr != null;
// } else {
// // they told us something different, but our tests are recent and positive,
// // so lets test again
// fireTest = true;
// if (_log.shouldLog(Log.WARN))
// _log.warn("Different address, but we're fine.. (" + _reachabilityStatus + ")");
// }
} else {
// matched what we expect
if (_log.shouldLog(Log.INFO))
_log.info("Same address as the current one");
}
}
if (fireTest) {
// always false, commented out above
_context.statManager().addRateData("udp.addressTestInsteadOfUpdate", 1);
_testEvent.forceRunImmediately(isIPv6);
} else if (updated) {
_context.statManager().addRateData("udp.addressUpdated", 1);
Map<String, String> changes = new HashMap<String, String>();
if (ourIP.length == 4 && !fixedPort)
changes.put(PROP_EXTERNAL_PORT, Integer.toString(ourPort));
// queue a country code lookup of the new IP
if (ourIP.length == 4)
_context.commSystem().queueLookup(ourIP);
// store these for laptop-mode (change ident on restart... or every time... when IP changes)
// IPV4 ONLY
String oldIP = _context.getProperty(PROP_IP);
String newIP = Addresses.toString(ourIP);
if (ourIP.length == 4 && !newIP.equals(oldIP)) {
long lastChanged = 0;
long now = _context.clock().now();
String lcs = _context.getProperty(PROP_IP_CHANGE);
if (lcs != null) {
try {
lastChanged = Long.parseLong(lcs);
} catch (NumberFormatException nfe) {
}
}
changes.put(PROP_IP, newIP);
changes.put(PROP_IP_CHANGE, Long.toString(now));
_context.router().saveConfig(changes, null);
if (oldIP != null) {
_context.router().eventLog().addEvent(EventLog.CHANGE_IP, newIP);
}
// For now, only do this at startup
if (oldIP != null && System.getProperty("wrapper.version") != null && _context.getBooleanProperty(PROP_LAPTOP_MODE) && now - lastChanged > 10 * 60 * 1000 && _context.router().getUptime() < 10 * 60 * 1000) {
System.out.println("WARN: IP changed, restarting with a new identity and port");
_log.logAlways(Log.WARN, "IP changed, restarting with a new identity and port");
// this removes the UDP port config
_context.router().killKeys();
// do we need WrapperManager.signalStopped() like in ConfigServiceHandler ???
// without it, the wrapper complains "shutdown unexpectedly"
// but we can't have that dependency in the router
_context.router().shutdown(Router.EXIT_HARD_RESTART);
// doesn't return
}
} else if (ourIP.length == 4 && !fixedPort) {
// save PROP_EXTERNAL_PORT
_context.router().saveConfig(changes, null);
}
// deadlock thru here ticket #1699
_context.router().rebuildRouterInfo();
_testEvent.forceRunImmediately(isIPv6);
}
return updated;
}
use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class UDPTransport method rebuildExternalAddress.
/**
* Update our IPv4 address and optionally tell the router to rebuild and republish the router info.
*
* If PROP_EXTERNAL_HOST is set, use those addresses (comma/space separated).
* If a hostname is configured in that property, use it.
* As of 0.9.32, a hostname is resolved here into one or more addresses
* and the IPs are published, to implement proposal 141.
*
* A max of one v4 and one v6 address will be set. Significant changes both
* here and in NTCP would be required to publish multiple v4 or v6 addresses.
*
* @param allowRebuildRouterInfo whether to tell the router
* @return the new address if changed, else null
*/
private RouterAddress rebuildExternalAddress(boolean allowRebuildRouterInfo) {
if (_log.shouldDebug())
_log.debug("REA2 " + allowRebuildRouterInfo);
// if the external port is specified, we want to use that to bind to even
// if we don't know the external host.
int port = _context.getProperty(PROP_EXTERNAL_PORT, -1);
String host = null;
if (explicitAddressSpecified()) {
host = _context.getProperty(PROP_EXTERNAL_HOST);
if (host != null) {
String[] hosts = DataHelper.split(host, "[,; \r\n\t]");
RouterAddress rv = null;
// we only take one each of v4 and v6
boolean v4 = false;
boolean v6 = false;
// prevent adding a type if disabled
TransportUtil.IPv6Config cfg = getIPv6Config();
if (cfg == IPV6_DISABLED)
v6 = true;
else if (cfg == IPV6_ONLY)
v4 = true;
for (int i = 0; i < hosts.length; i++) {
String h = hosts[i];
if (h.length() <= 0)
continue;
if (Addresses.isIPv4Address(h)) {
if (v4)
continue;
v4 = true;
} else if (Addresses.isIPv6Address(h)) {
if (v6)
continue;
v6 = true;
} else {
int valid = 0;
List<byte[]> ips = Addresses.getIPs(h);
if (ips != null) {
for (byte[] ip : ips) {
if (!isValid(ip)) {
if (_log.shouldWarn())
_log.warn("REA2: skipping invalid " + Addresses.toString(ip) + " for " + h);
continue;
}
if ((v4 && ip.length == 4) || (v6 && ip.length == 16)) {
if (_log.shouldWarn())
_log.warn("REA2: skipping additional " + Addresses.toString(ip) + " for " + h);
continue;
}
if (ip.length == 4)
v4 = true;
else if (ip.length == 16)
v6 = true;
valid++;
if (_log.shouldDebug())
_log.debug("REA2: adding " + Addresses.toString(ip) + " for " + h);
RouterAddress trv = rebuildExternalAddress(ip, port, allowRebuildRouterInfo);
if (trv != null)
rv = trv;
}
}
if (valid == 0)
_log.error("No valid IPs for configured hostname " + h);
continue;
}
RouterAddress trv = rebuildExternalAddress(h, port, allowRebuildRouterInfo);
if (trv != null)
rv = trv;
}
return rv;
}
} else {
if (!introducersRequired()) {
boolean v6Only = getIPv6Config() == IPV6_ONLY;
RouterAddress cur = getCurrentExternalAddress(v6Only);
if (cur != null)
host = cur.getHost();
}
}
return rebuildExternalAddress(host, port, allowRebuildRouterInfo);
}
use of net.i2p.data.router.RouterAddress in project i2p.i2p by i2p.
the class ConnectChecker method getConnectMask.
/**
* @param addrs non-empty, set your own default if empty
* @return bitmask of v4/v6 NTCP/SSU
* @since 0.9.34
*/
private static int getConnectMask(Collection<RouterAddress> addrs) {
int rv = 0;
for (RouterAddress ra : addrs) {
String style = ra.getTransportStyle();
String host = ra.getHost();
if ("NTCP".equals(style)) {
if (host != null) {
if (host.contains(":"))
rv |= NTCP_V6;
else
rv |= NTCP_V4;
}
} else if ("SSU".equals(style)) {
if (host == null) {
for (int i = 0; i < 2; i++) {
String ihost = ra.getOption(IHOST[i]);
if (ihost == null)
break;
if (ihost.contains(":"))
rv |= SSU_V6;
else
rv |= SSU_V4;
}
} else if (host.contains(":")) {
rv |= SSU_V6;
} else {
rv |= SSU_V4;
}
}
}
return rv;
}
Aggregations