From da21c0ddb7854348ed474c1102f7f264486c003f Mon Sep 17 00:00:00 2001 From: zzz <zzz@mail.i2p> Date: Mon, 2 Nov 2009 16:43:04 +0000 Subject: [PATCH] * UDP: Pick a random port on first install or bind failure - No more port 8887 to prevent easy state-level blocking --- initialNews.xml | 7 -- .../i2p/router/transport/udp/UDPEndpoint.java | 76 ++++++++++++++--- .../router/transport/udp/UDPTransport.java | 83 +++++++++++++------ 3 files changed, 119 insertions(+), 47 deletions(-) diff --git a/initialNews.xml b/initialNews.xml index 40892c28db..35800d7d53 100644 --- a/initialNews.xml +++ b/initialNews.xml @@ -11,10 +11,6 @@ While you are waiting, please <b>adjust your bandwidth settings</b> on the <a href="config.jsp">configuration page</a>. </li> <li> -If you can, open up <b>port 8887</b> on your firewall, then <b>enable inbound TCP</b> on the -<a href="config.jsp">configuration page</a>. -</li> -<li> Once you have a "shared clients" destination listed on the left, please <b>check out</b> our <a href="http://www.i2p2.i2p/faq.html">FAQ</a>. @@ -35,9 +31,6 @@ Passe bitte In der Wartezeit <b>deine Einstellungen zur Bandbreite</b> auf der <a href="config.jsp">Einstellungsseite</a> an. </li> <li> -Bitte öffne sobald möglich den <b>Port 8887</b> in deiner Firewall, aktiviere danach den <b>eingehenden TCP Verkehr</b> auf der <a href="config.jsp">Einstellungsseite</a>. -</li> -<li> Sobald auf der linken Seite eine "shared clients" Verbindung aufgelistet ist <b>besuche bitte</b> unsere <a href="http://www.i2p2.i2p/faq_de.html">FAQ</a>. </li> <li> 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 f828361e26..cffb8fd8bd 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPEndpoint.java @@ -22,7 +22,11 @@ public class UDPEndpoint { private DatagramSocket _socket; private InetAddress _bindAddress; - public UDPEndpoint(RouterContext ctx, UDPTransport transport, int listenPort, InetAddress bindAddress) throws SocketException { + /** + * @param listenPort -1 or the requested port, may not be honored + * @param bindAddress null ok + */ + public UDPEndpoint(RouterContext ctx, UDPTransport transport, int listenPort, InetAddress bindAddress) { _context = ctx; _log = ctx.logManager().getLog(UDPEndpoint.class); _transport = transport; @@ -30,23 +34,20 @@ public class UDPEndpoint { _listenPort = listenPort; } + /** caller should call getListenPort() after this to get the actual bound port and determine success */ public void startup() { if (_log.shouldLog(Log.DEBUG)) _log.debug("Starting up the UDP endpoint"); shutdown(); - try { - if (_bindAddress == null) - _socket = new DatagramSocket(_listenPort); - else - _socket = new DatagramSocket(_listenPort, _bindAddress); - _sender = new UDPSender(_context, _socket, "UDPSender"); - _receiver = new UDPReceiver(_context, _transport, _socket, "UDPReceiver"); - _sender.startup(); - _receiver.startup(); - } catch (SocketException se) { - _transport.setReachabilityStatus(CommSystemFacade.STATUS_HOSED); - _log.log(Log.CRIT, "Unable to bind on port " + _listenPort, se); + _socket = getSocket(); + if (_socket == null) { + _log.log(Log.CRIT, "UDP Unable to open a port"); + return; } + _sender = new UDPSender(_context, _socket, "UDPSender"); + _receiver = new UDPReceiver(_context, _transport, _socket, "UDPReceiver"); + _sender.startup(); + _receiver.startup(); } public void shutdown() { @@ -60,6 +61,8 @@ public class UDPEndpoint { } public void setListenPort(int newPort) { _listenPort = newPort; } + +/******* public void updateListenPort(int newPort) { if (newPort == _listenPort) return; try { @@ -76,7 +79,54 @@ public class UDPEndpoint { _log.error("Unable to bind on " + _listenPort); } } +********/ + /** 8998 is monotone, and 32000 is the wrapper, so let's stay between those */ + private static final int MIN_RANDOM_PORT = 9111; + private static final int MAX_RANDOM_PORT = 31777; + private static final int MAX_PORT_RETRIES = 20; + + /** + * Open socket using requested port in _listenPort and bind host in _bindAddress. + * If _listenPort <= 0, or requested port is busy, repeatedly try a new random port. + * @return null on failure + * Sets _listenPort to actual port or -1 on failure + */ + private DatagramSocket getSocket() { + DatagramSocket socket = null; + int port = _listenPort; + + for (int i = 0; i < MAX_PORT_RETRIES; i++) { + if (port <= 0) { + // try random ports rather than just do new DatagramSocket() + // so we stay out of the way of other I2P stuff + port = MIN_RANDOM_PORT + _context.random().nextInt(MAX_RANDOM_PORT - MIN_RANDOM_PORT); + } + try { + if (_bindAddress == null) + socket = new DatagramSocket(port); + else + socket = new DatagramSocket(port, _bindAddress); + break; + } catch (SocketException se) { + if (_log.shouldLog(Log.WARN)) + _log.warn("Binding to port " + port + " failed: " + se); + } + port = -1; + } + if (socket == null) { + _log.log(Log.CRIT, "SSU Unable to bind to a port on " + _bindAddress); + } else if (port != _listenPort) { + if (_listenPort > 0) + _log.error("SSU Unable to bind to requested port " + _listenPort + ", using random port " + port); + else + _log.error("SSU selected random port " + port); + } + _listenPort = port; + return socket; + } + + /** 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 2bdf8a466f..2556bdb7be 100644 --- a/router/java/src/net/i2p/router/transport/udp/UDPTransport.java +++ b/router/java/src/net/i2p/router/transport/udp/UDPTransport.java @@ -100,6 +100,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority public static final String STYLE = "SSU"; public static final String PROP_INTERNAL_PORT = "i2np.udp.internalPort"; + /** now unused, we pick a random port */ public static final int DEFAULT_INTERNAL_PORT = 8887; /** since fixed port defaults to true, this doesnt do anything at the moment. * We should have an exception if it matches the existing low port. */ @@ -137,6 +138,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority public static final String PROP_FORCE_INTRODUCERS = "i2np.udp.forceIntroducers"; /** do we allow direct SSU connections, sans introducers? */ public static final String PROP_ALLOW_DIRECT = "i2np.udp.allowDirect"; + /** this is rarely if ever used, default is to bind to wildcard address */ public static final String PROP_BIND_INTERFACE = "i2np.udp.bindInterface"; /** how many relays offered to us will we use at a time? */ @@ -226,40 +228,41 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority System.arraycopy(_context.routerHash().getData(), 0, _introKey.getData(), 0, SessionKey.KEYSIZE_BYTES); rebuildExternalAddress(); + + // bind host + String bindTo = _context.getProperty(PROP_BIND_INTERFACE); + InetAddress bindToAddr = null; + if (bindTo != null) { + try { + bindToAddr = InetAddress.getByName(bindTo); + } catch (UnknownHostException uhe) { + _log.log(Log.CRIT, "Invalid SSU bind interface specified [" + bindTo + "]", uhe); + setReachabilityStatus(CommSystemFacade.STATUS_HOSED); + return; + } + } - int port = -1; + // Requested bind port + // This may be -1 or may not be honored if busy, + // we will check below after starting up the endpoint. + int port; + int oldIPort = _context.getProperty(PROP_INTERNAL_PORT, -1); + int oldEPort = _context.getProperty(PROP_EXTERNAL_PORT, -1); if (_externalListenPort <= 0) { // no explicit external port, so lets try an internal one - port = _context.getProperty(PROP_INTERNAL_PORT, DEFAULT_INTERNAL_PORT); - // attempt to use it as our external port - this will be overridden by - // externalAddressReceived(...) - _context.router().setConfigSetting(PROP_EXTERNAL_PORT, port+""); - _context.router().saveConfig(); + if (oldIPort > 0) + port = oldIPort; + else + port = oldEPort; } else { port = _externalListenPort; - if (_log.shouldLog(Log.INFO)) - _log.info("Binding to the explicitly specified external port: " + port); } + if (_log.shouldLog(Log.INFO)) + _log.info("Binding to the port: " + port); if (_endpoint == null) { - String bindTo = _context.getProperty(PROP_BIND_INTERFACE); - InetAddress bindToAddr = null; - if (bindTo != null) { - try { - bindToAddr = InetAddress.getByName(bindTo); - } catch (UnknownHostException uhe) { - if (_log.shouldLog(Log.ERROR)) - _log.error("Invalid SSU bind interface specified [" + bindTo + "]", uhe); - bindToAddr = null; - } - } - try { - _endpoint = new UDPEndpoint(_context, this, port, bindToAddr); - } catch (SocketException se) { - if (_log.shouldLog(Log.CRIT)) - _log.log(Log.CRIT, "Unable to listen on the UDP port (" + port + ")", se); - return; - } + _endpoint = new UDPEndpoint(_context, this, port, bindToAddr); } else { + // todo, set bind address too _endpoint.setListenPort(port); } @@ -278,7 +281,24 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority if (_flooder == null) _flooder = new UDPFlooder(_context, this); + // Startup the endpoint with the requested port, check the actual port, and + // take action if it failed or was different than requested or it needs to be saved _endpoint.startup(); + int newPort = _endpoint.getListenPort(); + _externalListenPort = newPort; + if (newPort <= 0) { + _log.log(Log.CRIT, "Unable to open UDP port"); + setReachabilityStatus(CommSystemFacade.STATUS_HOSED); + return; + } + if (newPort != port || newPort != oldIPort || newPort != oldEPort) { + // attempt to use it as our external port - this will be overridden by + // externalAddressReceived(...) + _context.router().setConfigSetting(PROP_INTERNAL_PORT, newPort+""); + _context.router().setConfigSetting(PROP_EXTERNAL_PORT, newPort+""); + _context.router().saveConfig(); + } + _establisher.startup(); _handler.startup(); _fragments.startup(); @@ -321,11 +341,16 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority public int getLocalPort() { return _externalListenPort; } public InetAddress getLocalAddress() { return _externalListenHost; } public int getExternalPort() { return _externalListenPort; } + + /** + * _externalListenPort should always be set (by startup()) before this is called, + * so the returned value should be > 0 + */ @Override public int getRequestedPort() { if (_externalListenPort > 0) return _externalListenPort; - return _context.getProperty(PROP_INTERNAL_PORT, DEFAULT_INTERNAL_PORT); + return _context.getProperty(PROP_INTERNAL_PORT, -1); } /** @@ -2003,6 +2028,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority buf.append(" <td align=\"center\"><b>").append(resentTotal); buf.append("</b></td> <td align=\"center\"><b>").append(dupRecvTotal).append("</b></td>\n"); buf.append(" </tr></table></div>\n"); + + /***** long bytesTransmitted = _context.bandwidthLimiter().getTotalAllocatedOutboundBytes(); // NPE here early double averagePacketSize = _context.statManager().getRate("udp.sendPacketSize").getLifetimeAverageValue(); @@ -2012,6 +2039,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority double bwResent = (nondupSent <= 0 ? 0d : ((((double)resentTotal)*averagePacketSize) / nondupSent)); buf.append("<h3>Percentage of bytes retransmitted (lifetime): ").append(formatPct(bwResent)); buf.append("</h3><i>(Includes retransmission required by packet loss)</i>\n"); + *****/ + out.write(buf.toString()); buf.setLength(0); out.write(KEY); -- GitLab