diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java index b559ac3c9..021457538 100644 --- a/router/java/src/net/i2p/router/transport/TransportImpl.java +++ b/router/java/src/net/i2p/router/transport/TransportImpl.java @@ -70,6 +70,17 @@ public abstract class TransportImpl implements Transport { private static final long UNREACHABLE_PERIOD = 5*60*1000; private static final long WAS_UNREACHABLE_PERIOD = 30*60*1000; + /** @since 0.9.50 */ + protected static final String PROP_TRANSPORT_CAPS = "i2np.transportCaps"; + /** @since 0.9.50 */ + protected static final boolean ENABLE_TRANSPORT_CAPS = false; + /** @since 0.9.50 */ + public static final String CAP_IPV4 = "4"; + /** @since 0.9.50 */ + public static final String CAP_IPV6 = "6"; + /** @since 0.9.50 */ + public static final String CAP_IPV4_IPV6 = CAP_IPV4 + CAP_IPV6; + /** @since 0.9.44 */ protected static final String PROP_IPV6_FIREWALLED = "i2np.lastIPv6Firewalled"; diff --git a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java index 03f4cd6c3..ebf602dd4 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -1247,8 +1247,19 @@ public class NTCPTransport extends TransportImpl { if (!_enableNTCP2) return; // only set i if we are not firewalled - if (props.containsKey("host")) + if (props.containsKey("host")) { props.setProperty("i", _b64Ntcp2StaticIV); + } else if (_context.getProperty(PROP_TRANSPORT_CAPS, ENABLE_TRANSPORT_CAPS)) { + String caps; + TransportUtil.IPv6Config config = getIPv6Config(); + if (config == IPV6_ONLY) + caps = CAP_IPV6; + else if (config != IPV6_DISABLED && _haveIPv6Address) + caps = CAP_IPV4_IPV6; + else + caps = CAP_IPV4; + props.setProperty("caps", caps); + } props.setProperty("s", _b64Ntcp2StaticPubkey); props.setProperty("v", NTCP2_VERSION); } diff --git a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java index b8e849e63..49d00639b 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -187,6 +187,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final String CAP_TESTING = Character.toString(UDPAddress.CAPACITY_TESTING); private static final String CAP_TESTING_INTRO = CAP_TESTING + UDPAddress.CAPACITY_INTRODUCER; + private static final String CAP_TESTING_4 = CAP_TESTING + CAP_IPV4; /** how many relays offered to us will we use at a time? */ public static final int PUBLIC_RELAY_COUNT = 3; @@ -204,6 +205,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private static final int MAX_CONSECUTIVE_FAILED = 5; public static final int DEFAULT_COST = 5; + private static final int SSU_OUTBOUND_COST = 14; static final long[] RATES = { 10*60*1000 }; /** minimum active peers to maintain IP detection, etc. */ private static final int MIN_PEERS = 5; @@ -610,12 +612,21 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority // REA param is false; // TransportManager.startListening() calls router.rebuildRouterInfo() if (newPort > 0 && bindToAddrs.isEmpty()) { + // Update some config variables and event logs, + // because changeAddress() below won't do that for hidden mode + // because rebuildExternalAddress() always returns null. + boolean save = _context.router().isHidden(); + Map changes = save ? new HashMap(4) : null; boolean hasv6 = false; for (InetAddress ia : getSavedLocalAddresses()) { // Discovered or configured addresses are presumed good at the start. // when externalAddressReceived() was called with SOURCE_INTERFACE, // isAlive() was false, so setReachabilityStatus() was not called - if (ia.getAddress().length == 16) { + byte[] addr = ia.getAddress(); + String prop = addr.length == 4 ? PROP_IP : PROP_IPV6; + String oldIP = save ? _context.getProperty(prop) : null; + String newIP = Addresses.toString(addr); + if (addr.length == 16) { // only call REA for one v6 address if (hasv6) continue; @@ -625,14 +636,23 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority } else { _lastInboundIPv6 = _context.clock().now(); setReachabilityStatus(Status.IPV4_UNKNOWN_IPV6_OK, true); - rebuildExternalAddress(ia.getHostAddress(), newPort, false); + rebuildExternalAddress(newIP, newPort, false); } } else { if (!isIPv4Firewalled()) setReachabilityStatus(Status.IPV4_OK_IPV6_UNKNOWN); - rebuildExternalAddress(ia.getHostAddress(), newPort, false); + rebuildExternalAddress(newIP, newPort, false); + } + if (save && !newIP.equals(oldIP)) { + changes.put(prop, newIP); + if (addr.length == 4) + changes.put(PROP_IP_CHANGE, Long.toString(_context.clock().now())); + if (oldIP != null) + _context.router().eventLog().addEvent(EventLog.CHANGE_IP, newIP); } } + if (save && !changes.isEmpty()) + _context.router().saveConfig(changes, null); } else if (newPort > 0 && !bindToAddrs.isEmpty()) { for (InetAddress ia : bindToAddrs) { if (ia.getAddress().length == 16) { @@ -725,6 +745,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority */ SessionKey getIntroKey() { return _introKey; } + /** + * Published or requested port + */ int getExternalPort(boolean ipv6) { RouterAddress addr = getCurrentAddress(ipv6); if (addr != null) { @@ -736,7 +759,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority } /** - * IPv4 only + * Published IP, IPv4 only * @return IP or null * @since 0.9.2 */ @@ -942,6 +965,24 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority gotIPv6Addr = true; } } + if ((source == SOURCE_INTERFACE || source == SOURCE_UPNP) && + _context.router().isHidden()) { + // Update some config variables and event logs, + // because changeAddress() below won't do that for hidden mode + // because rebuildExternalAddress() always returns null. + String prop = ip.length == 4 ? PROP_IP : PROP_IPV6; + String oldIP = _context.getProperty(prop); + String newIP = Addresses.toString(ip); + if (!newIP.equals(oldIP)) { + Map changes = new HashMap(2); + changes.put(prop, newIP); + if (ip.length == 4) + changes.put(PROP_IP_CHANGE, Long.toString(_context.clock().now())); + _context.router().saveConfig(changes, null); + if (oldIP != null) + _context.router().eventLog().addEvent(EventLog.CHANGE_IP, newIP); + } + } boolean changed = changeAddress(ip, port); // Assume if we have an interface with a public IP that we aren't firewalled. // If this is wrong, the peer test will figure it out and change the status. @@ -2351,13 +2392,49 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority private RouterAddress locked_rebuildExternalAddress(String host, int port, boolean allowRebuildRouterInfo) { if (_log.shouldDebug()) _log.debug("REA4 " + host + ' ' + port, new Exception()); - if (_context.router().isHidden()) - return null; - + boolean isIPv6 = host != null && host.contains(":"); OrderedProperties options = new OrderedProperties(); + if (_context.router().isHidden()) { + // save the external address, since we didn't publish it + if (port > 0 && host != null) { + RouterAddress old = getCurrentExternalAddress(isIPv6); + if (old == null || !host.equals(old.getHost()) || port != old.getPort()) { + options.setProperty(UDPAddress.PROP_PORT, String.valueOf(port)); + options.setProperty(UDPAddress.PROP_HOST, host); + RouterAddress local = new RouterAddress(STYLE, options, SSU_OUTBOUND_COST); + replaceCurrentExternalAddress(local, isIPv6); + options = new OrderedProperties(); + } + } + if (!_context.getProperty(PROP_TRANSPORT_CAPS, ENABLE_TRANSPORT_CAPS)) + return null; + // As of 0.9.50, make an address with only 4/6 caps + String caps; + TransportUtil.IPv6Config config = getIPv6Config(); + if (config == IPV6_ONLY) + caps = CAP_IPV6; + else if (config != IPV6_DISABLED && hasIPv6Address()) + caps = CAP_IPV4_IPV6; + else + caps = CAP_IPV4; + options.setProperty(UDPAddress.PROP_CAPACITY, caps); + RouterAddress current = getCurrentAddress(false); + RouterAddress addr = new RouterAddress(STYLE, options, SSU_OUTBOUND_COST); + if (!addr.deepEquals(current)) { + if (_log.shouldInfo()) + _log.info("Address rebuilt: " + addr, new Exception()); + replaceAddress(addr); + if (allowRebuildRouterInfo) + rebuildRouterInfo(); + } else { + addr = null; + } + _needsRebuild = false; + return addr; + } + boolean directIncluded; // DNS name assumed IPv4 - boolean isIPv6 = host != null && host.contains(":"); boolean introducersRequired = (!isIPv6) && introducersRequired(); if (!introducersRequired && allowDirectUDP() && port > 0 && host != null) { options.setProperty(UDPAddress.PROP_PORT, String.valueOf(port)); @@ -2388,10 +2465,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority } // if we have explicit external addresses, they had better be reachable - if (introducersRequired) - options.setProperty(UDPAddress.PROP_CAPACITY, CAP_TESTING); - else - options.setProperty(UDPAddress.PROP_CAPACITY, CAP_TESTING_INTRO); + String caps; + if (introducersRequired) { + if (_context.getProperty(PROP_TRANSPORT_CAPS, ENABLE_TRANSPORT_CAPS)) + caps = CAP_TESTING_4; + else + caps = CAP_TESTING; + } else { + caps = CAP_TESTING_INTRO; + } + options.setProperty(UDPAddress.PROP_CAPACITY, caps); // MTU since 0.9.2 int mtu;