public void runJob() { _exec.buildComplete(_cfg, _pool); getContext().profileManager().tunnelTimedOut(_cfg.getPeer(1)); getContext().statManager().addRateData("tunnel.buildFailFirstHop", 1, 0); // static, no _log // System.err.println("Cant contact first hop for " + _cfg); }
/** * 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 = null; boolean useVariable = SEND_VARIABLE && cfg.getLength() <= MEDIUM_RECORDS; if (cfg.isInbound()) { // replyTunnel = 0; // as above replyRouter = ctx.routerHash(); 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); 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); Collections.shuffle(order, ctx.random()); // randomized placement within the message 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; }
/** * 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; }