Exemplo n.º 1
0
  /** new style requests need to fill in the tunnel IDs before hand */
  private static void prepare(RouterContext ctx, PooledTunnelCreatorConfig cfg) {
    int len = cfg.getLength();
    boolean isIB = cfg.isInbound();
    for (int i = 0; i < len; i++) {
      if ((!isIB) && (i == 0)) {
        // outbound gateway (us) doesn't receive on a tunnel id
        if (len <= 1) { // zero hop, pretend to have a send id
          long id = ctx.tunnelDispatcher().getNewOBGWID();
          cfg.getConfig(i).setSendTunnelId(DataHelper.toLong(4, id));
        }
      } else {
        long id;
        if (isIB && len == 1) id = ctx.tunnelDispatcher().getNewIBZeroHopID();
        else if (isIB && i == len - 1) id = ctx.tunnelDispatcher().getNewIBEPID();
        else id = 1 + ctx.random().nextLong(TunnelId.MAX_ID_VALUE - 1);
        cfg.getConfig(i).setReceiveTunnelId(DataHelper.toLong(4, id));
      }

      if (i > 0) cfg.getConfig(i - 1).setSendTunnelId(cfg.getConfig(i).getReceiveTunnelId());
      byte iv[] = new byte[16];
      ctx.random().nextBytes(iv);
      cfg.getConfig(i).setReplyIV(iv);
      cfg.getConfig(i).setReplyKey(ctx.keyGenerator().generateSessionKey());
    }
    // This is in BuildExecutor.buildTunnel() now
    // And it was overwritten by the one in createTunnelBuildMessage() anyway!
    // cfg.setReplyMessageId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE));
  }
Exemplo n.º 2
0
  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?
  }
Exemplo n.º 3
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 = 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;
  }
Exemplo n.º 4
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;
  }