Search in sources :

Example 26 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class StoreJob method sendStoreThroughGarlic.

/**
 * This is misnamed, it means sending it out through an exploratory tunnel,
 * with the reply to come back through an exploratory tunnel.
 * There is no garlic encryption added.
 */
private void sendStoreThroughGarlic(DatabaseStoreMessage msg, RouterInfo peer, long expiration) {
    long token = 1 + getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE);
    Hash to = peer.getIdentity().getHash();
    TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundExploratoryTunnel(to);
    if (replyTunnel == null) {
        _log.warn("No reply inbound tunnels available!");
        return;
    }
    TunnelId replyTunnelId = replyTunnel.getReceiveTunnelId(0);
    msg.setReplyToken(token);
    msg.setReplyTunnel(replyTunnelId);
    msg.setReplyGateway(replyTunnel.getPeer(0));
    if (_log.shouldLog(Log.DEBUG))
        _log.debug(getJobId() + ": send store thru expl. tunnel to " + peer.getIdentity().getHash() + "  w/ token expected " + token);
    _state.addPending(to);
    TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(to);
    if (outTunnel != null) {
        // if (_log.shouldLog(Log.DEBUG))
        // _log.debug(getJobId() + ": Sending tunnel message out " + outTunnelId + " to "
        // + peer.getIdentity().getHash().toBase64());
        // TunnelId targetTunnelId = null; // not needed
        // Job onSend = null; // not wanted
        SendSuccessJob onReply = new SendSuccessJob(getContext(), peer, outTunnel, msg.getMessageSize());
        FailedJob onFail = new FailedJob(getContext(), peer, getContext().clock().now());
        StoreMessageSelector selector = new StoreMessageSelector(getContext(), getJobId(), peer, token, expiration);
        if (_log.shouldLog(Log.DEBUG))
            _log.debug("sending store to " + peer.getIdentity().getHash() + " through " + outTunnel + ": " + msg);
        getContext().messageRegistry().registerPending(selector, onReply, onFail);
        getContext().tunnelDispatcher().dispatchOutbound(msg, outTunnel.getSendTunnelId(0), null, to);
    } else {
        if (_log.shouldLog(Log.WARN))
            _log.warn("No outbound tunnels to send a dbStore out!");
        fail();
    }
}
Also used : TunnelInfo(net.i2p.router.TunnelInfo) Hash(net.i2p.data.Hash) TunnelId(net.i2p.data.TunnelId)

Example 27 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class OutboundClientMessageOneShotJob method selectOutboundTunnel.

/**
 *  Choose our outbound tunnel to send the message through.
 *  Sets _wantACK if it's new or changed.
 *  @return the tunnel or null on failure
 */
private TunnelInfo selectOutboundTunnel(Destination to) {
    TunnelInfo tunnel;
    synchronized (_cache.tunnelCache) {
        /**
         * If old tunnel is valid and no longer backlogged, use it.
         * This prevents an active anonymity attack, where a peer could tell
         * if you were the originator by backlogging the tunnel, then removing the
         * backlog and seeing if traffic came back or not.
         */
        tunnel = _cache.backloggedTunnelCache.get(_hashPair);
        if (tunnel != null) {
            if (getContext().tunnelManager().isValidTunnel(_from.calculateHash(), tunnel)) {
                if (!getContext().commSystem().isBacklogged(tunnel.getPeer(1))) {
                    if (_log.shouldLog(Log.WARN))
                        _log.warn("Switching back to tunnel " + tunnel + " for " + _toString);
                    _cache.backloggedTunnelCache.remove(_hashPair);
                    _cache.tunnelCache.put(_hashPair, tunnel);
                    _wantACK = true;
                    return tunnel;
                }
            // else still backlogged
            } else
                // no longer valid
                _cache.backloggedTunnelCache.remove(_hashPair);
        }
        // Use the same tunnel unless backlogged
        tunnel = _cache.tunnelCache.get(_hashPair);
        if (tunnel != null) {
            if (getContext().tunnelManager().isValidTunnel(_from.calculateHash(), tunnel)) {
                if (tunnel.getLength() <= 1 || !getContext().commSystem().isBacklogged(tunnel.getPeer(1)))
                    return tunnel;
                // backlogged
                if (_log.shouldLog(Log.WARN))
                    _log.warn("Switching from backlogged " + tunnel + " for " + _toString);
                _cache.backloggedTunnelCache.put(_hashPair, tunnel);
            }
            // else no longer valid
            _cache.tunnelCache.remove(_hashPair);
        }
        // Pick a new tunnel
        tunnel = selectOutboundTunnel();
        if (tunnel != null)
            _cache.tunnelCache.put(_hashPair, tunnel);
        _wantACK = true;
    }
    return tunnel;
}
Also used : TunnelInfo(net.i2p.router.TunnelInfo)

