private static void buildZeroHop( RouterContext ctx, TunnelPool pool, PooledTunnelCreatorConfig cfg, BuildExecutor exec) { Log log = ctx.logManager().getLog(BuildRequestor.class); if (log.shouldLog(Log.DEBUG)) log.debug("Build zero hop tunnel " + cfg); exec.buildComplete(cfg, pool); if (cfg.isInbound()) ctx.tunnelDispatcher().joinInbound(cfg); else ctx.tunnelDispatcher().joinOutbound(cfg); pool.addTunnel(cfg); exec.buildSuccessful(cfg); ExpireJob expireJob = new ExpireJob(ctx, cfg, pool); cfg.setExpireJob(expireJob); ctx.jobQueue().addJob(expireJob); // can it get much easier? }
/** * 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; } // cfg.setPairedTunnel(pairedTunnel); // 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); } // if (log.shouldLog(Log.DEBUG)) // log.debug("Tunnel build message " + msg.getUniqueId() + " created in " + createTime // + "ms and dispatched in " + (System.currentTimeMillis()-beforeDispatch)); return true; }