use of net.i2p.router.TunnelManagerFacade in project i2p.i2p by i2p.
the class BuildExecutor method run2.
private void run2() {
List<TunnelPool> wanted = new ArrayList<TunnelPool>(MAX_CONCURRENT_BUILDS);
List<TunnelPool> pools = new ArrayList<TunnelPool>(8);
while (_isRunning && !_manager.isShutdown()) {
// loopBegin = System.currentTimeMillis();
try {
// resets repoll to false unless there are inbound requeusts pending
_repoll = false;
_manager.listPools(pools);
for (int i = 0; i < pools.size(); i++) {
TunnelPool pool = pools.get(i);
if (!pool.isAlive())
continue;
int howMany = pool.countHowManyToBuild();
for (int j = 0; j < howMany; j++) wanted.add(pool);
}
// allowed() also expires timed out requests (for new style requests)
int allowed = allowed();
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("Allowed: " + allowed + " wanted: " + wanted);
// zero hop ones can run inline
allowed = buildZeroHopTunnels(wanted, allowed);
// afterBuildZeroHop = System.currentTimeMillis();
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("Zero hops built, Allowed: " + allowed + " wanted: " + wanted);
// int realBuilt = 0;
TunnelManagerFacade mgr = _context.tunnelManager();
if ((mgr == null) || (mgr.getFreeTunnelCount() <= 0) || (mgr.getOutboundTunnelCount() <= 0)) {
// try to kickstart it to build a fallback, otherwise we may get stuck here for a long time (minutes)
if (mgr != null) {
if (mgr.getFreeTunnelCount() <= 0)
mgr.selectInboundTunnel();
if (mgr.getOutboundTunnelCount() <= 0)
mgr.selectOutboundTunnel();
}
synchronized (_currentlyBuilding) {
if (!_repoll) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("No tunnel to build with (allowed=" + allowed + ", wanted=" + wanted.size() + "), wait for a while");
try {
_currentlyBuilding.wait(1 * 1000 + _context.random().nextInt(1 * 1000));
} catch (InterruptedException ie) {
}
}
}
} else {
if ((allowed > 0) && (!wanted.isEmpty())) {
if (wanted.size() > 1) {
Collections.shuffle(wanted, _context.random());
// We generally prioritize pools with no tunnels,
// but sometimes (particularly at startup), the paired tunnel endpoint
// can start dropping the build messages... or hit connection limits,
// or be broken in other ways. So we allow other pools to go
// to the front of the line sometimes, to prevent being "locked up"
// for several minutes.
boolean preferEmpty = _context.random().nextInt(4) != 0;
// Java 7 TimSort - see info in TunnelPoolComparator
DataHelper.sort(wanted, new TunnelPoolComparator(preferEmpty));
}
// a long, long time
if (allowed > 2)
allowed = 2;
for (int i = 0; (i < allowed) && (!wanted.isEmpty()); i++) {
TunnelPool pool = wanted.remove(0);
// if (pool.countWantedTunnels() <= 0)
// continue;
long bef = System.currentTimeMillis();
PooledTunnelCreatorConfig cfg = pool.configureNewTunnel();
if (cfg != null) {
// 0hops are taken care of above, these are nonstandard 0hops
if (cfg.getLength() <= 1 && !pool.needFallback()) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("We don't need more fallbacks for " + pool);
// 0hop, we can keep going, as there's no worry about throttling
i--;
pool.buildComplete(cfg);
continue;
}
long pTime = System.currentTimeMillis() - bef;
_context.statManager().addRateData("tunnel.buildConfigTime", pTime, 0);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Configuring new tunnel " + i + " for " + pool + ": " + cfg);
buildTunnel(pool, cfg);
// realBuilt++;
} else {
i--;
}
}
}
// wait whether we built tunnels or not
try {
synchronized (_currentlyBuilding) {
if (!_repoll) {
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("Nothin' doin (allowed=" + allowed + ", wanted=" + wanted.size() + ", pending=" + pendingRemaining + "), wait for a while");
// if (allowed <= 0)
_currentlyBuilding.wait((LOOP_TIME / 2) + _context.random().nextInt(LOOP_TIME));
// else // wanted <= 0
// _currentlyBuilding.wait(_context.random().nextInt(30*1000));
}
}
} catch (InterruptedException ie) {
// someone wanted to build something
}
}
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("build loop complete, tot=" + (afterHandleInbound-loopBegin) +
// " inReply=" + (afterHandleInboundReplies-beforeHandleInboundReplies) +
// " zeroHop=" + (afterBuildZeroHop-afterHandleInboundReplies) +
// " real=" + (afterBuildReal-afterBuildZeroHop) +
// " in=" + (afterHandleInbound-afterBuildReal) +
// " built=" + realBuilt +
// " pending=" + pendingRemaining);
} catch (RuntimeException e) {
_log.log(Log.CRIT, "B0rked in the tunnel builder", e);
try {
Thread.sleep(LOOP_TIME);
} catch (InterruptedException ie) {
}
}
wanted.clear();
pools.clear();
}
if (_log.shouldLog(Log.WARN))
_log.warn("Done building");
}
use of net.i2p.router.TunnelManagerFacade in project i2p.i2p by i2p.
the class BuildRequestor method request.
/**
* Send out a build request message.
*
* @param cfg ReplyMessageId must be set
* @return success
*/
public static boolean request(RouterContext ctx, TunnelPool pool, PooledTunnelCreatorConfig cfg, BuildExecutor exec) {
// new style crypto fills in all the blanks, while the old style waits for replies to fill in the next hop, etc
prepare(ctx, cfg);
if (cfg.getLength() <= 1) {
buildZeroHop(ctx, pool, cfg, exec);
return true;
}
Log log = ctx.logManager().getLog(BuildRequestor.class);
cfg.setTunnelPool(pool);
TunnelInfo pairedTunnel = null;
Hash farEnd = cfg.getFarEnd();
TunnelManagerFacade mgr = ctx.tunnelManager();
boolean isInbound = pool.getSettings().isInbound();
if (pool.getSettings().isExploratory() || !usePairedTunnels(ctx)) {
if (isInbound)
pairedTunnel = mgr.selectOutboundExploratoryTunnel(farEnd);
else
pairedTunnel = mgr.selectInboundExploratoryTunnel(farEnd);
} else {
// building a client tunnel
if (isInbound)
pairedTunnel = mgr.selectOutboundTunnel(pool.getSettings().getDestination(), farEnd);
else
pairedTunnel = mgr.selectInboundTunnel(pool.getSettings().getDestination(), farEnd);
if (pairedTunnel == null) {
if (isInbound) {
// random more reliable than closest ??
// pairedTunnel = mgr.selectOutboundExploratoryTunnel(farEnd);
pairedTunnel = mgr.selectOutboundTunnel();
if (pairedTunnel != null && pairedTunnel.getLength() <= 1 && mgr.getOutboundSettings().getLength() > 0 && mgr.getOutboundSettings().getLength() + mgr.getOutboundSettings().getLengthVariance() > 0) {
// don't build using a zero-hop expl.,
// as it is both very bad for anonomyity,
// and it takes a build slot away from exploratory
pairedTunnel = null;
}
} else {
// random more reliable than closest ??
// pairedTunnel = mgr.selectInboundExploratoryTunnel(farEnd);
pairedTunnel = mgr.selectInboundTunnel();
if (pairedTunnel != null && pairedTunnel.getLength() <= 1 && mgr.getInboundSettings().getLength() > 0 && mgr.getInboundSettings().getLength() + mgr.getInboundSettings().getLengthVariance() > 0) {
// ditto
pairedTunnel = null;
}
}
if (pairedTunnel != null && log.shouldLog(Log.INFO))
log.info("Couldn't find a paired tunnel for " + cfg + ", using exploratory tunnel");
}
}
if (pairedTunnel == null) {
if (log.shouldLog(Log.WARN))
log.warn("Tunnel build failed, as we couldn't find a paired tunnel for " + cfg);
exec.buildComplete(cfg, pool);
// Not even an exploratory tunnel? We are in big trouble.
// Let's not spin through here too fast.
// But don't let a client tunnel waiting for exploratories slow things down too much,
// as there may be other tunnel pools who can build
int ms = pool.getSettings().isExploratory() ? 250 : 25;
try {
Thread.sleep(ms);
} catch (InterruptedException ie) {
}
return false;
}
// long beforeCreate = System.currentTimeMillis();
TunnelBuildMessage msg = createTunnelBuildMessage(ctx, pool, cfg, pairedTunnel, exec);
// long createTime = System.currentTimeMillis()-beforeCreate;
if (msg == null) {
if (log.shouldLog(Log.WARN))
log.warn("Tunnel build failed, as we couldn't create the tunnel build message for " + cfg);
exec.buildComplete(cfg, pool);
return false;
}
// long beforeDispatch = System.currentTimeMillis();
if (cfg.isInbound()) {
if (log.shouldLog(Log.INFO))
log.info("Sending the tunnel build request " + msg.getUniqueId() + " out the tunnel " + pairedTunnel + " to " + cfg.getPeer(0) + " for " + cfg + " waiting for the reply of " + cfg.getReplyMessageId());
// send it out a tunnel targetting the first hop
// TODO - would be nice to have a TunnelBuildFirstHopFailJob queued if the
// pairedTunnel is zero-hop, but no way to do that?
ctx.tunnelDispatcher().dispatchOutbound(msg, pairedTunnel.getSendTunnelId(0), cfg.getPeer(0));
} else {
if (log.shouldLog(Log.INFO))
log.info("Sending the tunnel build request directly to " + cfg.getPeer(1) + " for " + cfg + " waiting for the reply of " + cfg.getReplyMessageId() + " with msgId=" + msg.getUniqueId());
// send it directly to the first hop
// Add some fuzz to the TBM expiration to make it harder to guess how many hops
// or placement in the tunnel
msg.setMessageExpiration(ctx.clock().now() + BUILD_MSG_TIMEOUT + ctx.random().nextLong(20 * 1000));
// We set the OutNetMessage expiration much shorter, so that the
// TunnelBuildFirstHopFailJob fires before the 13s build expiration.
RouterInfo peer = ctx.netDb().lookupRouterInfoLocally(cfg.getPeer(1));
if (peer == null) {
if (log.shouldLog(Log.WARN))
log.warn("Could not find the next hop to send the outbound request to: " + cfg);
exec.buildComplete(cfg, pool);
return false;
}
OutNetMessage outMsg = new OutNetMessage(ctx, msg, ctx.clock().now() + FIRST_HOP_TIMEOUT, PRIORITY, peer);
outMsg.setOnFailedSendJob(new TunnelBuildFirstHopFailJob(ctx, pool, cfg, exec));
ctx.outNetMessagePool().add(outMsg);
}
// + "ms and dispatched in " + (System.currentTimeMillis()-beforeDispatch));
return true;
}
use of net.i2p.router.TunnelManagerFacade in project i2p.i2p by i2p.
the class ExploratoryPeerSelector method selectPeers.
/**
* Returns ENDPOINT FIRST, GATEWAY LAST!!!!
* In: us .. closest .. middle .. IBGW
* Out: OBGW .. middle .. closest .. us
*
* @return ordered list of Hash objects (one per peer) specifying what order
* they should appear in a tunnel (ENDPOINT FIRST). This includes
* the local router in the list. If there are no tunnels or peers
* to build through, and the settings reject 0 hop tunnels, this will
* return null.
*/
public List<Hash> selectPeers(TunnelPoolSettings settings) {
int length = getLength(settings);
if (length < 0) {
if (log.shouldLog(Log.DEBUG))
log.debug("Length requested is zero: " + settings);
return null;
}
// if (false && shouldSelectExplicit(settings)) {
// List<Hash> rv = selectExplicit(settings, length);
// if (l.shouldLog(Log.DEBUG))
// l.debug("Explicit peers selected: " + rv);
// return rv;
// }
boolean isInbound = settings.isInbound();
Set<Hash> exclude = getExclude(isInbound, true);
exclude.add(ctx.routerHash());
// special cases
boolean nonzero = length > 0;
boolean exploreHighCap = nonzero && shouldPickHighCap();
boolean v6Only = nonzero && isIPv6Only();
boolean ntcpDisabled = nonzero && isNTCPDisabled();
boolean ssuDisabled = nonzero && isSSUDisabled();
boolean checkClosestHop = v6Only || ntcpDisabled || ssuDisabled;
boolean hidden = nonzero && (ctx.router().isHidden() || ctx.router().getRouterInfo().getAddressCount() <= 0);
boolean hiddenInbound = hidden && isInbound;
boolean hiddenOutbound = hidden && !isInbound;
boolean lowOutbound = nonzero && !isInbound && !ctx.commSystem().haveHighOutboundCapacity();
// closest-hop restrictions
// Since we're applying orderPeers() later, we don't know
// which will be the closest hop, so select the closest one here if necessary.
Hash closestHop = null;
if (v6Only || hiddenInbound || lowOutbound) {
Set<Hash> closestExclude;
if (checkClosestHop) {
closestExclude = getClosestHopExclude(isInbound);
if (closestExclude != null)
closestExclude.addAll(exclude);
else
closestExclude = exclude;
} else {
closestExclude = exclude;
}
Set<Hash> closest = new HashSet<Hash>(1);
if (hiddenInbound || lowOutbound) {
// use only connected peers so we don't make more connections
if (log.shouldLog(Log.INFO))
log.info("EPS SANFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size());
// SANFP adds all not-connected to exclude, so make a copy
Set<Hash> SANFPExclude = new HashSet<Hash>(closestExclude);
ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, closest);
if (closest.isEmpty()) {
// ANFP does not fall back to non-connected
if (log.shouldLog(Log.INFO))
log.info("EPS SFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size());
ctx.profileOrganizer().selectFastPeers(1, closestExclude, closest);
}
} else if (exploreHighCap) {
if (log.shouldLog(Log.INFO))
log.info("EPS SHCP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size());
ctx.profileOrganizer().selectHighCapacityPeers(1, closestExclude, closest);
} else {
if (log.shouldLog(Log.INFO))
log.info("EPS SNFP closest " + (isInbound ? "IB" : "OB") + " exclude " + closestExclude.size());
ctx.profileOrganizer().selectNotFailingPeers(1, closestExclude, closest, false);
}
if (!closest.isEmpty()) {
closestHop = closest.iterator().next();
exclude.add(closestHop);
length--;
}
}
// furthest-hop restrictions
// Since we're applying orderPeers() later, we don't know
// which will be the furthest hop, so select the furthest one here if necessary.
Hash furthestHop = null;
if (hiddenOutbound && length > 0) {
// OBEP
// check for hidden and outbound, and the paired (inbound) tunnel is zero-hop
// if so, we need the OBEP to be connected to us, so we get the build reply back
// This should be rare except at startup
TunnelManagerFacade tmf = ctx.tunnelManager();
TunnelPool tp = tmf.getInboundExploratoryPool();
TunnelPoolSettings tps = tp.getSettings();
int len = tps.getLength();
boolean pickFurthest = true;
if (len <= 0 || tps.getLengthOverride() == 0 || len + tps.getLengthVariance() <= 0) {
// leave it true
} else {
for (TunnelInfo ti : tp.listTunnels()) {
if (ti.getLength() > 1) {
pickFurthest = false;
break;
}
}
}
if (pickFurthest) {
Set<Hash> furthest = new HashSet<Hash>(1);
if (log.shouldLog(Log.INFO))
log.info("EPS SANFP furthest OB exclude " + exclude.size());
// ANFP adds all not-connected to exclude, so make a copy
Set<Hash> SANFPExclude = new HashSet<Hash>(exclude);
ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, furthest);
if (furthest.isEmpty()) {
// ANFP does not fall back to non-connected
if (log.shouldLog(Log.INFO))
log.info("EPS SFP furthest OB exclude " + exclude.size());
ctx.profileOrganizer().selectFastPeers(1, exclude, furthest);
}
if (!furthest.isEmpty()) {
furthestHop = furthest.iterator().next();
exclude.add(furthestHop);
length--;
}
}
}
// Don't use ff peers for exploratory tunnels to lessen exposure to netDb searches and stores
// Hmm if they don't get explored they don't get a speed/capacity rating
// so they don't get used for client tunnels either.
// FloodfillNetworkDatabaseFacade fac = (FloodfillNetworkDatabaseFacade)ctx.netDb();
// exclude.addAll(fac.getFloodfillPeers());
HashSet<Hash> matches = new HashSet<Hash>(length);
if (length > 0) {
//
if (exploreHighCap) {
if (log.shouldLog(Log.INFO))
log.info("EPS SHCP " + length + (isInbound ? " IB" : " OB") + " exclude " + exclude.size());
ctx.profileOrganizer().selectHighCapacityPeers(length, exclude, matches);
} else {
// Peer org credits existing items in matches
if (length > 2)
ctx.profileOrganizer().selectHighCapacityPeers(length - 2, exclude, matches);
if (log.shouldLog(Log.INFO))
log.info("EPS SNFP " + length + (isInbound ? " IB" : " OB") + " exclude " + exclude.size());
ctx.profileOrganizer().selectNotFailingPeers(length, exclude, matches, false);
}
matches.remove(ctx.routerHash());
}
ArrayList<Hash> rv = new ArrayList<Hash>(matches);
if (rv.size() > 1)
orderPeers(rv, settings.getRandomKey());
if (closestHop != null) {
if (isInbound)
rv.add(0, closestHop);
else
rv.add(closestHop);
length++;
}
if (furthestHop != null) {
// always OBEP for now, nothing special for IBGW
if (isInbound)
rv.add(furthestHop);
else
rv.add(0, furthestHop);
length++;
}
// log.debug("EPS result: " + DataHelper.toString(rv));
if (isInbound)
rv.add(0, ctx.routerHash());
else
rv.add(ctx.routerHash());
if (rv.size() > 1) {
if (!checkTunnel(isInbound, rv))
rv = null;
}
return rv;
}
use of net.i2p.router.TunnelManagerFacade in project i2p.i2p by i2p.
the class ClientPeerSelector method selectPeers.
/**
* Returns ENDPOINT FIRST, GATEWAY LAST!!!!
* In: us .. closest .. middle .. IBGW
* Out: OBGW .. middle .. closest .. us
*
* @return ordered list of Hash objects (one per peer) specifying what order
* they should appear in a tunnel (ENDPOINT FIRST). This includes
* the local router in the list. If there are no tunnels or peers
* to build through, and the settings reject 0 hop tunnels, this will
* return null.
*/
public List<Hash> selectPeers(TunnelPoolSettings settings) {
int length = getLength(settings);
if (length < 0)
return null;
if ((length == 0) && (settings.getLength() + settings.getLengthVariance() > 0))
return null;
List<Hash> rv;
boolean isInbound = settings.isInbound();
if (length > 0) {
// special cases
boolean v6Only = isIPv6Only();
boolean ntcpDisabled = isNTCPDisabled();
boolean ssuDisabled = isSSUDisabled();
boolean checkClosestHop = v6Only || ntcpDisabled || ssuDisabled;
boolean hidden = ctx.router().isHidden() || ctx.router().getRouterInfo().getAddressCount() <= 0;
boolean hiddenInbound = hidden && isInbound;
boolean hiddenOutbound = hidden && !isInbound;
if (shouldSelectExplicit(settings))
return selectExplicit(settings, length);
Set<Hash> exclude = getExclude(isInbound, false);
Set<Hash> matches = new HashSet<Hash>(length);
if (length == 1) {
// closest-hop restrictions
if (checkClosestHop) {
Set<Hash> moreExclude = getClosestHopExclude(isInbound);
if (moreExclude != null)
exclude.addAll(moreExclude);
}
if (hiddenInbound) {
// SANFP adds all not-connected to exclude, so make a copy
Set<Hash> SANFPExclude = new HashSet<Hash>(exclude);
ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches);
}
if (matches.isEmpty()) {
// ANFP does not fall back to non-connected
ctx.profileOrganizer().selectFastPeers(length, exclude, matches, 0);
}
matches.remove(ctx.routerHash());
rv = new ArrayList<Hash>(matches);
} else {
// build a tunnel using 4 subtiers.
// For a 2-hop tunnel, the first hop comes from subtiers 0-1 and the last from subtiers 2-3.
// For a longer tunnels, the first hop comes from subtier 0, the middle from subtiers 2-3, and the last from subtier 1.
rv = new ArrayList<Hash>(length + 1);
Hash randomKey = settings.getRandomKey();
// OBEP or IB last hop
// group 0 or 1 if two hops, otherwise group 0
Set<Hash> lastHopExclude;
if (isInbound) {
// closest-hop restrictions
if (checkClosestHop) {
Set<Hash> moreExclude = getClosestHopExclude(false);
if (moreExclude != null) {
moreExclude.addAll(exclude);
lastHopExclude = moreExclude;
} else {
lastHopExclude = exclude;
}
} else {
lastHopExclude = exclude;
}
} else {
lastHopExclude = exclude;
}
if (hiddenInbound) {
// IB closest hop
if (log.shouldInfo())
log.info("CPS SANFP closest IB exclude " + lastHopExclude.size());
// SANFP adds all not-connected to exclude, so make a copy
Set<Hash> SANFPExclude = new HashSet<Hash>(lastHopExclude);
ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches);
if (matches.isEmpty()) {
if (log.shouldInfo())
log.info("CPS SFP closest IB exclude " + lastHopExclude.size());
// ANFP does not fall back to non-connected
ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0);
}
} else if (hiddenOutbound) {
// OBEP
// check for hidden and outbound, and the paired (inbound) tunnel is zero-hop
// if so, we need the OBEP to be connected to us, so we get the build reply back
// This should be rare except at startup
TunnelManagerFacade tmf = ctx.tunnelManager();
TunnelPool tp = tmf.getInboundPool(settings.getDestination());
boolean pickFurthest;
if (tp != null) {
pickFurthest = true;
TunnelPoolSettings tps = tp.getSettings();
int len = tps.getLength();
if (len <= 0 || tps.getLengthOverride() == 0 || len + tps.getLengthVariance() <= 0) {
// leave it true
} else {
List<TunnelInfo> tunnels = tp.listTunnels();
if (!tunnels.isEmpty()) {
for (TunnelInfo ti : tp.listTunnels()) {
if (ti.getLength() > 1) {
pickFurthest = false;
break;
}
}
} else {
// no tunnels in the paired tunnel pool
// BuildRequester will be using exploratory
tp = tmf.getInboundExploratoryPool();
tps = tp.getSettings();
len = tps.getLength();
if (len <= 0 || tps.getLengthOverride() == 0 || len + tps.getLengthVariance() <= 0) {
// leave it true
} else {
tunnels = tp.listTunnels();
if (!tunnels.isEmpty()) {
for (TunnelInfo ti : tp.listTunnels()) {
if (ti.getLength() > 1) {
pickFurthest = false;
break;
}
}
}
}
}
}
} else {
// shouldn't happen
pickFurthest = false;
}
if (pickFurthest) {
if (log.shouldInfo())
log.info("CPS SANFP OBEP exclude " + lastHopExclude.size());
// SANFP adds all not-connected to exclude, so make a copy
Set<Hash> SANFPExclude = new HashSet<Hash>(lastHopExclude);
ctx.profileOrganizer().selectActiveNotFailingPeers(1, SANFPExclude, matches);
if (matches.isEmpty()) {
// ANFP does not fall back to non-connected
if (log.shouldInfo())
log.info("CPS SFP OBEP exclude " + lastHopExclude.size());
ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0);
}
} else {
ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0);
}
} else {
// TODO exclude IPv6-only at OBEP? Caught in checkTunnel() below
ctx.profileOrganizer().selectFastPeers(1, lastHopExclude, matches, randomKey, length == 2 ? SLICE_0_1 : SLICE_0);
}
matches.remove(ctx.routerHash());
exclude.addAll(matches);
rv.addAll(matches);
matches.clear();
if (length > 2) {
// middle hop(s)
// group 2 or 3
ctx.profileOrganizer().selectFastPeers(length - 2, exclude, matches, randomKey, SLICE_2_3);
matches.remove(ctx.routerHash());
if (matches.size() > 1) {
// order the middle peers for tunnels >= 4 hops
List<Hash> ordered = new ArrayList<Hash>(matches);
orderPeers(ordered, randomKey);
rv.addAll(ordered);
} else {
rv.addAll(matches);
}
exclude.addAll(matches);
matches.clear();
}
// group 2 or 3 if two hops, otherwise group 1
if (!isInbound) {
// closest-hop restrictions
if (checkClosestHop) {
Set<Hash> moreExclude = getClosestHopExclude(true);
if (moreExclude != null)
exclude.addAll(moreExclude);
}
}
// TODO exclude IPv6-only at IBGW? Caught in checkTunnel() below
ctx.profileOrganizer().selectFastPeers(1, exclude, matches, randomKey, length == 2 ? SLICE_2_3 : SLICE_1);
matches.remove(ctx.routerHash());
rv.addAll(matches);
}
} else {
rv = new ArrayList<Hash>(1);
}
// log.debug("EPS result: " + DataHelper.toString(rv));
if (isInbound)
rv.add(0, ctx.routerHash());
else
rv.add(ctx.routerHash());
if (rv.size() > 1) {
if (!checkTunnel(isInbound, rv))
rv = null;
}
return rv;
}
use of net.i2p.router.TunnelManagerFacade in project i2p.i2p by i2p.
the class IterativeSearchJob method sendQuery.
/**
* Send a DLM to the peer
*/
private void sendQuery(Hash peer) {
TunnelManagerFacade tm = getContext().tunnelManager();
RouterInfo ri = getContext().netDb().lookupRouterInfoLocally(peer);
if (ri != null) {
// Now that most of the netdb is Ed RIs and EC LSs, don't even bother
// querying old floodfills that don't know about those sig types.
// This is also more recent than the version that supports encrypted replies,
// so we won't request unencrypted replies anymore either.
String v = ri.getVersion();
String since = MIN_QUERY_VERSION;
if (VersionComparator.comp(v, since) < 0) {
failed(peer, false);
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": not sending query to old version " + v + ": " + peer);
return;
}
}
TunnelInfo outTunnel;
TunnelInfo replyTunnel;
boolean isClientReplyTunnel;
boolean isDirect;
if (_fromLocalDest != null) {
outTunnel = tm.selectOutboundTunnel(_fromLocalDest, peer);
if (outTunnel == null)
outTunnel = tm.selectOutboundExploratoryTunnel(peer);
replyTunnel = tm.selectInboundTunnel(_fromLocalDest, peer);
isClientReplyTunnel = replyTunnel != null;
if (!isClientReplyTunnel)
replyTunnel = tm.selectInboundExploratoryTunnel(peer);
isDirect = false;
} else if ((!_isLease) && ri != null && getContext().commSystem().isEstablished(peer)) {
// If it's a RI lookup, not from a client, and we're already connected, just ask directly
// This also saves the ElG encryption for us and the decryption for the ff
// There's no anonymity reason to use an expl. tunnel... the main reason
// is to limit connections to the ffs. But if we're already connected,
// do it the fast and easy way.
outTunnel = null;
replyTunnel = null;
isClientReplyTunnel = false;
isDirect = true;
getContext().statManager().addRateData("netDb.RILookupDirect", 1);
} else {
outTunnel = tm.selectOutboundExploratoryTunnel(peer);
replyTunnel = tm.selectInboundExploratoryTunnel(peer);
isClientReplyTunnel = false;
isDirect = false;
getContext().statManager().addRateData("netDb.RILookupDirect", 0);
}
if ((!isDirect) && (replyTunnel == null || outTunnel == null)) {
failed();
return;
}
// not being able to send to the floodfill, if we don't have an older netdb entry.
if (outTunnel != null && outTunnel.getLength() <= 1) {
if (peer.equals(_key)) {
failed(peer, false);
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": not doing zero-hop self-lookup of " + peer);
return;
}
if (_facade.lookupLocallyWithoutValidation(peer) == null) {
failed(peer, false);
if (_log.shouldLog(Log.WARN))
_log.warn(getJobId() + ": not doing zero-hop lookup to unknown " + peer);
return;
}
}
DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true);
if (isDirect) {
dlm.setFrom(getContext().routerHash());
} else {
dlm.setFrom(replyTunnel.getPeer(0));
dlm.setReplyTunnel(replyTunnel.getReceiveTunnelId(0));
}
dlm.setMessageExpiration(getContext().clock().now() + SINGLE_SEARCH_MSG_TIME);
dlm.setSearchKey(_key);
dlm.setSearchType(_isLease ? DatabaseLookupMessage.Type.LS : DatabaseLookupMessage.Type.RI);
if (_log.shouldLog(Log.INFO)) {
int tries;
synchronized (this) {
tries = _unheardFrom.size() + _failedPeers.size();
}
_log.info(getJobId() + ": ISJ try " + tries + " for " + (_isLease ? "LS " : "RI ") + _key + " to " + peer + " direct? " + isDirect + " reply via client tunnel? " + isClientReplyTunnel);
}
long now = getContext().clock().now();
_sentTime.put(peer, Long.valueOf(now));
I2NPMessage outMsg = null;
if (isDirect) {
// never wrap
} else if (_isLease || (getContext().getProperty(PROP_ENCRYPT_RI, DEFAULT_ENCRYPT_RI) && getContext().jobQueue().getMaxLag() < 300)) {
// if we have the ff RI, garlic encrypt it
if (ri != null) {
// if (DatabaseLookupMessage.supportsEncryptedReplies(ri)) {
if (true) {
MessageWrapper.OneTimeSession sess;
if (isClientReplyTunnel)
sess = MessageWrapper.generateSession(getContext(), _fromLocalDest);
else
sess = MessageWrapper.generateSession(getContext());
if (sess != null) {
if (_log.shouldLog(Log.INFO))
_log.info(getJobId() + ": Requesting encrypted reply from " + peer + ' ' + sess.key + ' ' + sess.tag);
dlm.setReplySession(sess.key, sess.tag);
}
// else client went away, but send it anyway
}
outMsg = MessageWrapper.wrap(getContext(), dlm, ri);
// a response may have come in.
if (_dead) {
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": aborting send, finished while wrapping msg to " + peer);
return;
}
if (_log.shouldLog(Log.DEBUG))
_log.debug(getJobId() + ": Encrypted DLM for " + _key + " to " + peer);
}
}
if (outMsg == null)
outMsg = dlm;
if (isDirect) {
OutNetMessage m = new OutNetMessage(getContext(), outMsg, outMsg.getMessageExpiration(), OutNetMessage.PRIORITY_MY_NETDB_LOOKUP, ri);
// Should always succeed, we are connected already
// m.setOnFailedReplyJob(onFail);
// m.setOnFailedSendJob(onFail);
// m.setOnReplyJob(onReply);
// m.setReplySelector(selector);
// getContext().messageRegistry().registerPending(m);
getContext().commSystem().processMessage(m);
} else {
getContext().tunnelDispatcher().dispatchOutbound(outMsg, outTunnel.getSendTunnelId(0), peer);
}
// The timeout job is always run (never cancelled)
// Note that the timeout is much shorter than the message expiration (see above)
Job j = new IterativeTimeoutJob(getContext(), peer, this);
long expire = Math.min(_expiration, now + _singleSearchTime);
j.getTiming().setStartAfter(expire);
getContext().jobQueue().addJob(j);
}
Aggregations