diff --git a/initialNews.xml b/initialNews.xml
index 40892c28d..35800d7d5 100644
--- a/initialNews.xml
+++ b/initialNews.xml
@@ -11,10 +11,6 @@ While you are waiting, please adjust your bandwidth settings on the
configuration page.
-If you can, open up port 8887 on your firewall, then enable inbound TCP on the
-configuration page.
-
-
Once you have a "shared clients" destination listed on the left,
please check out our
FAQ.
@@ -35,9 +31,6 @@ Passe bitte In der Wartezeit deine Einstellungen zur Bandbreite auf der
Einstellungsseite an.
-Bitte öffne sobald möglich den Port 8887 in deiner Firewall, aktiviere danach den eingehenden TCP Verkehr auf der Einstellungsseite.
-
-
Sobald auf der linken Seite eine "shared clients" Verbindung aufgelistet ist besuche bitte unsere FAQ.
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 f828361e2..cffb8fd8b 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 2bdf8a466..2556bdb7b 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();
-
- int 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();
- } else {
- port = _externalListenPort;
- if (_log.shouldLog(Log.INFO))
- _log.info("Binding to the explicitly specified external 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;
- }
- }
+
+ // bind host
+ String bindTo = _context.getProperty(PROP_BIND_INTERFACE);
+ InetAddress bindToAddr = null;
+ if (bindTo != 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);
+ bindToAddr = InetAddress.getByName(bindTo);
+ } catch (UnknownHostException uhe) {
+ _log.log(Log.CRIT, "Invalid SSU bind interface specified [" + bindTo + "]", uhe);
+ setReachabilityStatus(CommSystemFacade.STATUS_HOSED);
return;
}
+ }
+
+ // 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
+ if (oldIPort > 0)
+ port = oldIPort;
+ else
+ port = oldEPort;
} else {
+ port = _externalListenPort;
+ }
+ if (_log.shouldLog(Log.INFO))
+ _log.info("Binding to the port: " + port);
+ if (_endpoint == null) {
+ _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(" ").append(resentTotal);
buf.append(" | ").append(dupRecvTotal).append(" | \n");
buf.append(" \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("Percentage of bytes retransmitted (lifetime): ").append(formatPct(bwResent));
buf.append("
(Includes retransmission required by packet loss)\n");
+ *****/
+
out.write(buf.toString());
buf.setLength(0);
out.write(KEY);