Example 28 with TunnelInfo

use of net.i2p.router.TunnelInfo 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)

Example 29 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class FloodOnlySearchJob method runJob.

@Override
public void runJob() {
    // pick some floodfill peers and send out the searches
    // old
    // List<Hash> floodfillPeers = _facade.getFloodfillPeers();
    // new
    List<Hash> floodfillPeers;
    KBucketSet<Hash> ks = _facade.getKBuckets();
    if (ks != null) {
        Hash rkey = getContext().routingKeyGenerator().getRoutingKey(_key);
        // Ideally we would add the key to an exclude list, so we don't try to query a ff peer for itself,
        // but we're passing the rkey not the key, so we do it below instead in certain cases.
        floodfillPeers = ((FloodfillPeerSelector) _facade.getPeerSelector()).selectFloodfillParticipants(rkey, MIN_FOR_NO_DSRM, ks);
    } else {
        floodfillPeers = Collections.emptyList();
    }
    // If we dont know enough floodfills,
    // or the global network routing key just changed (which is set at statrtup,
    // so this includes the first few minutes of uptime)
    _shouldProcessDSRM = floodfillPeers.size() < MIN_FOR_NO_DSRM || getContext().routingKeyGenerator().getLastChanged() > getContext().clock().now() - 60 * 60 * 1000;
    if (floodfillPeers.isEmpty()) {
        // so this situation should be temporary
        if (_log.shouldLog(Log.WARN))
            _log.warn("Running netDb searches against the floodfill peers, but we don't know any");
        floodfillPeers = new ArrayList<Hash>(_facade.getAllRouters());
        if (floodfillPeers.isEmpty()) {
            if (_log.shouldLog(Log.ERROR))
                _log.error("We don't know any peers at all");
            failed();
            return;
        }
        Collections.shuffle(floodfillPeers, getContext().random());
    }
    // This OutNetMessage is never used or sent (setMessage() is never called), it's only
    // so we can register a reply selector.
    _out = getContext().messageRegistry().registerPending(_replySelector, _onReply, _onTimeout);
    /**
     ******
     *        // We need to randomize our ff selection, else we stay with the same ones since
     *        // getFloodfillPeers() is sorted by closest distance. Always using the same
     *        // ones didn't help reliability.
     *        // Also, query the unheard-from, unprofiled, failing, unreachable and banlisted ones last.
     *        // We should hear from floodfills pretty frequently so set a 30m time limit.
     *        // If unprofiled we haven't talked to them in a long time.
     *        // We aren't contacting the peer directly, so banlist doesn't strictly matter,
     *        // but it's a bad sign, and we often banlist a peer before we fail it...
     *        if (floodfillPeers.size() > CONCURRENT_SEARCHES) {
     *            Collections.shuffle(floodfillPeers, getContext().random());
     *            List ffp = new ArrayList(floodfillPeers.size());
     *            int failcount = 0;
     *            long before = getContext().clock().now() - 30*60*1000;
     *            for (int i = 0; i < floodfillPeers.size(); i++) {
     *                 Hash peer = (Hash)floodfillPeers.get(i);
     *                 PeerProfile profile = getContext().profileOrganizer().getProfile(peer);
     *                 if (profile == null || profile.getLastHeardFrom() < before ||
     *                     profile.getIsFailing() || getContext().banlist().isBanlisted(peer) ||
     *                     getContext().commSystem().wasUnreachable(peer)) {
     *                     failcount++;
     *                     ffp.add(peer);
     *                 } else
     *                     ffp.add(0, peer);
     *            }
     *            // This will help us recover if the router just started and all the floodfills
     *            // have changed since the last time we were running
     *            if (floodfillPeers.size() - failcount <= 2)
     *                _shouldProcessDSRM = true;
     *            if (_log.shouldLog(Log.INFO) && failcount > 0)
     *                _log.info(getJobId() + ": " + failcount + " of " + floodfillPeers.size() + " floodfills are not heard from, unprofiled, failing, unreachable or banlisted");
     *            floodfillPeers = ffp;
     *        } else {
     *            _shouldProcessDSRM = true;
     *        }
     *******
     */
    // keep a separate count since _lookupsRemaining could be decremented elsewhere
    int count = 0;
    for (int i = 0; _lookupsRemaining.get() < CONCURRENT_SEARCHES && i < floodfillPeers.size(); i++) {
        Hash peer = floodfillPeers.get(i);
        if (peer.equals(getContext().routerHash()))
            continue;
        DatabaseLookupMessage dlm = new DatabaseLookupMessage(getContext(), true);
        TunnelInfo replyTunnel = getContext().tunnelManager().selectInboundTunnel();
        TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundTunnel();
        if ((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.getLength() <= 1 && peer.equals(_key) && floodfillPeers.size() > 1)
            continue;
        synchronized (_unheardFrom) {
            _unheardFrom.add(peer);
        }
        dlm.setFrom(replyTunnel.getPeer(0));
        dlm.setMessageExpiration(getContext().clock().now() + SINGLE_SEARCH_MSG_TIME);
        dlm.setReplyTunnel(replyTunnel.getReceiveTunnelId(0));
        dlm.setSearchKey(_key);
        if (_log.shouldLog(Log.INFO))
            _log.info(getJobId() + ": Floodfill search for " + _key + " to " + peer);
        getContext().tunnelDispatcher().dispatchOutbound(dlm, outTunnel.getSendTunnelId(0), peer);
        count++;
        _lookupsRemaining.incrementAndGet();
    }
    if (count <= 0) {
        if (_log.shouldLog(Log.INFO))
            _log.info(getJobId() + ": Floodfill search for " + _key + " had no peers to send to");
        // no floodfill peers, fail
        failed();
    }
}
Also used : DatabaseLookupMessage(net.i2p.data.i2np.DatabaseLookupMessage) TunnelInfo(net.i2p.router.TunnelInfo) Hash(net.i2p.data.Hash)

Example 30 with TunnelInfo

use of net.i2p.router.TunnelInfo in project i2p.i2p by i2p.

the class OutboundCache method cleanTunnelCache.

/**
 * Clean out old tunnels
 * Caller must synchronize on tc.
 */
private static void cleanTunnelCache(RouterContext ctx, Map<HashPair, TunnelInfo> tc) {
    for (Iterator<Map.Entry<HashPair, TunnelInfo>> iter = tc.entrySet().iterator(); iter.hasNext(); ) {
        Map.Entry<HashPair, TunnelInfo> entry = iter.next();
        HashPair k = entry.getKey();
        TunnelInfo tunnel = entry.getValue();
        // This is a little sneaky, but get the _from back out of the "opaque" hash key
        if (!ctx.tunnelManager().isValidTunnel(k.sh, tunnel))
            iter.remove();
    }
}
Also used : TunnelInfo(net.i2p.router.TunnelInfo) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap)

Aggregations

TunnelInfo (net.i2p.router.TunnelInfo)30 Hash (net.i2p.data.Hash)15 TunnelId (net.i2p.data.TunnelId)8 RouterInfo (net.i2p.data.router.RouterInfo)6 ArrayList (java.util.ArrayList)4 DatabaseLookupMessage (net.i2p.data.i2np.DatabaseLookupMessage)4 DatabaseStoreMessage (net.i2p.data.i2np.DatabaseStoreMessage)4 I2NPMessage (net.i2p.data.i2np.I2NPMessage)4 TunnelManagerFacade (net.i2p.router.TunnelManagerFacade)4 Job (net.i2p.router.Job)3 TunnelPoolSettings (net.i2p.router.TunnelPoolSettings)3 HashSet (java.util.HashSet)2 OutNetMessage (net.i2p.router.OutNetMessage)2 ReplyJob (net.i2p.router.ReplyJob)2 Date (java.util.Date)1 HashMap (java.util.HashMap)1 List (java.util.List)1 Map (java.util.Map)1 Set (java.util.Set)1 TreeSet (java.util.TreeSet)1