Ejemplo n.º 1
0
  private Future<Channel> handshake() {
    final ScheduledFuture<?> timeoutFuture;
    if (handshakeTimeoutMillis > 0) {
      timeoutFuture =
          ctx.executor()
              .schedule(
                  new Runnable() {
                    @Override
                    public void run() {
                      if (handshakePromise.isDone()) {
                        return;
                      }
                      notifyHandshakeFailure(HANDSHAKE_TIMED_OUT);
                    }
                  },
                  handshakeTimeoutMillis,
                  TimeUnit.MILLISECONDS);
    } else {
      timeoutFuture = null;
    }

    handshakePromise.addListener(
        new GenericFutureListener<Future<Channel>>() {
          @Override
          public void operationComplete(Future<Channel> f) throws Exception {
            if (timeoutFuture != null) {
              timeoutFuture.cancel(false);
            }
          }
        });
    try {
      engine.beginHandshake();
      wrapNonAppData(ctx, false);
      ctx.flush();
    } catch (Exception e) {
      notifyHandshakeFailure(e);
    }
    return handshakePromise;
  }
Ejemplo n.º 2
0
  /** Unwraps inbound SSL records. */
  private boolean unwrap(ChannelHandlerContext ctx, ByteBuf packet, int offset, int length)
      throws SSLException {

    boolean decoded = false;
    boolean wrapLater = false;
    boolean notifyClosure = false;
    ByteBuf decodeOut = allocate(ctx, length);
    try {
      for (; ; ) {
        final SSLEngineResult result = unwrap(engine, packet, offset, length, decodeOut);
        final Status status = result.getStatus();
        final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
        final int produced = result.bytesProduced();
        final int consumed = result.bytesConsumed();

        // Update indexes for the next iteration
        offset += consumed;
        length -= consumed;

        switch (status) {
          case BUFFER_OVERFLOW:
            int readableBytes = decodeOut.readableBytes();
            if (readableBytes > 0) {
              decoded = true;
              ctx.fireChannelRead(decodeOut);
            } else {
              decodeOut.release();
            }
            // Allocate a new buffer which can hold all the rest data and loop again.
            // TODO: We may want to reconsider how we calculate the length here as we may
            // have more then one ssl message to decode.
            decodeOut =
                allocate(ctx, engine.getSession().getApplicationBufferSize() - readableBytes);
            continue;
          case CLOSED:
            // notify about the CLOSED state of the SSLEngine. See #137
            notifyClosure = true;
            break;
          default:
            break;
        }

        switch (handshakeStatus) {
          case NEED_UNWRAP:
            break;
          case NEED_WRAP:
            wrapNonAppData(ctx, true);
            break;
          case NEED_TASK:
            runDelegatedTasks();
            break;
          case FINISHED:
            setHandshakeSuccess();
            wrapLater = true;
            continue;
          case NOT_HANDSHAKING:
            if (setHandshakeSuccessIfStillHandshaking()) {
              wrapLater = true;
              continue;
            }
            if (flushedBeforeHandshake) {
              // We need to call wrap(...) in case there was a flush done before the handshake
              // completed.
              //
              // See https://github.com/netty/netty/pull/2437
              flushedBeforeHandshake = false;
              wrapLater = true;
            }

            break;
          default:
            throw new IllegalStateException("unknown handshake status: " + handshakeStatus);
        }

        if (status == Status.BUFFER_UNDERFLOW || consumed == 0 && produced == 0) {
          break;
        }
      }

      if (wrapLater) {
        wrap(ctx, true);
      }

      if (notifyClosure) {
        sslCloseFuture.trySuccess(ctx.channel());
      }
    } catch (SSLException e) {
      setHandshakeFailure(ctx, e);
      throw e;
    } finally {
      if (decodeOut.isReadable()) {
        decoded = true;

        ctx.fireChannelRead(decodeOut);
      } else {
        decodeOut.release();
      }
    }
    return decoded;
  }
