forked from I2P_Developers/i2p.i2p
Router: Check for transport compatibility before direct store of RI;
send through tunnel if incompatible Fix repeated store of RI when IPv6-only Move connect checker to own class for use by netdb Log tweaks
This commit is contained in:
@ -29,6 +29,7 @@ import net.i2p.router.OutNetMessage;
|
||||
import net.i2p.router.ReplyJob;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelInfo;
|
||||
import net.i2p.router.tunnel.pool.ConnectChecker;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
@ -46,6 +47,8 @@ class StoreJob extends JobImpl {
|
||||
private final long _timeoutMs;
|
||||
private final long _expiration;
|
||||
private final PeerSelector _peerSelector;
|
||||
private final ConnectChecker _connectChecker;
|
||||
private final int _connectMask;
|
||||
|
||||
private final static int PARALLELIZATION = 4; // how many sent at a time
|
||||
private final static int REDUNDANCY = 4; // we want the data sent to 6 peers
|
||||
@ -75,6 +78,17 @@ class StoreJob extends JobImpl {
|
||||
_timeoutMs = timeoutMs;
|
||||
_expiration = context.clock().now() + timeoutMs;
|
||||
_peerSelector = facade.getPeerSelector();
|
||||
if (data.getType() == DatabaseEntry.KEY_TYPE_LEASESET) {
|
||||
_connectChecker = null;
|
||||
_connectMask = 0;
|
||||
} else {
|
||||
_connectChecker = new ConnectChecker(context);
|
||||
RouterInfo us = context.router().getRouterInfo();
|
||||
if (us != null)
|
||||
_connectMask = _connectChecker.getOutboundMask(us);
|
||||
else
|
||||
_connectMask = ConnectChecker.ANY_V4;
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() { return "Kademlia NetDb Store";}
|
||||
@ -333,7 +347,11 @@ class StoreJob extends JobImpl {
|
||||
sendStoreThroughClient(msg, peer, expiration);
|
||||
} else {
|
||||
getContext().statManager().addRateData("netDb.storeRouterInfoSent", 1);
|
||||
sendDirect(msg, peer, expiration);
|
||||
// if we can't connect to peer directly, just send it out an exploratory tunnel
|
||||
if (_connectChecker.canConnect(_connectMask, peer))
|
||||
sendDirect(msg, peer, expiration);
|
||||
else
|
||||
sendStoreThroughGarlic(msg, peer, expiration);
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,9 +365,6 @@ class StoreJob extends JobImpl {
|
||||
msg.setReplyToken(token);
|
||||
msg.setReplyGateway(getContext().routerHash());
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": send(dbStore) w/ token expected " + token);
|
||||
|
||||
_state.addPending(peer.getIdentity().getHash());
|
||||
|
||||
SendSuccessJob onReply = new SendSuccessJob(getContext(), peer);
|
||||
@ -357,7 +372,7 @@ class StoreJob extends JobImpl {
|
||||
StoreMessageSelector selector = new StoreMessageSelector(getContext(), getJobId(), peer, token, expiration);
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("sending store directly to " + peer.getIdentity().getHash());
|
||||
_log.debug(getJobId() + ": sending store directly to " + peer.getIdentity().getHash());
|
||||
OutNetMessage m = new OutNetMessage(getContext(), msg, expiration, STORE_PRIORITY, peer);
|
||||
m.setOnFailedReplyJob(onFail);
|
||||
m.setOnFailedSendJob(onFail);
|
||||
@ -388,7 +403,7 @@ class StoreJob extends JobImpl {
|
||||
msg.setReplyGateway(replyTunnel.getPeer(0));
|
||||
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(getJobId() + ": send(dbStore) w/ token expected " + token);
|
||||
_log.debug(getJobId() + ": send store thru expl. tunnel to " + peer.getIdentity().getHash() + " w/ token expected " + token);
|
||||
|
||||
_state.addPending(to);
|
||||
|
||||
@ -618,8 +633,8 @@ class StoreJob extends JobImpl {
|
||||
getContext().statManager().addRateData("netDb.ackTime", howLong, howLong);
|
||||
|
||||
if ( (_sendThrough != null) && (_msgSize > 0) ) {
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("sent a " + _msgSize + " byte netDb message through tunnel " + _sendThrough + " after " + howLong);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("sent a " + _msgSize + " byte netDb message through tunnel " + _sendThrough + " after " + howLong);
|
||||
for (int i = 0; i < _sendThrough.getLength(); i++)
|
||||
getContext().profileManager().tunnelDataPushed(_sendThrough.getPeer(i), howLong, _msgSize);
|
||||
_sendThrough.incrementVerifiedBytesTransferred(_msgSize);
|
||||
|
@ -1581,8 +1581,9 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
private boolean locked_needsRebuild() {
|
||||
if (_needsRebuild) return true; // simple enough
|
||||
if (_context.router().isHidden()) return false;
|
||||
RouterAddress addr = getCurrentAddress(false);
|
||||
if (introducersRequired()) {
|
||||
boolean v6Only = getIPv6Config() == IPV6_ONLY;
|
||||
RouterAddress addr = getCurrentAddress(v6Only);
|
||||
if (!v6Only && introducersRequired()) {
|
||||
UDPAddress ua = new UDPAddress(addr);
|
||||
long now = _context.clock().now();
|
||||
int valid = 0;
|
||||
@ -2139,7 +2140,8 @@ public class UDPTransport extends TransportImpl implements TimedWeightedPriority
|
||||
}
|
||||
} else {
|
||||
if (!introducersRequired()) {
|
||||
RouterAddress cur = getCurrentExternalAddress(false);
|
||||
boolean v6Only = getIPv6Config() == IPV6_ONLY;
|
||||
RouterAddress cur = getCurrentExternalAddress(v6Only);
|
||||
if (cur != null)
|
||||
host = cur.getHost();
|
||||
}
|
||||
|
328
router/java/src/net/i2p/router/tunnel/pool/ConnectChecker.java
Normal file
328
router/java/src/net/i2p/router/tunnel/pool/ConnectChecker.java
Normal file
@ -0,0 +1,328 @@
|
||||
package net.i2p.router.tunnel.pool;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.transport.TransportManager;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* Tools to check transport compatibility.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
public class ConnectChecker {
|
||||
protected final RouterContext ctx;
|
||||
protected final Log log;
|
||||
|
||||
private static final int NTCP_V4 = 0x01;
|
||||
private static final int SSU_V4 = 0x02;
|
||||
public static final int ANY_V4 = NTCP_V4 | SSU_V4;
|
||||
private static final int NTCP_V6 = 0x04;
|
||||
private static final int SSU_V6 = 0x08;
|
||||
private static final int ANY_V6 = NTCP_V6 | SSU_V6;
|
||||
|
||||
|
||||
public ConnectChecker(RouterContext context) {
|
||||
ctx = context;
|
||||
log = ctx.logManager().getLog(getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is NTCP disabled?
|
||||
* @since 0.9.34
|
||||
*/
|
||||
protected boolean isNTCPDisabled() {
|
||||
return !TransportManager.isNTCPEnabled(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is SSU disabled?
|
||||
* @since 0.9.34
|
||||
*/
|
||||
protected boolean isSSUDisabled() {
|
||||
return !ctx.getBooleanPropertyDefaultTrue(TransportManager.PROP_ENABLE_UDP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can "from" connect to "to" based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
* Either from or to may be us.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Published addresses or introducers may have changed.
|
||||
* Even if a can't connect to b, they may already be connected
|
||||
* as b connected to a.
|
||||
*
|
||||
* @return true if we don't have either RI
|
||||
* @since 0.9.34
|
||||
*/
|
||||
public boolean canConnect(Hash from, Hash to) {
|
||||
Hash us = ctx.routerHash();
|
||||
if (us == null)
|
||||
return true;
|
||||
boolean usf = from.equals(us);
|
||||
if (usf && ctx.commSystem().isEstablished(to))
|
||||
return true;
|
||||
boolean ust = to.equals(us);
|
||||
if (ust && ctx.commSystem().isEstablished(from))
|
||||
return true;
|
||||
RouterInfo rt = ctx.netDb().lookupRouterInfoLocally(to);
|
||||
if (rt == null)
|
||||
return true;
|
||||
RouterInfo rf = ctx.netDb().lookupRouterInfoLocally(from);
|
||||
if (rf == null)
|
||||
return true;
|
||||
int ct;
|
||||
if (ust) {
|
||||
// to us
|
||||
ct = getInboundMask(rt);
|
||||
} else {
|
||||
Collection<RouterAddress> at = rt.getAddresses();
|
||||
// assume nothing if hidden
|
||||
if (at.isEmpty())
|
||||
return false;
|
||||
ct = getConnectMask(at);
|
||||
}
|
||||
|
||||
int cf;
|
||||
if (usf) {
|
||||
// from us
|
||||
cf = getOutboundMask(rf);
|
||||
} else {
|
||||
Collection<RouterAddress> a = rf.getAddresses();
|
||||
if (a.isEmpty()) {
|
||||
// assume IPv4 if hidden
|
||||
cf = NTCP_V4 | SSU_V4;
|
||||
} else {
|
||||
cf = getConnectMask(a);
|
||||
}
|
||||
}
|
||||
|
||||
boolean rv = (ct & cf) != 0;
|
||||
if (!rv && log.shouldWarn()) {
|
||||
log.warn("Cannot connect: " +
|
||||
(usf ? "us" : from.toString()) + " with mask " + cf + "\nto " +
|
||||
(ust ? "us" : to.toString()) + " with mask " + ct);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we connect to "to" based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Does not check isEstablished(); do that first.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
public boolean canConnect(int ourMask, RouterInfo to) {
|
||||
Collection<RouterAddress> ra = to.getAddresses();
|
||||
// assume nothing if hidden
|
||||
if (ra.isEmpty())
|
||||
return false;
|
||||
int ct = getConnectMask(ra);
|
||||
boolean rv = (ourMask & ct) != 0;
|
||||
//if (!rv && log.shouldWarn())
|
||||
// log.warn("Cannot connect: us with mask " + ourMask + " to " + to + " with mask " + ct);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can "from" connect to us based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Does not check isEstablished(); do that first.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
public boolean canConnect(RouterInfo from, int ourMask) {
|
||||
if (ourMask == 0)
|
||||
return false;
|
||||
Collection<RouterAddress> ra = from.getAddresses();
|
||||
int cf;
|
||||
// assume v4 if hidden
|
||||
if (ra.isEmpty())
|
||||
cf = NTCP_V4 | SSU_V4;
|
||||
else
|
||||
cf = getConnectMask(ra);
|
||||
boolean rv = (cf & ourMask) != 0;
|
||||
//if (!rv && log.shouldWarn())
|
||||
// log.warn("Cannot connect: " + from + " with mask " + cf + " to us with mask " + ourMask);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our inbound mask.
|
||||
* For most cases, we use what we published, i.e. getConnectMask()
|
||||
*
|
||||
* @return bitmask for accepting connections
|
||||
* @since 0.9.34
|
||||
*/
|
||||
public int getInboundMask(RouterInfo us) {
|
||||
// to us
|
||||
int ct = 0;
|
||||
Status status = ctx.commSystem().getStatus();
|
||||
switch (status) {
|
||||
case OK:
|
||||
case IPV4_UNKNOWN_IPV6_OK:
|
||||
case IPV4_FIREWALLED_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_UNKNOWN:
|
||||
case IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||
case IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||
case IPV4_OK_IPV6_FIREWALLED:
|
||||
case DIFFERENT:
|
||||
case REJECT_UNSOLICITED:
|
||||
// use what we published
|
||||
Collection<RouterAddress> at = us.getAddresses();
|
||||
if (at.isEmpty())
|
||||
return 0;
|
||||
ct = getConnectMask(at);
|
||||
break;
|
||||
|
||||
case IPV4_DISABLED_IPV6_OK:
|
||||
case IPV4_DISABLED_IPV6_UNKNOWN:
|
||||
// maybe should return zero for this one?
|
||||
case IPV4_DISABLED_IPV6_FIREWALLED:
|
||||
// TODO look at force-firewalled settings per-transport
|
||||
if (!isNTCPDisabled())
|
||||
ct |= NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
ct |= SSU_V6;
|
||||
break;
|
||||
|
||||
case IPV4_OK_IPV6_UNKNOWN:
|
||||
case DISCONNECTED:
|
||||
case HOSED:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
if (!isNTCPDisabled())
|
||||
ct |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
ct |= SSU_V4;
|
||||
break;
|
||||
}
|
||||
return ct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our outbound mask.
|
||||
* For most cases, we use our comm system status.
|
||||
*
|
||||
* @return bitmask for initiating connections
|
||||
* @since 0.9.34
|
||||
*/
|
||||
public int getOutboundMask(RouterInfo us) {
|
||||
// from us
|
||||
int cf = 0;
|
||||
Status status = ctx.commSystem().getStatus();
|
||||
switch (status) {
|
||||
case OK:
|
||||
// use what we published, as the OK state doesn't tell us about IPv6
|
||||
// Addresses.isConnectedIPv6() is too slow
|
||||
Collection<RouterAddress> a = us.getAddresses();
|
||||
if (a.isEmpty()) {
|
||||
// we are hidden
|
||||
// TODO ipv6
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4;
|
||||
} else {
|
||||
cf = getConnectMask(a);
|
||||
}
|
||||
break;
|
||||
|
||||
case IPV4_OK_IPV6_FIREWALLED:
|
||||
case IPV4_UNKNOWN_IPV6_OK:
|
||||
case IPV4_FIREWALLED_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_OK:
|
||||
case IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4 | NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4 | SSU_V6;
|
||||
break;
|
||||
|
||||
case IPV4_DISABLED_IPV6_OK:
|
||||
case IPV4_DISABLED_IPV6_UNKNOWN:
|
||||
case IPV4_DISABLED_IPV6_FIREWALLED:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V6;
|
||||
break;
|
||||
|
||||
case DIFFERENT:
|
||||
case IPV4_SNAT_IPV6_UNKNOWN:
|
||||
case IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||
case REJECT_UNSOLICITED:
|
||||
case IPV4_OK_IPV6_UNKNOWN:
|
||||
case DISCONNECTED:
|
||||
case HOSED:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4;
|
||||
break;
|
||||
}
|
||||
return cf;
|
||||
}
|
||||
|
||||
/** prevent object churn */
|
||||
private static final String IHOST[] = { "ihost0", "ihost1", "ihost2" };
|
||||
|
||||
/**
|
||||
* @param addrs non-empty, set your own default if empty
|
||||
* @return bitmask of v4/v6 NTCP/SSU
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private static int getConnectMask(Collection<RouterAddress> addrs) {
|
||||
int rv = 0;
|
||||
for (RouterAddress ra : addrs) {
|
||||
String style = ra.getTransportStyle();
|
||||
String host = ra.getHost();
|
||||
if ("NTCP".equals(style)) {
|
||||
if (host != null) {
|
||||
if (host.contains(":"))
|
||||
rv |= NTCP_V6;
|
||||
else
|
||||
rv |= NTCP_V4;
|
||||
}
|
||||
} else if ("SSU".equals(style)) {
|
||||
if (host == null) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
String ihost = ra.getOption(IHOST[i]);
|
||||
if (ihost == null)
|
||||
break;
|
||||
if (ihost.contains(":"))
|
||||
rv |= SSU_V6;
|
||||
else
|
||||
rv |= SSU_V4;
|
||||
}
|
||||
} else if (host.contains(":")) {
|
||||
rv |= SSU_V6;
|
||||
} else {
|
||||
rv |= SSU_V4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
@ -12,20 +12,16 @@ import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.router.RouterAddress;
|
||||
import net.i2p.data.router.RouterInfo;
|
||||
import net.i2p.router.Router;
|
||||
import net.i2p.router.CommSystemFacade.Status;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.networkdb.kademlia.FloodfillNetworkDatabaseFacade;
|
||||
import net.i2p.router.transport.TransportManager;
|
||||
import net.i2p.router.transport.TransportUtil;
|
||||
import net.i2p.router.util.HashDistance;
|
||||
import net.i2p.util.Log;
|
||||
@ -35,23 +31,11 @@ import net.i2p.util.VersionComparator;
|
||||
* Coordinate the selection of peers to go into a tunnel for one particular
|
||||
* pool.
|
||||
*
|
||||
* Todo: there's nothing non-static in here
|
||||
*/
|
||||
public abstract class TunnelPeerSelector {
|
||||
protected final RouterContext ctx;
|
||||
protected final Log log;
|
||||
|
||||
private static final int NTCP_V4 = 0x01;
|
||||
private static final int SSU_V4 = 0x02;
|
||||
private static final int ANY_V4 = NTCP_V4 | SSU_V4;
|
||||
private static final int NTCP_V6 = 0x04;
|
||||
private static final int SSU_V6 = 0x08;
|
||||
private static final int ANY_V6 = NTCP_V6 | SSU_V6;
|
||||
|
||||
public abstract class TunnelPeerSelector extends ConnectChecker {
|
||||
|
||||
protected TunnelPeerSelector(RouterContext context) {
|
||||
ctx = context;
|
||||
log = ctx.logManager().getLog(getClass());
|
||||
super(context);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,7 +104,7 @@ public abstract class TunnelPeerSelector {
|
||||
if (opts != null) {
|
||||
String peers = opts.getProperty("explicitPeers");
|
||||
if (peers == null)
|
||||
peers = I2PAppContext.getGlobalContext().getProperty("explicitPeers");
|
||||
peers = ctx.getProperty("explicitPeers");
|
||||
if (peers != null)
|
||||
return true;
|
||||
}
|
||||
@ -139,7 +123,7 @@ public abstract class TunnelPeerSelector {
|
||||
peers = opts.getProperty("explicitPeers");
|
||||
|
||||
if (peers == null)
|
||||
peers = I2PAppContext.getGlobalContext().getProperty("explicitPeers");
|
||||
peers = ctx.getProperty("explicitPeers");
|
||||
|
||||
List<Hash> rv = new ArrayList<Hash>();
|
||||
StringTokenizer tok = new StringTokenizer(peers, ",");
|
||||
@ -350,22 +334,6 @@ public abstract class TunnelPeerSelector {
|
||||
return TransportUtil.getIPv6Config(ctx, "SSU") == TransportUtil.IPv6Config.IPV6_ONLY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is NTCP disabled?
|
||||
* @since 0.9.34
|
||||
*/
|
||||
protected boolean isNTCPDisabled() {
|
||||
return !TransportManager.isNTCPEnabled(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is SSU disabled?
|
||||
* @since 0.9.34
|
||||
*/
|
||||
protected boolean isSSUDisabled() {
|
||||
return !ctx.getBooleanPropertyDefaultTrue(TransportManager.PROP_ENABLE_UDP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we allow as OBEP?
|
||||
* This just checks for IPv4 support.
|
||||
@ -759,281 +727,4 @@ public abstract class TunnelPeerSelector {
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can "from" connect to "to" based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
* Either from or to may be us.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Published addresses or introducers may have changed.
|
||||
* Even if a can't connect to b, they may already be connected
|
||||
* as b connected to a.
|
||||
*
|
||||
* @return true if we don't have either RI
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean canConnect(Hash from, Hash to) {
|
||||
Hash us = ctx.routerHash();
|
||||
if (us == null)
|
||||
return true;
|
||||
boolean usf = from.equals(us);
|
||||
if (usf && ctx.commSystem().isEstablished(to))
|
||||
return true;
|
||||
boolean ust = to.equals(us);
|
||||
if (ust && ctx.commSystem().isEstablished(from))
|
||||
return true;
|
||||
RouterInfo rt = ctx.netDb().lookupRouterInfoLocally(to);
|
||||
if (rt == null)
|
||||
return true;
|
||||
RouterInfo rf = ctx.netDb().lookupRouterInfoLocally(from);
|
||||
if (rf == null)
|
||||
return true;
|
||||
int ct;
|
||||
if (ust) {
|
||||
// to us
|
||||
ct = getInboundMask(rt);
|
||||
} else {
|
||||
Collection<RouterAddress> at = rt.getAddresses();
|
||||
// assume nothing if hidden
|
||||
if (at.isEmpty())
|
||||
return false;
|
||||
ct = getConnectMask(at);
|
||||
}
|
||||
|
||||
int cf;
|
||||
if (usf) {
|
||||
// from us
|
||||
cf = getOutboundMask(rf);
|
||||
} else {
|
||||
Collection<RouterAddress> a = rf.getAddresses();
|
||||
if (a.isEmpty()) {
|
||||
// assume IPv4 if hidden
|
||||
cf = NTCP_V4 | SSU_V4;
|
||||
} else {
|
||||
cf = getConnectMask(a);
|
||||
}
|
||||
}
|
||||
|
||||
boolean rv = (ct & cf) != 0;
|
||||
if (!rv && log.shouldWarn()) {
|
||||
log.warn("Cannot connect: " +
|
||||
(usf ? "us" : from.toString()) + " with mask " + cf + "\nto " +
|
||||
(ust ? "us" : to.toString()) + " with mask " + ct);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can we connect to "to" based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Does not check isEstablished(); do that first.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean canConnect(int ourMask, RouterInfo to) {
|
||||
Collection<RouterAddress> ra = to.getAddresses();
|
||||
// assume nothing if hidden
|
||||
if (ra.isEmpty())
|
||||
return false;
|
||||
int ct = getConnectMask(ra);
|
||||
boolean rv = (ourMask & ct) != 0;
|
||||
//if (!rv && log.shouldWarn())
|
||||
// log.warn("Cannot connect: us with mask " + ourMask + " to " + to + " with mask " + ct);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can "from" connect to us based on published addresses?
|
||||
*
|
||||
* This is intended for tunnel candidates, where we already have
|
||||
* the RI. Will not force RI lookups.
|
||||
*
|
||||
* This is best effort, as we can't know for sure.
|
||||
* Does not check isEstablished(); do that first.
|
||||
*
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private boolean canConnect(RouterInfo from, int ourMask) {
|
||||
if (ourMask == 0)
|
||||
return false;
|
||||
Collection<RouterAddress> ra = from.getAddresses();
|
||||
int cf;
|
||||
// assume v4 if hidden
|
||||
if (ra.isEmpty())
|
||||
cf = NTCP_V4 | SSU_V4;
|
||||
else
|
||||
cf = getConnectMask(ra);
|
||||
boolean rv = (cf & ourMask) != 0;
|
||||
//if (!rv && log.shouldWarn())
|
||||
// log.warn("Cannot connect: " + from + " with mask " + cf + " to us with mask " + ourMask);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our inbound mask.
|
||||
* For most cases, we use what we published, i.e. getConnectMask()
|
||||
*
|
||||
* @return bitmask for accepting connections
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private int getInboundMask(RouterInfo us) {
|
||||
// to us
|
||||
int ct = 0;
|
||||
Status status = ctx.commSystem().getStatus();
|
||||
switch (status) {
|
||||
case OK:
|
||||
case IPV4_UNKNOWN_IPV6_OK:
|
||||
case IPV4_FIREWALLED_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_UNKNOWN:
|
||||
case IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||
case IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||
case IPV4_OK_IPV6_FIREWALLED:
|
||||
case DIFFERENT:
|
||||
case REJECT_UNSOLICITED:
|
||||
// use what we published
|
||||
Collection<RouterAddress> at = us.getAddresses();
|
||||
if (at.isEmpty())
|
||||
return 0;
|
||||
ct = getConnectMask(at);
|
||||
break;
|
||||
|
||||
case IPV4_DISABLED_IPV6_OK:
|
||||
case IPV4_DISABLED_IPV6_UNKNOWN:
|
||||
// maybe should return zero for this one?
|
||||
case IPV4_DISABLED_IPV6_FIREWALLED:
|
||||
// TODO look at force-firewalled settings per-transport
|
||||
if (!isNTCPDisabled())
|
||||
ct |= NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
ct |= SSU_V6;
|
||||
break;
|
||||
|
||||
case IPV4_OK_IPV6_UNKNOWN:
|
||||
case DISCONNECTED:
|
||||
case HOSED:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
if (!isNTCPDisabled())
|
||||
ct |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
ct |= SSU_V4;
|
||||
break;
|
||||
}
|
||||
return ct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our outbound mask.
|
||||
* For most cases, we use our comm system status.
|
||||
*
|
||||
* @return bitmask for initiating connections
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private int getOutboundMask(RouterInfo us) {
|
||||
// from us
|
||||
int cf = 0;
|
||||
Status status = ctx.commSystem().getStatus();
|
||||
switch (status) {
|
||||
case OK:
|
||||
// use what we published, as the OK state doesn't tell us about IPv6
|
||||
// Addresses.isConnectedIPv6() is too slow
|
||||
Collection<RouterAddress> a = us.getAddresses();
|
||||
if (a.isEmpty()) {
|
||||
// we are hidden
|
||||
// TODO ipv6
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4;
|
||||
} else {
|
||||
cf = getConnectMask(a);
|
||||
}
|
||||
break;
|
||||
|
||||
case IPV4_OK_IPV6_FIREWALLED:
|
||||
case IPV4_UNKNOWN_IPV6_OK:
|
||||
case IPV4_FIREWALLED_IPV6_OK:
|
||||
case IPV4_SNAT_IPV6_OK:
|
||||
case IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4 | NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4 | SSU_V6;
|
||||
break;
|
||||
|
||||
case IPV4_DISABLED_IPV6_OK:
|
||||
case IPV4_DISABLED_IPV6_UNKNOWN:
|
||||
case IPV4_DISABLED_IPV6_FIREWALLED:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V6;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V6;
|
||||
break;
|
||||
|
||||
case DIFFERENT:
|
||||
case IPV4_SNAT_IPV6_UNKNOWN:
|
||||
case IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||
case REJECT_UNSOLICITED:
|
||||
case IPV4_OK_IPV6_UNKNOWN:
|
||||
case DISCONNECTED:
|
||||
case HOSED:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
if (!isNTCPDisabled())
|
||||
cf |= NTCP_V4;
|
||||
if (!isSSUDisabled())
|
||||
cf |= SSU_V4;
|
||||
break;
|
||||
}
|
||||
return cf;
|
||||
}
|
||||
|
||||
/** prevent object churn */
|
||||
private static final String IHOST[] = { "ihost0", "ihost1", "ihost2" };
|
||||
|
||||
/**
|
||||
* @param addrs non-empty, set your own default if empty
|
||||
* @return bitmask of v4/v6 NTCP/SSU
|
||||
* @since 0.9.34
|
||||
*/
|
||||
private static int getConnectMask(Collection<RouterAddress> addrs) {
|
||||
int rv = 0;
|
||||
for (RouterAddress ra : addrs) {
|
||||
String style = ra.getTransportStyle();
|
||||
String host = ra.getHost();
|
||||
if ("NTCP".equals(style)) {
|
||||
if (host != null) {
|
||||
if (host.contains(":"))
|
||||
rv |= NTCP_V6;
|
||||
else
|
||||
rv |= NTCP_V4;
|
||||
}
|
||||
} else if ("SSU".equals(style)) {
|
||||
if (host == null) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
String ihost = ra.getOption(IHOST[i]);
|
||||
if (ihost == null)
|
||||
break;
|
||||
if (ihost.contains(":"))
|
||||
rv |= SSU_V6;
|
||||
else
|
||||
rv |= SSU_V4;
|
||||
}
|
||||
} else if (host.contains(":")) {
|
||||
rv |= SSU_V6;
|
||||
} else {
|
||||
rv |= SSU_V4;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user