forked from I2P_Developers/i2p.i2p
* CSFI compile fix
* Start transports in a standard order to make testing easier * When transports learn of interface addresses before being started, save them and use them at startup * Pick SSU random port before startListening() and have the TransportManager pass it to NTCP before starting * Only restart NTCP after changing addresses when necessary; prevent thrashing at startup (ticket #459) * Only call rebuildRouterInfo() once at startup * More checking of min/max SSU port config * Invalid SSU bind config no longer fatal * Allow "true" for ipv6 config * log tweaks * javadocs
This commit is contained in:
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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<Hash, Long> _unreachableEntries;
|
||||
private final Set<Hash> _wasUnreachableEntries;
|
||||
private final Set<InetAddress> _localAddresses;
|
||||
/** global router ident -> IP */
|
||||
private static final Map<Hash, byte[]> _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<InetAddress> getSavedLocalAddresses() {
|
||||
List<InetAddress> 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
|
||||
|
@ -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<Transport> 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");
|
||||
|
@ -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) {
|
||||
|
@ -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<InetSocketAddress> _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<NTCPConnection> 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";
|
||||
|
@ -66,6 +66,7 @@ class InboundMessageFragments /*implements UDPTransport.PartialACKSource */{
|
||||
_ackSender.shutdown();
|
||||
_messageReceiver.shutdown();
|
||||
}
|
||||
|
||||
public boolean isAlive() { return _alive; }
|
||||
|
||||
/**
|
||||
|
@ -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; }
|
||||
|
@ -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<String, String> 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) {
|
||||
|
Reference in New Issue
Block a user