Search in sources :

Example 1 with TunnelManagerFacade

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");
}
Also used : ArrayList(java.util.ArrayList) TunnelManagerFacade(net.i2p.router.TunnelManagerFacade)

Example 2 with TunnelManagerFacade

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;
}
Also used : OutNetMessage(net.i2p.router.OutNetMessage) Log(net.i2p.util.Log) RouterInfo(net.i2p.data.router.RouterInfo) VariableTunnelBuildMessage(net.i2p.data.i2np.VariableTunnelBuildMessage) TunnelBuildMessage(net.i2p.data.i2np.TunnelBuildMessage) TunnelInfo(net.i2p.router.TunnelInfo) Hash(net.i2p.data.Hash) TunnelManagerFacade(net.i2p.router.TunnelManagerFacade)

Example 3 with TunnelManagerFacade

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;
}
Also used : ArrayList(java.util.ArrayList) TunnelPoolSettings(net.i2p.router.TunnelPoolSettings) TunnelInfo(net.i2p.router.TunnelInfo) Hash(net.i2p.data.Hash) HashSet(java.util.HashSet) TunnelManagerFacade(net.i2p.router.TunnelManagerFacade)

Example 4 with TunnelManagerFacade

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;
}
Also used : HashSet(java.util.HashSet) Set(java.util.Set) ArrayList(java.util.ArrayList) TunnelInfo(net.i2p.router.TunnelInfo) Hash(net.i2p.data.Hash) TunnelManagerFacade(net.i2p.router.TunnelManagerFacade) TunnelPoolSettings(net.i2p.router.TunnelPoolSettings) List(java.util.List) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet)

Example 5 with TunnelManagerFacade

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);
}
Also used : RouterInfo(net.i2p.data.router.RouterInfo) TunnelInfo(net.i2p.router.TunnelInfo) TunnelManagerFacade(net.i2p.router.TunnelManagerFacade) DatabaseLookupMessage(net.i2p.data.i2np.DatabaseLookupMessage) OutNetMessage(net.i2p.router.OutNetMessage) I2NPMessage(net.i2p.data.i2np.I2NPMessage) ReplyJob(net.i2p.router.ReplyJob) Job(net.i2p.router.Job)

Aggregations

TunnelManagerFacade (net.i2p.router.TunnelManagerFacade)5 TunnelInfo (net.i2p.router.TunnelInfo)4 ArrayList (java.util.ArrayList)3 Hash (net.i2p.data.Hash)3 HashSet (java.util.HashSet)2 RouterInfo (net.i2p.data.router.RouterInfo)2 OutNetMessage (net.i2p.router.OutNetMessage)2 TunnelPoolSettings (net.i2p.router.TunnelPoolSettings)2 List (java.util.List)1 Set (java.util.Set)1 DatabaseLookupMessage (net.i2p.data.i2np.DatabaseLookupMessage)1 I2NPMessage (net.i2p.data.i2np.I2NPMessage)1 TunnelBuildMessage (net.i2p.data.i2np.TunnelBuildMessage)1 VariableTunnelBuildMessage (net.i2p.data.i2np.VariableTunnelBuildMessage)1 Job (net.i2p.router.Job)1 ReplyJob (net.i2p.router.ReplyJob)1 Log (net.i2p.util.Log)1