Ejemplo n.º 3
0
  /**
   * Performs TLS (re)negotiation.
   *
   * @param newHandshakePromise if {@code null}, use the existing {@link #handshakePromise},
   *     assuming that the current negotiation has not been finished. Currently, {@code null} is
   *     expected only for the initial handshake.
   */
  private void handshake(final Promise<Channel> newHandshakePromise) {
    final Promise<Channel> p;
    if (newHandshakePromise != null) {
      final Promise<Channel> oldHandshakePromise = handshakePromise;
      if (!oldHandshakePromise.isDone()) {
        // There's no need to handshake because handshake is in progress already.
        // Merge the new promise into the old one.
        oldHandshakePromise.addListener(
            new FutureListener<Channel>() {
              @Override
              public void operationComplete(Future<Channel> future) throws Exception {
                if (future.isSuccess()) {
                  newHandshakePromise.setSuccess(future.getNow());
                } else {
                  newHandshakePromise.setFailure(future.cause());
                }
              }
            });
        return;
      }

      handshakePromise = p = newHandshakePromise;
    } else {
      // Forced to reuse the old handshake.
      p = handshakePromise;
      assert !p.isDone();
    }

    // Begin handshake.
    final ChannelHandlerContext ctx = this.ctx;
    try {
      engine.beginHandshake();
      wrapNonAppData(ctx, false);
      ctx.flush();
    } catch (Exception e) {
      notifyHandshakeFailure(e);
    }

    // Set timeout if necessary.
    final long handshakeTimeoutMillis = this.handshakeTimeoutMillis;
    if (handshakeTimeoutMillis <= 0 || p.isDone()) {
      return;
    }

    final ScheduledFuture<?> timeoutFuture =
        ctx.executor()
            .schedule(
                new Runnable() {
                  @Override
                  public void run() {
                    if (p.isDone()) {
                      return;
                    }
                    notifyHandshakeFailure(HANDSHAKE_TIMED_OUT);
                  }
                },
                handshakeTimeoutMillis,
                TimeUnit.MILLISECONDS);

    // Cancel the handshake timeout when handshake is finished.
    p.addListener(
        new FutureListener<Channel>() {
          @Override
          public void operationComplete(Future<Channel> f) throws Exception {
            timeoutFuture.cancel(false);
          }
        });
  }
Ejemplo n.º 4
0
  /** Unwraps a single SSL record. */
  private void unwrapSingle(
      ChannelHandlerContext ctx, ByteBuffer packet, int initialOutAppBufCapacity)
      throws SSLException {

    boolean wrapLater = false;
    try {
      for (; ; ) {
        if (decodeOut == null) {
          decodeOut = ctx.alloc().buffer(initialOutAppBufCapacity);
        }

        final SSLEngineResult result = unwrap(engine, packet, decodeOut);
        final Status status = result.getStatus();
        final HandshakeStatus handshakeStatus = result.getHandshakeStatus();
        final int produced = result.bytesProduced();
        final int consumed = result.bytesConsumed();

        if (status == Status.CLOSED) {
          // notify about the CLOSED state of the SSLEngine. See #137
          sslCloseFuture.trySuccess(ctx.channel());
          break;
        }

        switch (handshakeStatus) {
          case NEED_UNWRAP:
            break;
          case NEED_WRAP:
            wrapNonAppData(ctx, true);
            break;
          case NEED_TASK:
            runDelegatedTasks();
            break;
          case FINISHED:
            setHandshakeSuccess();
            wrapLater = true;
            continue;
          case NOT_HANDSHAKING:
            if (setHandshakeSuccessIfStillHandshaking()) {
              wrapLater = true;
              continue;
            }
            if (flushedBeforeHandshakeDone) {
              // We need to call wrap(...) in case there was a flush done before the handshake
              // completed.
              //
              // See https://github.com/netty/netty/pull/2437
              flushedBeforeHandshakeDone = false;
              wrapLater = true;
            }

            break;
          default:
            throw new IllegalStateException("Unknown handshake status: " + handshakeStatus);
        }

        if (status == Status.BUFFER_UNDERFLOW || consumed == 0 && produced == 0) {
          break;
        }
      }

      if (wrapLater) {
        wrap(ctx, true);
      }
    } catch (SSLException e) {
      setHandshakeFailure(e);
      throw e;
    }
  }