use of net.i2p.data.router.RouterInfo in project i2p.i2p by i2p.
the class BuildRequestor method createTunnelBuildMessage.
/**
* @since 0.7.12
*/
/**
**
*we can assume everybody supports variable now...
*keep this here for the next time we change the build protocol
* private static boolean supportsVariable(RouterContext ctx, Hash h) {
* RouterInfo ri = ctx.netDb().lookupRouterInfoLocally(h);
* if (ri == null)
* return false;
* String v = ri.getVersion();
* return VersionComparator.comp(v, MIN_VARIABLE_VERSION) >= 0;
* }
***
*/
/**
* If the tunnel is short enough, and everybody in the tunnel, and the
* OBEP or IBGW for the paired tunnel, all support the new variable-sized tunnel build message,
* then use that, otherwise the old 8-entry version.
* @return null on error
*/
private static TunnelBuildMessage createTunnelBuildMessage(RouterContext ctx, TunnelPool pool, PooledTunnelCreatorConfig cfg, TunnelInfo pairedTunnel, BuildExecutor exec) {
Log log = ctx.logManager().getLog(BuildRequestor.class);
long replyTunnel = 0;
Hash replyRouter;
boolean useVariable = SEND_VARIABLE && cfg.getLength() <= MEDIUM_RECORDS;
if (cfg.isInbound()) {
// replyTunnel = 0; // as above
replyRouter = ctx.routerHash();
/**
**
*we can assume everybody supports variable now...
*keep this here for the next time we change the build protocol
* if (useVariable) {
* // check the reply OBEP and all the tunnel peers except ourselves
* if (!supportsVariable(ctx, pairedTunnel.getPeer(pairedTunnel.getLength() - 1))) {
* useVariable = false;
* } else {
* for (int i = 0; i < cfg.getLength() - 1; i++) {
* if (!supportsVariable(ctx, cfg.getPeer(i))) {
* useVariable = false;
* break;
* }
* }
* }
* }
***
*/
} else {
replyTunnel = pairedTunnel.getReceiveTunnelId(0).getTunnelId();
replyRouter = pairedTunnel.getPeer(0);
/**
**
*we can assume everybody supports variable now
*keep this here for the next time we change the build protocol
* if (useVariable) {
* // check the reply IBGW and all the tunnel peers except ourselves
* if (!supportsVariable(ctx, replyRouter)) {
* useVariable = false;
* } else {
* for (int i = 1; i < cfg.getLength() - 1; i++) {
* if (!supportsVariable(ctx, cfg.getPeer(i))) {
* useVariable = false;
* break;
* }
* }
* }
* }
***
*/
}
// populate and encrypt the message
TunnelBuildMessage msg;
List<Integer> order;
if (useVariable) {
if (cfg.getLength() <= SHORT_RECORDS) {
msg = new VariableTunnelBuildMessage(ctx, SHORT_RECORDS);
order = new ArrayList<Integer>(SHORT_ORDER);
} else {
msg = new VariableTunnelBuildMessage(ctx, MEDIUM_RECORDS);
order = new ArrayList<Integer>(MEDIUM_ORDER);
}
} else {
msg = new TunnelBuildMessage(ctx);
order = new ArrayList<Integer>(ORDER);
}
// This is in BuildExecutor.buildTunnel() now
// long replyMessageId = ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE);
// cfg.setReplyMessageId(replyMessageId);
// randomized placement within the message
Collections.shuffle(order, ctx.random());
cfg.setReplyOrder(order);
if (log.shouldLog(Log.DEBUG))
log.debug("Build order: " + order + " for " + cfg);
for (int i = 0; i < msg.getRecordCount(); i++) {
int hop = order.get(i).intValue();
PublicKey key = null;
if (BuildMessageGenerator.isBlank(cfg, hop)) {
// erm, blank
} else {
Hash peer = cfg.getPeer(hop);
RouterInfo peerInfo = ctx.netDb().lookupRouterInfoLocally(peer);
if (peerInfo == null) {
if (log.shouldLog(Log.WARN))
log.warn("Peer selected for hop " + i + "/" + hop + " was not found locally: " + peer + " for " + cfg);
return null;
} else {
key = peerInfo.getIdentity().getPublicKey();
}
}
if (log.shouldLog(Log.DEBUG))
log.debug(cfg.getReplyMessageId() + ": record " + i + "/" + hop + " has key " + key);
BuildMessageGenerator.createRecord(i, hop, msg, cfg, replyRouter, replyTunnel, ctx, key);
}
BuildMessageGenerator.layeredEncrypt(ctx, msg, cfg, order);
return msg;
}
use of net.i2p.data.router.RouterInfo 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.data.router.RouterInfo in project i2p.i2p by i2p.
the class ConnectChecker method canConnect.
/**
* Can "from" connect to "to" based on published addresses?
*
* This is intended for tunnel candidates, where we already have
* the RI. Will not force RI lookups.
* Either from or to may be us.
*
* This is best effort, as we can't know for sure.
* Published addresses or introducers may have changed.
* Even if a can't connect to b, they may already be connected
* as b connected to a.
*
* @return true if we don't have either RI
* @since 0.9.34
*/
public boolean canConnect(Hash from, Hash to) {
Hash us = ctx.routerHash();
if (us == null)
return true;
boolean usf = from.equals(us);
if (usf && ctx.commSystem().isEstablished(to))
return true;
boolean ust = to.equals(us);
if (ust && ctx.commSystem().isEstablished(from))
return true;
RouterInfo rt = ctx.netDb().lookupRouterInfoLocally(to);
if (rt == null)
return true;
RouterInfo rf = ctx.netDb().lookupRouterInfoLocally(from);
if (rf == null)
return true;
int ct;
if (ust) {
// to us
ct = getInboundMask(rt);
} else {
Collection<RouterAddress> at = rt.getAddresses();
// assume nothing if hidden
if (at.isEmpty())
return false;
ct = getConnectMask(at);
}
int cf;
if (usf) {
// from us
cf = getOutboundMask(rf);
} else {
Collection<RouterAddress> a = rf.getAddresses();
if (a.isEmpty()) {
// assume IPv4 if hidden
cf = NTCP_V4 | SSU_V4;
} else {
cf = getConnectMask(a);
}
}
boolean rv = (ct & cf) != 0;
if (!rv && log.shouldWarn()) {
log.warn("Cannot connect: " + (usf ? "us" : from.toString()) + " with mask " + cf + "\nto " + (ust ? "us" : to.toString()) + " with mask " + ct);
}
return rv;
}
use of net.i2p.data.router.RouterInfo in project i2p.i2p by i2p.
the class FloodfillVerifyStoreJob method runJob.
/**
* Query a random floodfill for the leaseset or routerinfo
* that we just stored to a (hopefully different) floodfill peer.
*
* If it fails (after a timeout period), resend the data.
* If the queried data is older than what we stored, that counts as a fail.
*/
public void runJob() {
_target = pickTarget();
if (_target == null) {
_facade.verifyFinished(_key);
return;
}
boolean isInboundExploratory;
TunnelInfo replyTunnelInfo;
if (_isRouterInfo || getContext().keyRing().get(_key) != null) {
replyTunnelInfo = getContext().tunnelManager().selectInboundExploratoryTunnel(_target);
isInboundExploratory = true;
} else {
replyTunnelInfo = getContext().tunnelManager().selectInboundTunnel(_key, _target);
isInboundExploratory = false;
}
if (replyTunnelInfo == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No inbound tunnels to get a reply from!");
return;
}
DatabaseLookupMessage lookup = buildLookup(replyTunnelInfo);
// If we are verifying a leaseset, use the destination's own tunnels,
// to avoid association by the exploratory tunnel OBEP.
// Unless it is an encrypted leaseset.
TunnelInfo outTunnel;
if (_isRouterInfo || getContext().keyRing().get(_key) != null)
outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(_target);
else
outTunnel = getContext().tunnelManager().selectOutboundTunnel(_key, _target);
if (outTunnel == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No outbound tunnels to verify a store");
_facade.verifyFinished(_key);
return;
}
// garlic encrypt to hide contents from the OBEP
RouterInfo peer = _facade.lookupRouterInfoLocally(_target);
if (peer == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Fail finding target RI");
_facade.verifyFinished(_key);
return;
}
if (DatabaseLookupMessage.supportsEncryptedReplies(peer)) {
// register the session with the right SKM
MessageWrapper.OneTimeSession sess;
if (isInboundExploratory) {
sess = MessageWrapper.generateSession(getContext());
} else {
sess = MessageWrapper.generateSession(getContext(), _key);
if (sess == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No SKM to reply to");
_facade.verifyFinished(_key);
return;
}
}
if (_log.shouldLog(Log.INFO))
_log.info("Requesting encrypted reply from " + _target + ' ' + sess.key + ' ' + sess.tag);
lookup.setReplySession(sess.key, sess.tag);
}
Hash fromKey;
if (_isRouterInfo)
fromKey = null;
else
fromKey = _key;
_wrappedMessage = MessageWrapper.wrap(getContext(), lookup, fromKey, peer);
if (_wrappedMessage == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("Fail Garlic encrypting");
_facade.verifyFinished(_key);
return;
}
I2NPMessage sent = _wrappedMessage.getMessage();
if (_log.shouldLog(Log.INFO))
_log.info("Starting verify (stored " + _key + " to " + _sentTo + "), asking " + _target);
_sendTime = getContext().clock().now();
_expiration = _sendTime + VERIFY_TIMEOUT;
getContext().messageRegistry().registerPending(new VerifyReplySelector(), new VerifyReplyJob(getContext()), new VerifyTimeoutJob(getContext()));
getContext().tunnelDispatcher().dispatchOutbound(sent, outTunnel.getSendTunnelId(0), _target);
}
use of net.i2p.data.router.RouterInfo in project i2p.i2p by i2p.
the class HandleFloodfillDatabaseStoreMessageJob method sendAck.
private void sendAck(Hash storedKey) {
DeliveryStatusMessage msg = new DeliveryStatusMessage(getContext());
msg.setMessageId(_message.getReplyToken());
// Randomize for a little protection against clock-skew fingerprinting.
// But the "arrival" isn't used for anything, right?
// TODO just set to 0?
// TODO we have no session to garlic wrap this with, needs new message
msg.setArrival(getContext().clock().now() - getContext().random().nextInt(3 * 1000));
// may be null
TunnelId replyTunnel = _message.getReplyTunnel();
// A store of our own RI, only if we are not FF
DatabaseStoreMessage msg2;
if ((getContext().netDb().floodfillEnabled() && !getContext().router().gracefulShutdownInProgress()) || storedKey.equals(getContext().routerHash())) {
// don't send our RI if the store was our RI (from PeerTestJob)
msg2 = null;
} else {
// we aren't ff, send a go-away message
msg2 = new DatabaseStoreMessage(getContext());
RouterInfo me = getContext().router().getRouterInfo();
msg2.setEntry(me);
if (_log.shouldWarn())
_log.warn("Got a store w/ reply token, but we aren't ff: from: " + _from + " fromHash: " + _fromHash + " msg: " + _message, new Exception());
}
Hash toPeer = _message.getReplyGateway();
boolean toUs = getContext().routerHash().equals(toPeer);
// else through an exploratory tunnel.
if (toUs && replyTunnel != null) {
// if we are the gateway, act as if we received it
TunnelGatewayMessage tgm = new TunnelGatewayMessage(getContext());
tgm.setMessage(msg);
tgm.setTunnelId(replyTunnel);
tgm.setMessageExpiration(msg.getMessageExpiration());
getContext().tunnelDispatcher().dispatch(tgm);
if (msg2 != null) {
TunnelGatewayMessage tgm2 = new TunnelGatewayMessage(getContext());
tgm2.setMessage(msg2);
tgm2.setTunnelId(replyTunnel);
tgm2.setMessageExpiration(msg.getMessageExpiration());
getContext().tunnelDispatcher().dispatch(tgm2);
}
} else if (toUs || getContext().commSystem().isEstablished(toPeer)) {
Job send = new SendMessageDirectJob(getContext(), msg, toPeer, REPLY_TIMEOUT, MESSAGE_PRIORITY);
send.runJob();
if (msg2 != null) {
Job send2 = new SendMessageDirectJob(getContext(), msg2, toPeer, REPLY_TIMEOUT, MESSAGE_PRIORITY);
send2.runJob();
}
} else {
// pick tunnel with endpoint closest to toPeer
TunnelInfo outTunnel = getContext().tunnelManager().selectOutboundExploratoryTunnel(toPeer);
if (outTunnel == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No outbound tunnel could be found");
return;
}
getContext().tunnelDispatcher().dispatchOutbound(msg, outTunnel.getSendTunnelId(0), replyTunnel, toPeer);
if (msg2 != null)
getContext().tunnelDispatcher().dispatchOutbound(msg2, outTunnel.getSendTunnelId(0), replyTunnel, toPeer);
}
}
Aggregations