public long receiveEncrypted(byte encrypted[]) { TunnelDataMessage msg = new TunnelDataMessage(_context); msg.setData(encrypted); msg.setTunnelId(_config.getConfig(0).getSendTunnel()); if (_log.shouldLog(Log.DEBUG)) _log.debug("received encrypted, sending out " + _config + ": " + msg); RouterInfo ri = _nextHopCache; if (ri == null) ri = _context.netDb().lookupRouterInfoLocally(_config.getPeer(1)); if (ri != null) { _nextHopCache = ri; send(msg, ri); return msg.getUniqueId(); } else { // It should be rare to forget the router info for a peer in our own tunnel. if (_log.shouldLog(Log.WARN)) _log.warn("lookup of " + _config.getPeer(1) + " required for " + msg); _context .netDb() .lookupRouterInfo( _config.getPeer(1), new SendJob(_context, msg), new FailedJob(_context), MAX_LOOKUP_TIME); return -1; } }
public OutboundReceiver(RouterContext ctx, TunnelCreatorConfig cfg) { _context = ctx; _log = ctx.logManager().getLog(OutboundReceiver.class); _config = cfg; _nextHopCache = _context.netDb().lookupRouterInfoLocally(_config.getPeer(1)); // all createRateStat() in TunnelDispatcher }
/** Die a horrible death. Cannot be restarted. */ public synchronized void stopRunning() { if (_dead) return; if (_context.router().isAlive() && _log.shouldLog(Log.WARN)) _log.warn( "Stop the I2CP connection! current leaseSet: " + _currentLeaseSet, new Exception("Stop client connection")); _dead = true; // we need these keys to unpublish the leaseSet if (_reader != null) _reader.stopReading(); if (_writer != null) _writer.stopWriting(); if (_socket != null) try { _socket.close(); } catch (IOException ioe) { } _messages.clear(); _acceptedPending.clear(); if (_sessionKeyManager != null) _sessionKeyManager.shutdown(); _manager.unregisterConnection(this); if (_currentLeaseSet != null) _context.netDb().unpublish(_currentLeaseSet); _leaseRequest = null; synchronized (_alreadyProcessed) { _alreadyProcessed.clear(); } // _config = null; // _manager = null; }
/** * 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; }
/** @since 0.7.12 */ 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; }
/** * 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; }