diff --git a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java index c8cc31577..5ef9532cf 100644 --- a/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java +++ b/router/java/src/net/i2p/router/transport/CommSystemFacadeImpl.java @@ -26,9 +26,6 @@ import net.i2p.data.RouterInfo; import net.i2p.router.CommSystemFacade; import net.i2p.router.OutNetMessage; import net.i2p.router.RouterContext; -import net.i2p.router.transport.ntcp.NTCPAddress; -import net.i2p.router.transport.ntcp.NTCPTransport; -import net.i2p.router.transport.udp.UDPAddress; import net.i2p.router.transport.udp.UDPTransport; import net.i2p.util.Addresses; import net.i2p.util.Log; @@ -203,6 +200,8 @@ public class CommSystemFacadeImpl extends CommSystemFacade { * UDP changed addresses, tell NTCP and restart * * All the work moved to NTCPTransport.externalAddressReceived() + * + * @param udpAddr may be null; or udpAddr's host/IP may be null */ @Override public void notifyReplaceAddress(RouterAddress udpAddr) { diff --git a/router/java/src/net/i2p/router/transport/Transport.java b/router/java/src/net/i2p/router/transport/Transport.java index ffcf0b4eb..0c576b193 100644 --- a/router/java/src/net/i2p/router/transport/Transport.java +++ b/router/java/src/net/i2p/router/transport/Transport.java @@ -76,10 +76,45 @@ public interface Transport { } } + /** + * Notify a transport of an external address change. + * This may be from a local interface, UPnP, a config change, etc. + * This should not be called if the ip didn't change + * (from that source's point of view), or is a local address. + * May be called multiple times for IPv4 or IPv6. + * The transport should also do its own checking on whether to accept + * notifications from this source. + * + * This can be called before startListening() to set an initial address, + * or after the transport is running. + * + * @param source defined in Transport.java + * @param ip typ. IPv4 or IPv6 non-local; may be null to indicate IPv4 failure or port info only + * @param port 0 for unknown or unchanged + */ public void externalAddressReceived(AddressSource source, byte[] ip, int port); + + /** + * Notify a transport of the results of trying to forward a port. + * + * @param port the internal port + * @param externalPort the external port, which for now should always be the same as + * the internal port if the forwarding was successful. + */ public void forwardPortStatus(int port, int externalPort, boolean success, String reason); + + /** + * What INTERNAL port would the transport like to have forwarded by UPnP. + * This can't be passed via getCurrentAddress(), as we have to open the port + * before we can publish the address, and that's the external port anyway. + * + * @return port or -1 for none or 0 for any + */ public int getRequestedPort(); + + /** Who to notify on message availability */ public void setListener(TransportEventListener listener); + public String getStyle(); public int countPeers(); @@ -94,6 +129,11 @@ public interface Transport { public short getReachabilityStatus(); public void recheckReachability(); public boolean isBacklogged(Hash dest); + + /** + * Was the peer UNreachable (outbound only) the last time we tried it? + * This is NOT reset if the peer contacts us and it is never expired. + */ public boolean wasUnreachable(Hash dest); public boolean isUnreachable(Hash peer); diff --git a/router/java/src/net/i2p/router/transport/TransportImpl.java b/router/java/src/net/i2p/router/transport/TransportImpl.java index 153e4b358..b3d2c224c 100644 --- a/router/java/src/net/i2p/router/transport/TransportImpl.java +++ b/router/java/src/net/i2p/router/transport/TransportImpl.java @@ -10,6 +10,9 @@ package net.i2p.router.transport; import java.io.IOException; import java.io.Writer; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; @@ -57,6 +60,7 @@ public abstract class TransportImpl implements Transport { /** map from routerIdentHash to timestamp (Long) that the peer was last unreachable */ private final Map _unreachableEntries; private final Set _wasUnreachableEntries; + private final Set _localAddresses; /** global router ident -> IP */ private static final Map _IPMap; @@ -95,6 +99,7 @@ public abstract class TransportImpl implements Transport { _sendPool = null; _unreachableEntries = new HashMap(16); _wasUnreachableEntries = new ConcurrentHashSet(16); + _localAddresses = new ConcurrentHashSet(4); _context.simpleScheduler().addPeriodicEvent(new CleanupUnreachable(), 2 * UNREACHABLE_PERIOD, UNREACHABLE_PERIOD / 2); } @@ -537,6 +542,26 @@ public abstract class TransportImpl implements Transport { _listener.transportAddressChanged(); } + /** + * Save a local address we were notified about before we started. + * + * @since IPv6 + */ + protected void saveLocalAddress(InetAddress address) { + _localAddresses.add(address); + } + + /** + * Return and then clear all saved local addresses. + * + * @since IPv6 + */ + protected Collection getSavedLocalAddresses() { + List rv = new ArrayList(_localAddresses); + _localAddresses.clear(); + return rv; + } + /** * Notify a transport of an external address change. * This may be from a local interface, UPnP, a config change, etc. @@ -569,9 +594,9 @@ public abstract class TransportImpl implements Transport { public void forwardPortStatus(int port, int externalPort, boolean success, String reason) {} /** - * What port would the transport like to have forwarded by UPnP. + * What INTERNAL port would the transport like to have forwarded by UPnP. * This can't be passed via getCurrentAddress(), as we have to open the port - * before we can publish the address. + * before we can publish the address, and that's the external port anyway. * * @return port or -1 for none or 0 for any */ @@ -590,6 +615,7 @@ public abstract class TransportImpl implements Transport { public boolean isEstablished(Hash dest) { return false; } private static final long UNREACHABLE_PERIOD = 5*60*1000; + public boolean isUnreachable(Hash peer) { long now = _context.clock().now(); synchronized (_unreachableEntries) { @@ -603,6 +629,7 @@ public abstract class TransportImpl implements Transport { } } } + /** called when we can't reach a peer */ /** This isn't very useful since it is cleared when they contact us */ public void markUnreachable(Hash peer) { @@ -612,6 +639,7 @@ public abstract class TransportImpl implements Transport { } markWasUnreachable(peer, true); } + /** called when we establish a peer connection (outbound or inbound) */ public void markReachable(Hash peer, boolean isInbound) { // if *some* transport can reach them, then we shouldn't banlist 'em diff --git a/router/java/src/net/i2p/router/transport/TransportManager.java b/router/java/src/net/i2p/router/transport/TransportManager.java index 4a1f10ada..f60ab8d7c 100644 --- a/router/java/src/net/i2p/router/transport/TransportManager.java +++ b/router/java/src/net/i2p/router/transport/TransportManager.java @@ -87,13 +87,24 @@ public class TransportManager implements TransportEventListener { private void configTransports() { boolean enableUDP = _context.getBooleanPropertyDefaultTrue(PROP_ENABLE_UDP); + Transport udp = null; if (enableUDP) { - UDPTransport udp = new UDPTransport(_context, _dhThread); + udp = new UDPTransport(_context, _dhThread); addTransport(udp); initializeAddress(udp); } - if (isNTCPEnabled(_context)) - addTransport(new NTCPTransport(_context, _dhThread)); + if (isNTCPEnabled(_context)) { + Transport ntcp = new NTCPTransport(_context, _dhThread); + addTransport(ntcp); + initializeAddress(ntcp); + if (udp != null) { + // pass along the port SSU is probably going to use + // so that NTCP may bind early + int port = udp.getRequestedPort(); + if (port > 0) + ntcp.externalAddressReceived(SOURCE_CONFIG, null, port); + } + } if (_transports.isEmpty()) _log.log(Log.CRIT, "No transports are enabled"); } @@ -120,8 +131,8 @@ public class TransportManager implements TransportEventListener { } /** - * callback from UPnP or SSU - * Only tell SSU, it will tell NTCP + * Initialize from interfaces, and callback from UPnP or SSU. + * Tell all transports... but don't loop * */ public void externalAddressReceived(Transport.AddressSource source, byte[] ip, int port) { @@ -154,7 +165,17 @@ public class TransportManager implements TransportEventListener { _upnpManager.start(); configTransports(); _log.debug("Starting up the transport manager"); - for (Transport t : _transports.values()) { + // Let's do this in a predictable order to make testing easier + // Start NTCP first so it can get notified from SSU + List tps = new ArrayList(); + Transport tp = getTransport(NTCPTransport.STYLE); + if (tp != null) + tps.add(tp); + tp = getTransport(UDPTransport.STYLE); + if (tp != null) + tps.add(tp); + //for (Transport t : _transports.values()) { + for (Transport t : tps) { t.startListening(); if (_log.shouldLog(Log.DEBUG)) _log.debug("Transport " + t.getStyle() + " started"); diff --git a/router/java/src/net/i2p/router/transport/TransportUtil.java b/router/java/src/net/i2p/router/transport/TransportUtil.java index 64f284041..ab15c1f2a 100644 --- a/router/java/src/net/i2p/router/transport/TransportUtil.java +++ b/router/java/src/net/i2p/router/transport/TransportUtil.java @@ -57,6 +57,8 @@ public abstract class TransportUtil { for (IPv6Config cfg : IPv6Config.values()) { BY_NAME.put(cfg.toConfigString(), cfg); } + // alias + BY_NAME.put("true", IPv6Config.IPV6_ENABLED); } public static IPv6Config getIPv6Config(RouterContext ctx, String transportStyle) { 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 ba3cbc58b..258953a57 100644 --- a/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java +++ b/router/java/src/net/i2p/router/transport/ntcp/NTCPTransport.java @@ -8,9 +8,11 @@ import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.text.DecimalFormat; import java.text.NumberFormat; +import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -58,6 +60,10 @@ public class NTCPTransport extends TransportImpl { private final EventPumper _pumper; private final Reader _reader; private net.i2p.router.transport.ntcp.Writer _writer; + private int _ssuPort; + /** synch on this */ + private final Set _endpoints; + /** * list of NTCPConnection of connections not yet established that we * want to remove on establishment or close on timeout @@ -155,6 +161,7 @@ public class NTCPTransport extends TransportImpl { _context.statManager().createRateStat("ntcp.wantsQueuedWrite", "", "ntcp", RATES); //_context.statManager().createRateStat("ntcp.write", "", "ntcp", RATES); _context.statManager().createRateStat("ntcp.writeError", "", "ntcp", RATES); + _endpoints = new HashSet(4); _establishing = new ConcurrentHashSet(16); _conLock = new Object(); _conByIdent = new ConcurrentHashMap(64); @@ -474,27 +481,47 @@ public class NTCPTransport extends TransportImpl { startIt(); RouterAddress addr = configureLocalAddress(); + int port; if (addr != null) - bindAddress(addr); + // probably not set + port = addr.getPort(); + else + // received by externalAddressReceived() from TransportManager + port = _ssuPort; + RouterAddress myAddress = bindAddress(port); + if (myAddress != null) { + replaceAddress(myAddress); + } else if (port > 0) { + for (InetAddress ia : getSavedLocalAddresses()) { + OrderedProperties props = new OrderedProperties(); + props.setProperty(RouterAddress.PROP_HOST, ia.getHostAddress()); + props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port)); + myAddress = new RouterAddress(STYLE, props, DEFAULT_COST); + replaceAddress(myAddress); + } + } + // TransportManager.startListening() calls router.rebuildRouterInfo() } /** * Only called by externalAddressReceived(). - * Caller should stop the transport first, then - * verify stopped with isAlive() + * + * Doesn't actually restart unless addr is non-null and + * the port is different from the current listen port. + * + * If we had interface addresses before, we lost them. * * @param addr may be null */ private synchronized void restartListening(RouterAddress addr) { - // try once again to prevent two pumpers which is fatal - // we could just return null since the return value is ignored - if (_pumper.isAlive()) - return; - if (_log.shouldLog(Log.WARN)) _log.warn("Restarting ntcp transport listening"); - - startIt(); - if (addr != null) - bindAddress(addr); + if (addr != null) { + RouterAddress myAddress = bindAddress(addr.getPort()); + if (myAddress != null) + replaceAddress(myAddress); + else + replaceAddress(addr); + // UDPTransport.rebuildExternalAddress() calls router.rebuildRouterInfo() + } } /** @@ -526,12 +553,18 @@ public class NTCPTransport extends TransportImpl { } /** + * Only does something if myPort > 0 and myPort != current bound port + * (or there's no current port, or the configured interface or hostname changed). + * If we are changing the bound port, this restarts everything, which takes a long time. + * * call from synchronized method - * @param myAddress new address, may be null - * @return new address or null + * + * @param myPort does nothing if <= 0 + * @return new address ONLY if bound to specific address, otherwise null */ - private RouterAddress bindAddress(RouterAddress myAddress) { - if (myAddress != null) { + private RouterAddress bindAddress(int port) { + RouterAddress myAddress = null; + if (port > 0) { InetAddress bindToAddr = null; String bindTo = _context.getProperty(PROP_BIND_INTERFACE); @@ -539,16 +572,7 @@ public class NTCPTransport extends TransportImpl { // If we are configured with a fixed IP address, // AND it's one of our local interfaces, // bind only to that. - boolean isFixed = _context.getProperty(PROP_I2NP_NTCP_AUTO_IP, "true") - .toLowerCase(Locale.US).equals("false"); - String fixedHost = _context.getProperty(PROP_I2NP_NTCP_HOSTNAME); - if (isFixed && fixedHost != null) { - try { - String testAddr = InetAddress.getByName(fixedHost).getHostAddress(); - if (Addresses.getAddresses().contains(testAddr)) - bindTo = testAddr; - } catch (UnknownHostException uhe) {} - } + bindTo = getFixedHost(); } if (bindTo != null) { @@ -564,43 +588,104 @@ public class NTCPTransport extends TransportImpl { } try { - ServerSocketChannel chan = ServerSocketChannel.open(); - chan.configureBlocking(false); - - int port = myAddress.getPort(); - if (port > 0 && port < 1024) - _log.logAlways(Log.WARN, "Specified NTCP port is " + port + ", ports lower than 1024 not recommended"); - InetSocketAddress addr = null; + InetSocketAddress addr; if(bindToAddr==null) { addr = new InetSocketAddress(port); } else { addr = new InetSocketAddress(bindToAddr, port); if (_log.shouldLog(Log.WARN)) _log.warn("Binding only to " + bindToAddr); + OrderedProperties props = new OrderedProperties(); + props.setProperty(RouterAddress.PROP_HOST, bindTo); + props.setProperty(RouterAddress.PROP_PORT, Integer.toString(port)); + myAddress = new RouterAddress(STYLE, props, DEFAULT_COST); } + if (!_endpoints.isEmpty()) { + // If we are already bound to the new address, OR + // if the host is specified and we are bound to the wildcard on the same port, + // do nothing. Changing config from wildcard to a specified host will + // require a restart. + if (_endpoints.contains(addr) || + (bindToAddr != null && _endpoints.contains(new InetSocketAddress(port)))) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Already listening on " + addr); + return null; + } + // FIXME support multiple binds + // FIXME just close and unregister + stopWaitAndRestart(); + } + if (port < 1024) + _log.logAlways(Log.WARN, "Specified NTCP port is " + port + ", ports lower than 1024 not recommended"); + ServerSocketChannel chan = ServerSocketChannel.open(); + chan.configureBlocking(false); chan.socket().bind(addr); + _endpoints.add(addr); if (_log.shouldLog(Log.INFO)) _log.info("Listening on " + addr); _pumper.register(chan); } catch (IOException ioe) { _log.error("Error listening", ioe); + myAddress = null; } } else { if (_log.shouldLog(Log.INFO)) _log.info("Outbound NTCP connections only - no listener configured"); } - - if (myAddress != null) { - replaceAddress(myAddress); - return myAddress; - } else { - return null; - } + return myAddress; } + /** + * @return configured host or null. Must be one of our local interfaces. + * @since IPv6 moved from bindAddress() + */ + private String getFixedHost() { + boolean isFixed = _context.getProperty(PROP_I2NP_NTCP_AUTO_IP, "true") + .toLowerCase(Locale.US).equals("false"); + String fixedHost = _context.getProperty(PROP_I2NP_NTCP_HOSTNAME); + if (isFixed && fixedHost != null) { + try { + String testAddr = InetAddress.getByName(fixedHost).getHostAddress(); + // FIXME range of IPv6 addresses + if (Addresses.getAddresses().contains(testAddr)) + return testAddr; + } catch (UnknownHostException uhe) {} + } + return null; + } + + /** + * Caller must sync + * @since IPv6 moved from externalAddressReceived() + */ + private void stopWaitAndRestart() { + if (_log.shouldLog(Log.WARN)) + _log.warn("Halting NTCP to change address"); + stopListening(); + // Wait for NTCP Pumper to stop so we don't end up with two... + while (isAlive()) { + try { Thread.sleep(5*1000); } catch (InterruptedException ie) {} + } + if (_log.shouldLog(Log.WARN)) + _log.warn("Restarting NTCP transport listening"); + startIt(); + } + + /** + * Hook for NTCPConnection + */ Reader getReader() { return _reader; } + + /** + * Hook for NTCPConnection + */ net.i2p.router.transport.ntcp.Writer getWriter() { return _writer; } + public String getStyle() { return STYLE; } + + /** + * Hook for NTCPConnection + */ EventPumper getPumper() { return _pumper; } /** @@ -677,6 +762,7 @@ public class NTCPTransport extends TransportImpl { * @since IPv6 moved from CSFI */ private RouterAddress createNTCPAddress() { + // Fixme doesn't check PROP_BIND_INTERFACE String name = _context.getProperty(PROP_I2NP_NTCP_HOSTNAME); if ( (name == null) || (name.trim().length() <= 0) || ("null".equals(name)) ) return null; @@ -691,7 +777,7 @@ public class NTCPTransport extends TransportImpl { } /** - * UDP changed addresses, tell NTCP and restart + * UDP changed addresses, tell NTCP and (possibly) restart * * @since IPv6 moved from CSFI.notifyReplaceAddress() */ @@ -699,6 +785,23 @@ public class NTCPTransport extends TransportImpl { public void externalAddressReceived(AddressSource source, byte[] ip, int port) { if (_log.shouldLog(Log.WARN)) _log.warn("Received address: " + Addresses.toString(ip, port) + " from: " + source); + if (ip != null && !isPubliclyRoutable(ip)) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Invalid address: " + Addresses.toString(ip, port) + " from: " + source); + return; + } + if (!isAlive()) { + if (source == SOURCE_INTERFACE) { + try { + InetAddress ia = InetAddress.getByAddress(ip); + saveLocalAddress(ia); + } catch (UnknownHostException uhe) {} + } else if (source == SOURCE_CONFIG) { + // save for startListening() + _ssuPort = port; + } + return; + } // ignore UPnP for now, get everything from SSU if (source != SOURCE_SSU) return; @@ -706,10 +809,10 @@ public class NTCPTransport extends TransportImpl { } /** - * UDP changed addresses, tell NTCP and restart - * Port may be set to indicate requested port even if ip is null; - * see CSFI.notifyReplaceAddress() + * UDP changed addresses, tell NTCP and restart. + * Port may be set to indicate requested port even if ip is null. * + * @param ip previously validated * @since IPv6 moved from CSFI.notifyReplaceAddress() */ private synchronized void externalAddressReceived(byte[] ip, int port) { @@ -845,16 +948,16 @@ public class NTCPTransport extends TransportImpl { // without tearing down everything // Especially on disabling the address, we shouldn't tear everything down. // - if (_log.shouldLog(Log.WARN)) - _log.warn("Halting NTCP to change address"); - stopListening(); + //if (_log.shouldLog(Log.WARN)) + // _log.warn("Halting NTCP to change address"); + //stopListening(); // Wait for NTCP Pumper to stop so we don't end up with two... - while (isAlive()) { - try { Thread.sleep(5*1000); } catch (InterruptedException ie) {} - } + //while (isAlive()) { + // try { Thread.sleep(5*1000); } catch (InterruptedException ie) {} + //} restartListening(newAddr); if (_log.shouldLog(Log.WARN)) - _log.warn("Changed NTCP Address and started up, address is now " + newAddr); + _log.warn("Updating NTCP Address with " + newAddr); return; } @@ -929,17 +1032,17 @@ public class NTCPTransport extends TransportImpl { _writer.stopWriting(); _reader.stopReading(); _finisher.stop(); - Map cons = null; + List cons; synchronized (_conLock) { - cons = new HashMap(_conByIdent); + cons = new ArrayList(_conByIdent.values()); _conByIdent.clear(); } - for (Iterator iter = cons.values().iterator(); iter.hasNext(); ) { - NTCPConnection con = (NTCPConnection)iter.next(); + for (NTCPConnection con : cons) { con.close(); } NTCPConnection.releaseResources(); replaceAddress(null); + _endpoints.clear(); } public static final String STYLE = "NTCP"; diff --git a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java index 2d551fda9..33b766d6c 100644 --- a/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java +++ b/router/java/src/net/i2p/router/transport/udp/InboundMessageFragments.java @@ -66,6 +66,7 @@ class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{ _ackSender.shutdown(); _messageReceiver.shutdown(); } + public boolean isAlive() { return _alive; } /** diff --git a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java index 2822fe8fb..7170a7fcb 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java @@ -108,9 +108,7 @@ class UDPEndpoint { if (port <= 0) { // try random ports rather than just do new DatagramSocket() // so we stay out of the way of other I2P stuff - int minPort = _context.getProperty(PROP_MIN_PORT, MIN_RANDOM_PORT); - int maxPort = _context.getProperty(PROP_MAX_PORT, MAX_RANDOM_PORT); - port = minPort + _context.random().nextInt(maxPort - minPort); + port = selectRandomPort(_context); } try { if (_bindAddress == null) @@ -136,6 +134,17 @@ class UDPEndpoint { return socket; } + /** + * Pick a random port between the configured boundaries + * @since IPv6 + */ + public static int selectRandomPort(RouterContext ctx) { + int minPort = Math.min(65535, Math.max(1, ctx.getProperty(PROP_MIN_PORT, MIN_RANDOM_PORT))); + int maxPort = Math.min(65535, Math.max(minPort, ctx.getProperty(PROP_MAX_PORT, MAX_RANDOM_PORT))); + return minPort + ctx.random().nextInt(1 + maxPort - minPort); + } + + /** call after startup() to get actual port or -1 on startup failure */ public int getListenPort() { return _listenPort; } public UDPSender getSender() { return _sender; } 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 be0fcce9b..339ea5cdf 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -243,6 +243,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _introducersSelectedOn = -1; _lastInboundReceivedOn = -1; _mtu = PeerState.LARGE_MTU; + setupPort(); _needsRebuild = true; _context.statManager().createRateStat("udp.alreadyConnected", "What is the lifetime of a reestablished session", "udp", RATES); @@ -264,7 +265,26 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _context.simpleScheduler().addPeriodicEvent(new PingIntroducers(), MIN_EXPIRE_TIMEOUT * 3 / 4); } - public synchronized void startup() { + /** + * Pick a port if not previously configured, so that TransportManager may + * call getRequestedPort() before we've started to get a best-guess of what our + * port is going to be, and pass that to NTCP + * + * @since IPv6 + */ + private void setupPort() { + int port = getRequestedPort(); + if (port < 0) { + port = UDPEndpoint.selectRandomPort(_context); + Map changes = new HashMap(); + changes.put(PROP_INTERNAL_PORT, Integer.toString(port)); + changes.put(PROP_EXTERNAL_PORT, Integer.toString(port)); + _context.router().saveConfig(changes, null); + _log.logAlways(Log.INFO, "UDP selected random port " + port); + } + } + + private synchronized void startup() { _fragments.shutdown(); if (_pusher != null) _pusher.shutdown(); @@ -285,11 +305,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _introManager.reset(); UDPPacket.clearCache(); + if (_log.shouldLog(Log.WARN)) _log.warn("Starting SSU transport listening"); _introKey = new SessionKey(new byte[SessionKey.KEYSIZE_BYTES]); System.arraycopy(_context.routerHash().getData(), 0, _introKey.getData(), 0, SessionKey.KEYSIZE_BYTES); - rebuildExternalAddress(); - // bind host String bindTo = _context.getProperty(PROP_BIND_INTERFACE); @@ -312,9 +331,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority try { bindToAddr = InetAddress.getByName(bindTo); } catch (UnknownHostException uhe) { - _log.log(Log.CRIT, "Invalid SSU bind interface specified [" + bindTo + "]", uhe); - setReachabilityStatus(CommSystemFacade.STATUS_HOSED); - return; + _log.error("Invalid SSU bind interface specified [" + bindTo + "]", uhe); + //setReachabilityStatus(CommSystemFacade.STATUS_HOSED); + //return; + // fall thru... } } @@ -412,6 +432,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority _expireEvent.setIsAlive(true); _testEvent.setIsAlive(true); // this queues it for 3-6 minutes in the future... _testEvent.reschedule(10*1000); // lets requeue it for Real Soon + + // set up external addresses + // REA param is false; + // TransportManager.startListening() calls router.rebuildRouterInfo() + if (newPort > 0 && bindToAddr == null) { + for (InetAddress ia : getSavedLocalAddresses()) { + rebuildExternalAddress(ia.getHostAddress(), newPort, false); + } + } + rebuildExternalAddress(false); } public synchronized void shutdown() { @@ -442,6 +472,11 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority UDPPacket.clearCache(); UDPAddress.clearCache(); } + + /** @since IPv6 */ + private boolean isAlive() { + return _inboundFragments.isAlive(); + } /** * Introduction key that people should use to contact us @@ -581,8 +616,20 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority String sources = _context.getProperty(PROP_SOURCES, DEFAULT_SOURCES); if (!sources.contains(source.toConfigString())) return; - if (!isValid(ip)) + if (!isValid(ip)) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Invalid address: " + Addresses.toString(ip, port) + " from: " + source); return; + } + if (!isAlive()) { + if (source == SOURCE_INTERFACE) { + try { + InetAddress ia = InetAddress.getByAddress(ip); + saveLocalAddress(ia); + } catch (UnknownHostException uhe) {} + } + return; + } if (source == SOURCE_INTERFACE) { // temp prevent multiples if (ip.length == 4) {