package net.i2p.router.tunnel; import net.i2p.data.Hash; import net.i2p.data.TunnelId; import net.i2p.data.i2np.I2NPMessage; import net.i2p.data.i2np.TunnelDataMessage; import net.i2p.router.OutNetMessage; import net.i2p.router.RouterContext; import net.i2p.util.Log; /** * We are the end of an outbound tunnel that we did not create. Gather fragments * and honor the instructions as received. * */ class OutboundTunnelEndpoint { private final RouterContext _context; private final Log _log; private final HopConfig _config; private final HopProcessor _processor; private final FragmentHandler _handler; private final OutboundMessageDistributor _outDistributor; public OutboundTunnelEndpoint(RouterContext ctx, HopConfig config, HopProcessor processor) { _context = ctx; _log = ctx.logManager().getLog(OutboundTunnelEndpoint.class); _config = config; _processor = processor; _handler = new RouterFragmentHandler(ctx, new DefragmentedHandler()); _outDistributor = new OutboundMessageDistributor(ctx, OutNetMessage.PRIORITY_PARTICIPATING); } public void dispatch(TunnelDataMessage msg, Hash recvFrom) { _config.incrementProcessedMessages(); boolean ok = _processor.process(msg.getData(), 0, msg.getData().length, recvFrom); if (!ok) { // invalid IV // If we pass it on to the handler, it will fail // If we don't, the data buf won't get released from the cache... that's ok if (_log.shouldLog(Log.WARN)) _log.warn("Invalid IV, dropping at OBEP " + _config); _context.statManager().addRateData("tunnel.corruptMessage", 1, 1); return; } _handler.receiveTunnelMessage(msg.getData(), 0, msg.getData().length); } private class DefragmentedHandler implements FragmentHandler.DefragmentedReceiver { public void receiveComplete(I2NPMessage msg, Hash toRouter, TunnelId toTunnel) { if (toRouter == null) { // Delivery type LOCAL is not supported at the OBEP // We don't have any use for it yet. // Don't send to OutboundMessageDistributor.distribute() which will NPE or fail if (_log.shouldLog(Log.WARN)) _log.warn("Dropping msg at OBEP with unsupported delivery instruction type LOCAL"); return; } if (_log.shouldLog(Log.DEBUG)) _log.debug("outbound tunnel " + _config + " received a full message: " + msg + " to be forwarded on to " + toRouter.toBase64().substring(0,4) + (toTunnel != null ? ":" + toTunnel.getTunnelId() : "")); int size = msg.getMessageSize(); // don't drop it if we are the target boolean toUs = _context.routerHash().equals(toRouter); if ((!toUs) && _context.tunnelDispatcher().shouldDropParticipatingMessage(TunnelDispatcher.Location.OBEP, msg.getType(), size)) return; // this overstates the stat somewhat, but ok for now //int kb = (size + 1023) / 1024; //for (int i = 0; i < kb; i++) // _config.incrementSentMessages(); if (!toUs) _context.bandwidthLimiter().sentParticipatingMessage(size); _outDistributor.distribute(msg, toRouter, toTunnel); } } /** @since 0.9.8 */ @Override public String toString() { return "OBEP " + _config.getReceiveTunnel(); } }