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();
}
}
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;
}
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);
}
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();
}
}
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();
}
}
Aggregations