Search in sources :

Example 1 with TunnelBuildMessage

use of net.i2p.data.i2np.TunnelBuildMessage 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;
}
Also used : Log(net.i2p.util.Log) PublicKey(net.i2p.data.PublicKey) RouterInfo(net.i2p.data.router.RouterInfo) VariableTunnelBuildMessage(net.i2p.data.i2np.VariableTunnelBuildMessage) TunnelBuildMessage(net.i2p.data.i2np.TunnelBuildMessage) VariableTunnelBuildMessage(net.i2p.data.i2np.VariableTunnelBuildMessage) Hash(net.i2p.data.Hash)

Example 2 with TunnelBuildMessage

use of net.i2p.data.i2np.TunnelBuildMessage 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;
}
Also used : OutNetMessage(net.i2p.router.OutNetMessage) Log(net.i2p.util.Log) RouterInfo(net.i2p.data.router.RouterInfo) VariableTunnelBuildMessage(net.i2p.data.i2np.VariableTunnelBuildMessage) TunnelBuildMessage(net.i2p.data.i2np.TunnelBuildMessage) TunnelInfo(net.i2p.router.TunnelInfo) Hash(net.i2p.data.Hash) TunnelManagerFacade(net.i2p.router.TunnelManagerFacade)

Example 3 with TunnelBuildMessage

use of net.i2p.data.i2np.TunnelBuildMessage in project i2p.i2p by i2p.

the class BuildMessageTestStandalone method testBuildMessage.

public void testBuildMessage() {
    I2PAppContext ctx = I2PAppContext.getGlobalContext();
    Log log = ctx.logManager().getLog(getClass());
    List<Integer> order = pickOrder();
    TunnelCreatorConfig cfg = createConfig(ctx);
    _replyRouter = new Hash();
    byte[] h = new byte[Hash.HASH_LENGTH];
    Arrays.fill(h, (byte) 0xFF);
    _replyRouter.setData(h);
    _replyTunnel = 42;
    // populate and encrypt the message
    TunnelBuildMessage msg = new TunnelBuildMessage(ctx);
    for (int i = 0; i < order.size(); i++) {
        int hop = order.get(i).intValue();
        PublicKey key = null;
        if (hop < _pubKeys.length)
            key = _pubKeys[hop];
        BuildMessageGenerator.createRecord(i, hop, msg, cfg, _replyRouter, _replyTunnel, ctx, key);
    }
    BuildMessageGenerator.layeredEncrypt(ctx, msg, cfg, order);
    log.debug("\n================================================================" + "\nMessage fully encrypted" + "\n================================================================");
    // now msg is fully encrypted, so lets go through the hops, decrypting and replying
    // as necessary
    BuildMessageProcessor proc = new BuildMessageProcessor(ctx);
    for (int i = 0; i < cfg.getLength(); i++) {
        // this not only decrypts the current hop's record, but encrypts the other records
        // with the reply key
        BuildRequestRecord req = proc.decrypt(msg, _peers[i], _privKeys[i]);
        // If false, no records matched the _peers[i], or the decryption failed
        assertTrue("foo @ " + i, req != null);
        long ourId = req.readReceiveTunnelId();
        byte[] replyIV = req.readReplyIV();
        long nextId = req.readNextTunnelId();
        Hash nextPeer = req.readNextIdentity();
        boolean isInGW = req.readIsInboundGateway();
        boolean isOutEnd = req.readIsOutboundEndpoint();
        long time = req.readRequestTime();
        long now = (ctx.clock().now() / (60l * 60l * 1000l)) * (60 * 60 * 1000);
        int ourSlot = -1;
        EncryptedBuildRecord reply = BuildResponseRecord.create(ctx, 0, req.readReplyKey(), req.readReplyIV(), -1);
        for (int j = 0; j < TunnelBuildMessage.MAX_RECORD_COUNT; j++) {
            if (msg.getRecord(j) == null) {
                ourSlot = j;
                msg.setRecord(j, reply);
                break;
            }
        }
        log.debug("Read slot " + ourSlot + " containing hop " + i + " @ " + _peers[i].toBase64() + " receives on " + ourId + " w/ replyIV " + Base64.encode(replyIV) + " sending to " + nextId + " on " + nextPeer.toBase64() + " inGW? " + isInGW + " outEnd? " + isOutEnd + " time difference " + (now - time));
    }
    log.debug("\n================================================================" + "\nAll hops traversed and replies gathered" + "\n================================================================");
    // now all of the replies are populated, toss 'em into a reply message and handle it
    TunnelBuildReplyMessage reply = new TunnelBuildReplyMessage(ctx);
    for (int i = 0; i < TunnelBuildMessage.MAX_RECORD_COUNT; i++) reply.setRecord(i, msg.getRecord(i));
    int[] statuses = (new BuildReplyHandler(ctx)).decrypt(reply, cfg, order);
    if (statuses == null)
        throw new RuntimeException("bar");
    boolean allAgree = true;
    for (int i = 0; i < cfg.getLength(); i++) {
        Hash peer = cfg.getPeer(i);
        int record = order.get(i).intValue();
        if (statuses[record] != 0)
            allAgree = false;
    // else
    // penalize peer according to the rejection cause
    }
    log.debug("\n================================================================" + "\nAll peers agree? " + allAgree + "\n================================================================");
}
Also used : EncryptedBuildRecord(net.i2p.data.i2np.EncryptedBuildRecord) I2PAppContext(net.i2p.I2PAppContext) Log(net.i2p.util.Log) PublicKey(net.i2p.data.PublicKey) TunnelBuildMessage(net.i2p.data.i2np.TunnelBuildMessage) Hash(net.i2p.data.Hash) BuildRequestRecord(net.i2p.data.i2np.BuildRequestRecord) TunnelBuildReplyMessage(net.i2p.data.i2np.TunnelBuildReplyMessage)

Aggregations

Hash (net.i2p.data.Hash)3 TunnelBuildMessage (net.i2p.data.i2np.TunnelBuildMessage)3 Log (net.i2p.util.Log)3 PublicKey (net.i2p.data.PublicKey)2 VariableTunnelBuildMessage (net.i2p.data.i2np.VariableTunnelBuildMessage)2 RouterInfo (net.i2p.data.router.RouterInfo)2 I2PAppContext (net.i2p.I2PAppContext)1 BuildRequestRecord (net.i2p.data.i2np.BuildRequestRecord)1 EncryptedBuildRecord (net.i2p.data.i2np.EncryptedBuildRecord)1 TunnelBuildReplyMessage (net.i2p.data.i2np.TunnelBuildReplyMessage)1 OutNetMessage (net.i2p.router.OutNetMessage)1 TunnelInfo (net.i2p.router.TunnelInfo)1 TunnelManagerFacade (net.i2p.router.TunnelManagerFacade)1