* SSU: Allow port change if firewalled

* UPnP:
   - Prep for UPnP returning different external port
   - Better logging of errors
This commit is contained in:
zzz
2012-08-20 12:22:00 +00:00
parent 34c09583b4
commit 9452547204
9 changed files with 126 additions and 45 deletions

View File

@ -1,3 +1,13 @@
2012-08-20 zzz
* I2CP: MessageStatus cleanup
* i2psnark: Add minimum tracker and DHT announce intervals
* I2PTunnelRunner: Remove unnecessary lock (ticket #690)
* SSU: Allow port change if firewalled
* Streaming: Increase max connection timeout
* UPnP:
- Prep for UPnP returning different external port
- Better logging of errors
2012-08-18 kytv
* Fix hang during uninstallation experienced by some users in Windows
@ -8,7 +18,7 @@
- Remove duplicate clean task for nodes
- Fix another DHT warning message
* SSU:
- Use remote MTU when published (ticket #687)
- Use remote MTU when published (ticket #682)
- Queue outbound msgs during inbound establish
- IntroManager cleanups
- More synchronization

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 12;
public final static long BUILD = 13;
/** for example "-test" */
public final static String EXTRA = "";

View File

@ -40,7 +40,7 @@ public interface Transport {
public static final String SOURCE_INTERFACE = "local";
public static final String SOURCE_CONFIG = "config"; // unused
public void externalAddressReceived(String source, byte[] ip, int port);
public void forwardPortStatus(int port, boolean success, String reason);
public void forwardPortStatus(int port, int externalPort, boolean success, String reason);
public int getRequestedPort();
public void setListener(TransportEventListener listener);
public String getStyle();

View File

@ -477,9 +477,12 @@ public abstract class TransportImpl implements Transport {
public void externalAddressReceived(String source, byte[] ip, int port) {}
/**
* Notify a transport of the results of trying to forward a 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, boolean success, String reason) {}
public void forwardPortStatus(int port, int externalPort, boolean success, String reason) {}
/**
* What port would the transport like to have forwarded by UPnP.
@ -583,15 +586,18 @@ public abstract class TransportImpl implements Transport {
return _IPMap.get(peer);
}
/** @param addr non-null */
public static boolean isPubliclyRoutable(byte addr[]) {
if (addr.length == 4) {
if ((addr[0]&0xFF) == 127) return false;
if ((addr[0]&0xFF) == 10) return false;
if ( ((addr[0]&0xFF) == 172) && ((addr[1]&0xFF) >= 16) && ((addr[1]&0xFF) <= 31) ) return false;
if ( ((addr[0]&0xFF) == 192) && ((addr[1]&0xFF) == 168) ) return false;
if ((addr[0]&0xFF) >= 224) return false; // no multicast
if ((addr[0]&0xFF) == 0) return false;
if ( ((addr[0]&0xFF) == 169) && ((addr[1]&0xFF) == 254) ) return false;
int a0 = addr[0] & 0xFF;
if (a0 == 127) return false;
if (a0 == 10) return false;
int a1 = addr[1] & 0xFF;
if (a0 == 172 && a1 >= 16 && a1 <= 31) return false;
if (a0 == 192 && a1 == 168) return false;
if (a0 >= 224) return false; // no multicast
if (a0 == 0) return false;
if (a0 == 169 && a1 == 254) return false;
// 5/8 allocated to RIPE (30 November 2010)
//if ((addr[0]&0xFF) == 5) return false; // Hamachi
return true; // or at least possible to be true

View File

@ -130,10 +130,10 @@ public class TransportManager implements TransportEventListener {
* callback from UPnP
*
*/
public void forwardPortStatus(String style, int port, boolean success, String reason) {
public void forwardPortStatus(String style, int port, int externalPort, boolean success, String reason) {
Transport t = getTransport(style);
if (t != null)
t.forwardPortStatus(port, success, reason);
t.forwardPortStatus(port, externalPort, success, reason);
}
public void startListening() {

View File

@ -7,9 +7,10 @@ import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.UnknownHostException;
import java.net.URL;
import java.util.HashMap;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import net.i2p.I2PAppContext;
@ -25,6 +26,7 @@ import org.cybergarage.upnp.Device;
import org.cybergarage.upnp.DeviceList;
import org.cybergarage.upnp.Service;
import org.cybergarage.upnp.ServiceList;
import org.cybergarage.upnp.UPnPStatus;
import org.cybergarage.upnp.device.DeviceChangeListener;
import org.cybergarage.upnp.event.EventListener;
import org.freenetproject.DetectedIP;
@ -161,6 +163,9 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
}
}
/**
* DeviceChangeListener
*/
public void deviceAdded(Device dev) {
synchronized (lock) {
if(isDisabled) {
@ -279,6 +284,9 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
this.unregisterPorts(ports);
}
/**
* DeviceChangeListener
*/
public void deviceRemoved(Device dev ){
if (_log.shouldLog(Log.WARN))
_log.warn("UP&P device removed : " + dev.getFriendlyName() + " UDN: " + dev.getUDN());
@ -298,7 +306,10 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
}
}
/** event callback - unused for now - how many devices support events? */
/**
* EventListener callback -
* unused for now - how many devices support events?
*/
public void eventNotifyReceived(String uuid, long seq, String varName, String value) {
if (_log.shouldLog(Log.WARN))
_log.warn("Event: " + uuid + ' ' + seq + ' ' + varName + '=' + value);
@ -535,7 +546,10 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
return sb.toString();
}
/** blocking */
/**
* This always requests that the external port == the internal port, for now.
* Blocking!
*/
private boolean addMapping(String protocol, int port, String description, ForwardPort fp) {
if(isDisabled || !isNATPresent() || _router == null) {
_log.error("Can't addMapping: " + isDisabled + " " + isNATPresent() + " " + _router);
@ -568,12 +582,42 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
add.setArgumentValue("NewEnabled","1");
add.setArgumentValue("NewLeaseDuration", 0);
if(add.postControlAction()) {
boolean rv = add.postControlAction();
if(rv) {
synchronized(lock) {
portsForwarded.add(fp);
}
return true;
} else return false;
}
int level = rv ? Log.INFO : Log.WARN;
if (_log.shouldLog(level)) {
StringBuilder buf = new StringBuilder();
buf.append("AddPortMapping result for ").append(protocol).append(" port ").append(port);
// Not sure which of these has the good info
UPnPStatus status = add.getStatus();
if (status != null)
buf.append(" Status: ").append(status.getCode()).append(' ').append(status.getDescription());
status = add.getControlStatus();
if (status != null)
buf.append(" ControlStatus: ").append(status.getCode()).append(' ').append(status.getDescription());
_log.log(level, buf.toString());
}
// TODO if port is busy, retry with wildcard external port ??
// from spec:
// 402 Invalid Args See UPnP Device Architecture section on Control.
// 501 Action Failed See UPnP Device Architecture section on Control.
// 715 WildCardNotPermittedInSrcIP The source IP address cannot be wild-carded
// 716 WildCardNotPermittedInExtPort The external port cannot be wild-carded
// 718 ConflictInMappingEntry The port mapping entry specified conflicts with a mapping assigned previously to another client
// 724 SamePortValuesRequired Internal and External port values must be the same
// 725 OnlyPermanentLeasesSupported The NAT implementation only supports permanent lease times on port mappings
// 726 RemoteHostOnlySupportsWildcard RemoteHost must be a wildcard and cannot be a specific IP address or DNS name
// 727 ExternalPortOnlySupportsWildcard ExternalPort must be a wildcard and cannot be a specific port value
// TODO return error code and description for display
return rv;
}
/**
@ -667,8 +711,10 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
}
/**
* Registers a callback when the given ports change.
* non-blocking
* @param ports non-null
* @param cb in UPnPManager
*/
public void onChangePublicPorts(Set<ForwardPort> ports, ForwardPortCallback cb) {
Set<ForwardPort> portsToDumpNow = null;
@ -760,10 +806,8 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
}
public void run() {
HashMap<ForwardPort, ForwardPortStatus> map = new HashMap(1);
for(ForwardPort port : portsToForwardNow) {
String proto = protoToString(port.protocol);
map.clear();
ForwardPortStatus fps;
if (proto.length() <= 1) {
fps = new ForwardPortStatus(ForwardPortStatus.DEFINITE_FAILURE, "Protocol not supported", port.portNumber);
@ -772,7 +816,7 @@ class UPnP extends ControlPoint implements DeviceChangeListener, EventListener {
} else {
fps = new ForwardPortStatus(ForwardPortStatus.PROBABLE_FAILURE, "UPnP port forwarding apparently failed", port.portNumber);
}
map.put(port, fps);
Map map = Collections.singletonMap(port, fps);
forwardCallback.portForwardStatus(map);
}
}

View File

@ -152,7 +152,7 @@ class UPnPManager {
else
continue;
boolean success = fps.status >= ForwardPortStatus.MAYBE_SUCCESS;
_manager.forwardPortStatus(style, fp.portNumber, success, fps.reasonString);
_manager.forwardPortStatus(style, fp.portNumber, fps.externalPort, success, fps.reasonString);
}
}
}

View File

@ -664,7 +664,7 @@ public class NTCPTransport extends TransportImpl {
* NTCP address when it transitions to OK.
*/
@Override
public void forwardPortStatus(int port, boolean success, String reason) {
public void forwardPortStatus(int port, int externalPort, boolean success, String reason) {
if (_log.shouldLog(Log.WARN)) {
if (success)
_log.warn("UPnP has opened the NTCP port: " + port);

View File

@ -102,11 +102,17 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
private static final int DROPLIST_PERIOD = 10*60*1000;
public static final String STYLE = "SSU";
public static final String PROP_INTERNAL_PORT = "i2np.udp.internalPort";
/** now unused, we pick a random port */
/** now unused, we pick a random port
* @deprecated unused
*/
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. */
/** Limits on port told to us by others,
* We should have an exception if it matches the existing low port.
*/
private static final int MIN_EXTERNAL_PORT = 1024;
private static final int MAX_EXTERNAL_PORT = 65535;
/** define this to explicitly set an external IP address */
public static final String PROP_EXTERNAL_HOST = "i2np.udp.host";
@ -125,9 +131,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
public static final String PROP_PREFER_UDP = "i2np.udp.preferred";
private static final String DEFAULT_PREFER_UDP = "false";
/** if true (default), we don't change our advertised port no matter what our peers tell us */
public static final String PROP_FIXED_PORT = "i2np.udp.fixedPort";
private static final String DEFAULT_FIXED_PORT = "true";
/** Override whether we will change our advertised port no matter what our peers tell us
* See getIsPortFixed() for default behaviour.
*/
private static final String PROP_FIXED_PORT = "i2np.udp.fixedPort";
/** allowed sources of address updates */
public static final String PROP_SOURCES = "i2np.udp.addressSources";
@ -480,10 +487,10 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* Don't do anything if UPnP claims failure.
*/
@Override
public void forwardPortStatus(int port, boolean success, String reason) {
public void forwardPortStatus(int port, int externalPort, boolean success, String reason) {
if (_log.shouldLog(Log.WARN)) {
if (success)
_log.warn("UPnP has opened the SSU port: " + port);
_log.warn("UPnP has opened the SSU port: " + port + " via external port: " + externalPort);
else
_log.warn("UPnP has failed to open the SSU port: " + port + " reason: " + reason);
}
@ -509,7 +516,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
*/
void externalAddressReceived(Hash from, byte ourIP[], int ourPort) {
boolean isValid = isValid(ourIP) &&
(ourPort >= MIN_EXTERNAL_PORT || ourPort == _externalListenPort || _externalListenPort <= 0);
((ourPort >= MIN_EXTERNAL_PORT && ourPort <= MAX_EXTERNAL_PORT) ||
ourPort == _externalListenPort || _externalListenPort <= 0);
boolean explicitSpecified = explicitAddressSpecified();
boolean inboundRecent = _lastInboundReceivedOn + ALLOW_IP_CHANGE_INTERVAL > System.currentTimeMillis();
if (_log.shouldLog(Log.INFO))
@ -561,7 +569,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
* @param ourPort >= 1024 or 0 for no change
*/
private boolean changeAddress(byte ourIP[], int ourPort) {
/** this defaults to true, which means we never change our external port based on what somebody tells us */
// this defaults to true when we are firewalled and false otherwise.
boolean fixedPort = getIsPortFixed();
boolean updated = false;
boolean fireTest = false;
@ -583,7 +591,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
try {
_externalListenHost = InetAddress.getByAddress(ourIP);
// fixed port defaults to true so we never do this
if (ourPort >= MIN_EXTERNAL_PORT && !fixedPort)
if (ourPort >= MIN_EXTERNAL_PORT && ourPort <= MAX_EXTERNAL_PORT && !fixedPort)
_externalListenPort = ourPort;
if (_log.shouldLog(Log.WARN))
_log.warn("Trying to change our external address to " +
@ -614,9 +622,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
}
if (fireTest) {
_context.statManager().addRateData("udp.addressTestInsteadOfUpdate", 1, 0);
_context.statManager().addRateData("udp.addressTestInsteadOfUpdate", 1);
} else if (updated) {
_context.statManager().addRateData("udp.addressUpdated", 1, 0);
_context.statManager().addRateData("udp.addressUpdated", 1);
Map<String, String> changes = new HashMap();
if (!fixedPort)
changes.put(PROP_EXTERNAL_PORT, Integer.toString(ourPort));
@ -669,16 +677,25 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
return (rport == lport) && DataHelper.eq(laddr, raddr);
}
/** @param addr may be null */
public final boolean isValid(byte addr[]) {
if (addr == null) return false;
if (addr.length < 4) return false;
if (isPubliclyRoutable(addr))
return true;
return _context.getBooleanProperty("i2np.udp.allowLocal");
}
/**
* Was true before 0.9.2
* Now false if we need introducers (as perhaps that's why we need them,
* our firewall is changing our port), unless overridden by the property.
*/
private boolean getIsPortFixed() {
return DEFAULT_FIXED_PORT.equals(_context.getProperty(PROP_FIXED_PORT, DEFAULT_FIXED_PORT));
String prop = _context.getProperty(PROP_FIXED_PORT);
if (prop != null)
return Boolean.valueOf(prop).booleanValue();
int status = getReachabilityStatus();
return status != CommSystemFacade.STATUS_REJECT_UNSOLICITED;
}
/**
@ -862,7 +879,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
if (peer != null) {
RemoteHostId remote = peer.getRemoteHostId();
_dropList.add(remote);
_context.statManager().addRateData("udp.dropPeerDroplist", 1, 0);
_context.statManager().addRateData("udp.dropPeerDroplist", 1);
_context.simpleScheduler().addEvent(new RemoveDropList(remote), DROPLIST_PERIOD);
}
markUnreachable(peerHash);
@ -2360,17 +2377,21 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
long now = _context.clock().now();
switch (status) {
case CommSystemFacade.STATUS_OK:
_context.statManager().addRateData("udp.statusOK", 1, 0);
// TODO if OK but internal port != external port, should we have
// a different status state? ...as we don't know if the TCP
// port will be mapped the same way or not...
// Right now, we assume it is and hope for the best for TCP.
_context.statManager().addRateData("udp.statusOK", 1);
_reachabilityStatus = status;
_reachabilityStatusLastUpdated = now;
break;
case CommSystemFacade.STATUS_DIFFERENT:
_context.statManager().addRateData("udp.statusDifferent", 1, 0);
_context.statManager().addRateData("udp.statusDifferent", 1);
_reachabilityStatus = status;
_reachabilityStatusLastUpdated = now;
break;
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
_context.statManager().addRateData("udp.statusReject", 1, 0);
_context.statManager().addRateData("udp.statusReject", 1);
// if old != unsolicited && now - lastUpdated > STATUS_GRACE_PERIOD)
//
// fall through...
@ -2380,7 +2401,7 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
break;
case CommSystemFacade.STATUS_UNKNOWN:
default:
_context.statManager().addRateData("udp.statusUnknown", 1, 0);
_context.statManager().addRateData("udp.statusUnknown", 1);
//if (now - _reachabilityStatusLastUpdated < STATUS_GRACE_PERIOD) {
// _testEvent.forceRun();
// SimpleTimer.getInstance().addEvent(_testEvent, 5*1000);