This is an automated email from the ASF dual-hosted git repository.

johnnyv pushed a commit to branch bugfix/DIRMINA1132
in repository https://gitbox.apache.org/repos/asf/mina.git

commit 4c7af5615549cf557141765cefe84f409413130e
Author: Jonathan Valliere <john...@apache.org>
AuthorDate: Sat Jul 24 11:19:50 2021 -0400

    Adds initial ssl2
---
 .../mina/filter/ssl2/EncryptedWriteRequest.java    |  32 +++
 .../org/apache/mina/filter/ssl2/SSL2Filter.java    | 248 ++++++++++++++++
 .../org/apache/mina/filter/ssl2/SSL2Handler.java   | 217 ++++++++++++++
 .../org/apache/mina/filter/ssl2/SSL2HandlerG0.java | 318 +++++++++++++++++++++
 .../org/apache/mina/util/BasicThreadFactory.java   |  62 ++++
 .../java/org/apache/mina/util/StackInspector.java  |  78 +++++
 .../apache/mina/filter/ssl2/SSL2SimpleTest.java    | 114 ++++++++
 .../org/apache/mina/filter/ssl2/keystore.sslTest   | Bin 0 -> 1368 bytes
 .../org/apache/mina/filter/ssl2/truststore.sslTest | Bin 0 -> 654 bytes
 9 files changed, 1069 insertions(+)

diff --git 
a/mina-core/src/main/java/org/apache/mina/filter/ssl2/EncryptedWriteRequest.java
 
b/mina-core/src/main/java/org/apache/mina/filter/ssl2/EncryptedWriteRequest.java
new file mode 100644
index 0000000..91fabc7
--- /dev/null
+++ 
b/mina-core/src/main/java/org/apache/mina/filter/ssl2/EncryptedWriteRequest.java
@@ -0,0 +1,32 @@
+package org.apache.mina.filter.ssl2;
+
+import org.apache.mina.core.future.WriteFuture;
+import org.apache.mina.core.write.DefaultWriteRequest;
+import org.apache.mina.core.write.WriteRequest;
+
+public class EncryptedWriteRequest extends DefaultWriteRequest {
+
+       // The original message
+       private WriteRequest parentRequest;
+
+       public EncryptedWriteRequest(Object encodedMessage, WriteRequest 
parent) {
+               super(encodedMessage, null);
+       }
+
+       /**
+        * {@inheritDoc}
+        */
+       @Override
+       public boolean isEncoded() {
+               return true;
+       }
+
+       public WriteRequest getParentRequest() {
+               return this.parentRequest;
+       }
+
+       @Override
+       public WriteFuture getFuture() {
+               return (this.getParentRequest() != null) ? 
this.getParentRequest().getFuture() : super.getFuture();
+       }
+}
\ No newline at end of file
diff --git 
a/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Filter.java 
b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Filter.java
new file mode 100644
index 0000000..85d43c6
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Filter.java
@@ -0,0 +1,248 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.filter.ssl2;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLEngine;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.filterchain.IoFilterAdapter;
+import org.apache.mina.core.filterchain.IoFilterChain;
+import org.apache.mina.core.session.AttributeKey;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.write.WriteRequest;
+import org.apache.mina.util.BasicThreadFactory;
+import org.apache.mina.util.StackInspector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An SSL filter that encrypts and decrypts the data exchanged in the session.
+ * Adding this filter triggers SSL handshake procedure immediately by sending a
+ * SSL 'hello' message, so you don't need to call {@link #startSsl(IoSession)}
+ * manually unless you are implementing StartTLS (see below). If you don't want
+ * the handshake procedure to start immediately, please specify {@code false} 
as
+ * {@code autoStart} parameter in the constructor.
+ * <p>
+ * This filter uses an {@link SSLEngine} which was introduced in Java 5, so 
Java
+ * version 5 or above is mandatory to use this filter. And please note that 
this
+ * filter only works for TCP/IP connections.
+ *
+ * @author <a href="http://mina.apache.org";>Apache MINA Project</a>
+ */
+public class SSL2Filter extends IoFilterAdapter {
+       /**
+        * The logger
+        */
+       protected static final Logger LOGGER = 
LoggerFactory.getLogger(SSL2Filter.class);
+
+       protected static final Executor EXECUTOR = new ThreadPoolExecutor(2, 4, 
100, TimeUnit.MILLISECONDS,
+                       new LinkedBlockingDeque<Runnable>(), new 
BasicThreadFactory("ssl-exec"));
+
+       protected static final AttributeKey SSL_HANDLER = new 
AttributeKey(SSL2Filter.class, "handler");
+
+       protected final SSLContext mContext;
+
+       protected boolean mNeedClientAuth;
+
+       protected boolean mWantClientAuth;
+
+       protected String[] mEnabledCipherSuites;
+
+       protected String[] mEnabledProtocols;
+
+       /**
+        * Creates a new SSL filter using the specified {@link SSLContext}.
+        * 
+        * @param sslContext The SSLContext to use
+        */
+       public SSL2Filter(SSLContext sslContext) {
+               if (sslContext == null) {
+                       throw new IllegalArgumentException("SSLContext is 
null");
+               }
+
+               this.mContext = sslContext;
+       }
+
+       /**
+        * @return <tt>true</tt> if the engine will <em>require</em> client
+        *         authentication. This option is only useful to engines in the 
server
+        *         mode.
+        */
+       public boolean isNeedClientAuth() {
+               return mNeedClientAuth;
+       }
+
+       /**
+        * Configures the engine to <em>require</em> client authentication. 
This option
+        * is only useful for engines in the server mode.
+        * 
+        * @param needClientAuth A flag set when we need to authenticate the 
client
+        */
+       public void setNeedClientAuth(boolean needClientAuth) {
+               this.mNeedClientAuth = needClientAuth;
+       }
+
+       /**
+        * @return <tt>true</tt> if the engine will <em>request</em> client
+        *         authentication. This option is only useful to engines in the 
server
+        *         mode.
+        */
+       public boolean isWantClientAuth() {
+               return mWantClientAuth;
+       }
+
+       /**
+        * Configures the engine to <em>request</em> client authentication. 
This option
+        * is only useful for engines in the server mode.
+        * 
+        * @param wantClientAuth A flag set when we want to check the client
+        *                       authentication
+        */
+       public void setWantClientAuth(boolean wantClientAuth) {
+               this.mWantClientAuth = wantClientAuth;
+       }
+
+       /**
+        * @return the list of cipher suites to be enabled when {@link 
SSLEngine} is
+        *         initialized. <tt>null</tt> means 'use {@link SSLEngine}'s 
default.'
+        */
+       public String[] getEnabledCipherSuites() {
+               return mEnabledCipherSuites;
+       }
+
+       /**
+        * Sets the list of cipher suites to be enabled when {@link SSLEngine} 
is
+        * initialized.
+        *
+        * @param cipherSuites <tt>null</tt> means 'use {@link SSLEngine}'s 
default.'
+        */
+       public void setEnabledCipherSuites(String[] cipherSuites) {
+               this.mEnabledCipherSuites = cipherSuites;
+       }
+
+       /**
+        * @return the list of protocols to be enabled when {@link SSLEngine} is
+        *         initialized. <tt>null</tt> means 'use {@link SSLEngine}'s 
default.'
+        */
+       public String[] getEnabledProtocols() {
+               return mEnabledProtocols;
+       }
+
+       /**
+        * Sets the list of protocols to be enabled when {@link SSLEngine} is
+        * initialized.
+        *
+        * @param protocols <tt>null</tt> means 'use {@link SSLEngine}'s 
default.'
+        */
+       public void setEnabledProtocols(String[] protocols) {
+               this.mEnabledProtocols = protocols;
+       }
+
+       /**
+        * Executed just before the filter is added into the chain, we do :
+        * <ul>
+        * <li>check that we don't have a SSL filter already present
+        * <li>we update the next filter
+        * <li>we create the SSL handler helper class
+        * <li>and we store it into the session's Attributes
+        * </ul>
+        */
+       @Override
+       public void onPreAdd(IoFilterChain parent, String name, NextFilter 
next) throws Exception {
+               // Check that we don't have a SSL filter already present in the 
chain
+               if (parent.contains(SSL2Filter.class)) {
+                       String msg = "Only one SSL filter is permitted in a 
chain.";
+                       LOGGER.error(msg);
+                       throw new IllegalStateException(msg);
+               }
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("Adding the SSL Filter {} to the chain", 
name);
+               }
+       }
+
+       @Override
+       public void onPreRemove(IoFilterChain parent, String name, NextFilter 
next) throws Exception {
+               IoSession session = parent.getSession();
+               session.removeAttribute(SSL_HANDLER);
+       }
+
+       @Override
+       public void sessionOpened(NextFilter next, IoSession session) throws 
Exception {
+
+               LOGGER.debug("session openend {}", session);
+
+               StackInspector.get().printStackTrace();
+
+               SSL2Handler x = 
SSL2Handler.class.cast(session.getAttribute(SSL_HANDLER));
+
+               if (x == null) {
+                       SSLEngine e = mContext.createSSLEngine();
+
+                       e.setNeedClientAuth(mNeedClientAuth);
+                       e.setWantClientAuth(mWantClientAuth);
+                       e.setEnabledCipherSuites(mEnabledCipherSuites);
+                       e.setEnabledProtocols(mEnabledProtocols);
+                       e.setUseClientMode(!session.isServer());
+
+                       x = new SSL2HandlerG0(e, EXECUTOR, session);
+
+                       session.setAttribute(SSL_HANDLER, x);
+               }
+
+               x.open(next);
+       }
+
+       @Override
+       public void messageReceived(NextFilter next, IoSession session, Object 
message) throws Exception {
+               SSL2Handler x = 
SSL2Handler.class.cast(session.getAttribute(SSL_HANDLER));
+               x.receive(next, IoBuffer.class.cast(message));
+       }
+
+       @Override
+       public void messageSent(NextFilter next, IoSession session, 
WriteRequest writeRequest) throws Exception {
+               if (writeRequest instanceof EncryptedWriteRequest) {
+                       EncryptedWriteRequest e = 
EncryptedWriteRequest.class.cast(writeRequest);
+                       SSL2Handler x = 
SSL2Handler.class.cast(session.getAttribute(SSL_HANDLER));
+                       x.ack(next, writeRequest);
+                       if (e.getParentRequest() != null) {
+                               next.messageSent(session, e.getParentRequest());
+                       }
+               } else {
+                       super.messageSent(next, session, writeRequest);
+               }
+       }
+
+       @Override
+       public void filterWrite(NextFilter next, IoSession session, 
WriteRequest writeRequest) throws Exception {
+               if (writeRequest instanceof EncryptedWriteRequest) {
+                       super.filterWrite(next, session, writeRequest);
+               } else {
+                       SSL2Handler x = 
SSL2Handler.class.cast(session.getAttribute(SSL_HANDLER));
+                       x.write(next, writeRequest);
+               }
+       }
+}
diff --git 
a/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Handler.java 
b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Handler.java
new file mode 100644
index 0000000..601e73b
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2Handler.java
@@ -0,0 +1,217 @@
+package org.apache.mina.filter.ssl2;
+
+import java.util.Deque;
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.Executor;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.write.WriteRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class SSL2Handler {
+
+       /**
+        * Static logger
+        */
+       static protected final Logger LOGGER = 
LoggerFactory.getLogger(SSL2Handler.class);
+
+       /**
+        * Write Requests which are enqueued prior to the completion of the 
handshaking
+        */
+       protected final Deque<WriteRequest> mWriteQueue = new 
ConcurrentLinkedDeque<>();
+
+       /**
+        * Requests which have been sent to the socket and waiting 
acknowledgment
+        */
+       protected final Deque<WriteRequest> mAckQueue = new 
ConcurrentLinkedDeque<>();
+
+       /**
+        * SSL Engine
+        */
+       protected final SSLEngine mEngine;
+
+       /**
+        * Task executor
+        */
+       protected final Executor mExecutor;
+
+       /**
+        * Socket session
+        */
+       protected final IoSession mSession;
+
+       /**
+        * Progressive decoder buffer
+        */
+       protected IoBuffer mReceiveBuffer;
+
+       public SSL2Handler(SSLEngine p, Executor e, IoSession s) {
+               this.mEngine = p;
+               this.mExecutor = e;
+               this.mSession = s;
+       }
+
+       /**
+        * Opens the encryption session, this may include sending the initial 
handshake
+        * message
+        * 
+        * @param session
+        * @param next
+        * 
+        * @throws SSLException
+        */
+       abstract public void open(NextFilter next) throws SSLException;
+
+       /**
+        * Decodes encrypted messages and passes the results to the {@code 
next} filter.
+        * 
+        * @param message
+        * @param session
+        * @param next
+        * 
+        * @throws SSLException
+        */
+       abstract public void receive(NextFilter next, final IoBuffer message) 
throws SSLException;
+
+       /**
+        * Acknowledge that a {@link WriteRequest} has been successfully 
written to the
+        * {@link IoSession}
+        * <p>
+        * This functionality is used to enforce flow control by allowing only a
+        * specific number of pending write operations at any moment of time. 
When one
+        * {@code WriteRequest} is acknowledged, another can be encoded and 
written.
+        * 
+        * @param request
+        * @param session
+        * @param next
+        * 
+        * @throws SSLException
+        */
+       abstract public void ack(NextFilter next, final WriteRequest request) 
throws SSLException;
+
+       /**
+        * Encrypts and writes the specified {@link WriteRequest} to the
+        * {@link IoSession} or enqueues it to be processed later.
+        * <p>
+        * The encryption session may be currently handshaking preventing 
application
+        * messages from being written.
+        * 
+        * @param request
+        * @param session
+        * @param next
+        * 
+        * @throws SSLException
+        */
+       abstract public void write(NextFilter next, final WriteRequest request) 
throws SSLException;
+
+       /**
+        * Closes the encryption session and writes any required messages
+        * 
+        * @param session
+        * @param next
+        * 
+        * @throws SSLException
+        */
+       abstract public void close(NextFilter next) throws SSLException;
+
+       /**
+        * {@inheritDoc}
+        */
+       public String toString() {
+               StringBuilder b = new StringBuilder();
+
+               b.append(this.getClass().getSimpleName());
+               b.append("@");
+               b.append(Integer.toHexString(this.hashCode()));
+               b.append("[mode=");
+
+               if (this.mEngine.getUseClientMode()) {
+                       b.append("client");
+               } else {
+                       b.append("server");
+               }
+
+               b.append("]");
+
+               return b.toString();
+       }
+
+       /**
+        * Combines the received data with any previously received data
+        * 
+        * @param source received data
+        * @return buffer to decode
+        */
+       protected IoBuffer resume_decode_buffer(IoBuffer source) {
+               if (mReceiveBuffer == null)
+                       if (source == null)
+                               return IoBuffer.allocate(0);
+                       else
+                               return source;
+               else {
+                       if (source != null) {
+                               mReceiveBuffer.expand(source.remaining());
+                               mReceiveBuffer.put(source);
+                               source.free();
+                       }
+                       mReceiveBuffer.flip();
+                       return mReceiveBuffer;
+               }
+       }
+
+       /**
+        * Stores data for later use if any is remaining
+        * 
+        * @param source the buffer previously returned by
+        *               {@link #resume_decode_buffer(IoBuffer)}
+        */
+       protected void save_decode_buffer(IoBuffer source) {
+               if (source.hasRemaining()) {
+                       if (source.isDerived()) {
+                               this.mReceiveBuffer = 
IoBuffer.allocate(source.remaining());
+                               this.mReceiveBuffer.put(source);
+                       } else {
+                               source.compact();
+                               this.mReceiveBuffer = source;
+                       }
+               } else {
+                       source.free();
+                       this.mReceiveBuffer = null;
+               }
+       }
+
+       /**
+        * Allocates the default encoder buffer for the given source size
+        * 
+        * @param source
+        * @return buffer
+        */
+       protected IoBuffer allocate_encode_buffer(int estimate) {
+               SSLSession session = this.mEngine.getHandshakeSession();
+               if (session == null)
+                       session = this.mEngine.getSession();
+               int packets = Math.max(2, Math.min(16, 1 + (estimate / 
session.getApplicationBufferSize())));
+               return IoBuffer.allocate(packets * 
session.getPacketBufferSize());
+       }
+
+       /**
+        * Allocates the default decoder buffer for the given source size
+        * 
+        * @param source
+        * @return buffer
+        */
+       protected IoBuffer allocate_app_buffer(int estimate) {
+               SSLSession session = this.mEngine.getHandshakeSession();
+               if (session == null)
+                       session = this.mEngine.getSession();
+               int packets = 1 + (estimate / session.getPacketBufferSize());
+               return IoBuffer.allocate(packets * 
session.getApplicationBufferSize());
+       }
+}
diff --git 
a/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2HandlerG0.java 
b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2HandlerG0.java
new file mode 100644
index 0000000..700ebdd
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl2/SSL2HandlerG0.java
@@ -0,0 +1,318 @@
+package org.apache.mina.filter.ssl2;
+
+import java.util.concurrent.Executor;
+
+import javax.net.ssl.SSLEngine;
+import javax.net.ssl.SSLEngineResult;
+import javax.net.ssl.SSLEngineResult.HandshakeStatus;
+import javax.net.ssl.SSLException;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.write.WriteRequest;
+
+public class SSL2HandlerG0 extends SSL2Handler {
+
+       public SSL2HandlerG0(SSLEngine p, Executor e, IoSession s) {
+               super(p, e, s);
+       }
+
+       synchronized public void open(final NextFilter next) throws 
SSLException {
+               if (this.mEngine.getUseClientMode()) {
+                       this.mEngine.beginHandshake();
+                       this.lwrite(next);
+               }
+       }
+
+       synchronized public void receive(final NextFilter next, final IoBuffer 
message) throws SSLException {
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} receive() - source {}", toString(), 
message);
+               }
+
+               final IoBuffer input = resume_decode_buffer(message);
+
+               try {
+                       while (lreceive(next, input) && message.hasRemaining()) 
{
+                               // spin
+                       }
+               } finally {
+                       save_decode_buffer(input);
+               }
+       }
+
+       /**
+        * Process a received message
+        * 
+        * @param message received data
+        * @param session user session
+        * @param next    filter
+        * @return {@code true} if some of the message was consumed
+        * @throws SSLException
+        */
+       @SuppressWarnings("incomplete-switch")
+       protected boolean lreceive(final NextFilter next, final IoBuffer 
message) throws SSLException {
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} lreceive() - source {}", toString(), 
message);
+               }
+
+               final IoBuffer source = message == null ? IoBuffer.allocate(0) 
: message;
+               final IoBuffer dest = allocate_app_buffer(source.remaining());
+
+               final SSLEngineResult result = mEngine.unwrap(source.buf(), 
dest.buf());
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} lreceive() - bytes-consumed {}, 
bytes-produced {}, status {}", toString(),
+                                       result.bytesConsumed(), 
result.bytesProduced(), result.getStatus());
+               }
+
+               if (result.bytesProduced() == 0) {
+                       dest.free();
+               } else {
+                       dest.flip();
+
+                       if (LOGGER.isDebugEnabled()) {
+                               LOGGER.debug("{} lreceive() - result {}", 
toString(), dest);
+                       }
+
+                       next.messageReceived(this.mSession, dest);
+               }
+
+               switch (result.getHandshakeStatus()) {
+                       case NEED_TASK:
+                               if (LOGGER.isDebugEnabled()) {
+                                       LOGGER.debug("{} lreceive() - handshake 
needs task, scheduling tasks", toString());
+                               }
+                               this.schedule_task(next);
+                               break;
+                       case NEED_WRAP:
+                               if (LOGGER.isDebugEnabled()) {
+                                       LOGGER.debug("{} lreceive() - handshake 
needs to write a new message", toString());
+                               }
+                               this.lwrite(next);
+                               break;
+                       case FINISHED:
+                               if (LOGGER.isDebugEnabled()) {
+                                       LOGGER.debug("{} lreceive() - handshake 
finished, flushing pending requests", toString());
+                               }
+                               this.lflush(next);
+                               break;
+               }
+
+               return result.bytesConsumed() > 0;
+       }
+
+       synchronized public void ack(final NextFilter next, final WriteRequest 
request) throws SSLException {
+
+       }
+
+       synchronized public void write(final NextFilter next, final 
WriteRequest request) throws SSLException {
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} write() - source {}", toString(), 
request);
+               }
+
+               this.mWriteQueue.add(request);
+               this.lflush(next);
+       }
+
+       /**
+        * Attempts to encode the WriteRequest and write the data to the 
IoSession
+        * 
+        * @param request
+        * @param session
+        * @param next
+        * @return {@code true} if the WriteRequest was successfully written
+        * @throws SSLException
+        */
+       @SuppressWarnings("incomplete-switch")
+       synchronized protected boolean lwrite(final NextFilter next, final 
WriteRequest request) throws SSLException {
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} lwrite() - source {}", toString(), 
request);
+               }
+
+               final IoBuffer source = 
IoBuffer.class.cast(request.getMessage());
+               final IoBuffer dest = 
allocate_encode_buffer(source.remaining());
+
+               final SSLEngineResult result = this.mEngine.wrap(source.buf(), 
dest.buf());
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} lwrite() - bytes-consumed {}, 
bytes-produced {}, status {}, handshake {}", toString(),
+                                       result.bytesConsumed(), 
result.bytesProduced(), result.getStatus(), result.getHandshakeStatus());
+               }
+
+               if (result.getHandshakeStatus() == 
HandshakeStatus.NOT_HANDSHAKING) {
+                       // then we probably consumed some data
+                       dest.flip();
+                       if (source.hasRemaining()) {
+                               next.filterWrite(this.mSession, new 
EncryptedWriteRequest(dest, null));
+                               lwrite(next, request); // write additional 
chunks
+                       } else {
+                               source.rewind();
+                               next.filterWrite(this.mSession, new 
EncryptedWriteRequest(dest, request));
+                       }
+
+                       return true;
+               } else {
+                       if (dest.position() == 0) {
+                               dest.free();
+                       } else {
+                               next.filterWrite(this.mSession, new 
EncryptedWriteRequest(dest, null));
+                       }
+
+                       switch (result.getHandshakeStatus()) {
+                               case NEED_TASK:
+                                       if (LOGGER.isDebugEnabled()) {
+                                               LOGGER.debug("{} lwrite() - 
handshake needs task, scheduling tasks", toString());
+                                       }
+                                       this.schedule_task(next);
+                                       break;
+                               case NEED_WRAP:
+                                       if (LOGGER.isDebugEnabled()) {
+                                               LOGGER.debug("{} lwrite() - 
handshake needs to encode a message", toString());
+                                       }
+                                       return this.lwrite(next, request);
+                               case FINISHED:
+                                       if (LOGGER.isDebugEnabled()) {
+                                               LOGGER.debug("{} lwrite() - 
handshake finished, flushing pending requests", toString());
+                                       }
+                                       if (this.lwrite(next, request)) {
+                                               this.lflush(next);
+                                               return true;
+                                       }
+                                       break;
+                       }
+               }
+
+               return false;
+       }
+
+       /**
+        * Attempts to generate a handshake message and write the data to the 
IoSession
+        * 
+        * @param session
+        * @param next
+        * @return {@code true} if a message was generated and written
+        * @throws SSLException
+        */
+       @SuppressWarnings("incomplete-switch")
+       synchronized protected boolean lwrite(NextFilter next) throws 
SSLException {
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} lwrite() - internal", toString());
+               }
+
+               final IoBuffer source = IoBuffer.allocate(0);
+               final IoBuffer dest = 
allocate_encode_buffer(source.remaining());
+
+               final SSLEngineResult result = this.mEngine.wrap(source.buf(), 
dest.buf());
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} lwrite() - bytes-consumed {}, 
bytes-produced {}", toString(), result.bytesConsumed(),
+                                       result.bytesProduced());
+               }
+
+               if (dest.position() == 0) {
+                       dest.free();
+               } else {
+                       dest.flip();
+
+                       if (LOGGER.isDebugEnabled()) {
+                               LOGGER.debug("{} lwrite() - result {}", 
toString(), dest);
+                       }
+
+                       final EncryptedWriteRequest encrypted = new 
EncryptedWriteRequest(dest, null);
+                       next.filterWrite(this.mSession, encrypted);
+               }
+
+               switch (result.getHandshakeStatus()) {
+                       case NEED_TASK:
+                               if (LOGGER.isDebugEnabled()) {
+                                       LOGGER.debug("{} lwrite() - handshake 
needs task, scheduling tasks", toString());
+                               }
+                               this.schedule_task(next);
+                               break;
+                       case NEED_WRAP:
+                               if (LOGGER.isDebugEnabled()) {
+                                       LOGGER.debug("{} lwrite() - handshake 
needs to encode a message", toString());
+                               }
+                               this.lwrite(next);
+                               break;
+                       case FINISHED:
+                               if (LOGGER.isDebugEnabled()) {
+                                       LOGGER.debug("{} lwrite() - handshake 
finished, flushing pending requests", toString());
+                               }
+                               this.lflush(next);
+                               break;
+               }
+
+               return result.bytesProduced() > 0;
+       }
+
+       protected void lflush(final NextFilter next) throws SSLException {
+               if (this.mWriteQueue.isEmpty()) {
+                       if (LOGGER.isDebugEnabled()) {
+                               LOGGER.debug("{} flush() - no saved messages", 
toString());
+                       }
+                       return;
+               }
+
+               WriteRequest current = null;
+
+               while ((current = this.mWriteQueue.poll()) != null) {
+                       if (lwrite(next, current) == false) {
+                               this.mWriteQueue.addFirst(current);
+                               break;
+                       }
+               }
+       }
+
+       synchronized public void close(final NextFilter next) throws 
SSLException {
+               if (mEngine.isOutboundDone())
+                       return;
+
+               if (LOGGER.isDebugEnabled()) {
+                       LOGGER.debug("{} close() - closing session", 
toString());
+               }
+
+               mEngine.closeOutbound();
+               this.lwrite(next);
+       }
+
+       protected void schedule_task(final NextFilter next) {
+               if (this.mExecutor == null) {
+                       this.execute_task(next);
+               } else {
+                       this.mExecutor.execute(new Runnable() {
+                               @Override
+                               public void run() {
+                                       SSL2HandlerG0.this.execute_task(next);
+                               }
+                       });
+               }
+       }
+
+       synchronized protected void execute_task(final NextFilter next) {
+               Runnable t = null;
+               while ((t = mEngine.getDelegatedTask()) != null) {
+                       try {
+                               if (LOGGER.isDebugEnabled()) {
+                                       LOGGER.debug("{} task() - executing 
{}", toString(), t);
+                               }
+
+                               t.run();
+
+                               if (LOGGER.isDebugEnabled()) {
+                                       LOGGER.debug("{} task() - writing 
handshake messages", toString());
+                               }
+
+                               lwrite(next);
+                       } catch (SSLException e) {
+                               e.printStackTrace();
+                       }
+               }
+       }
+}
diff --git 
a/mina-core/src/main/java/org/apache/mina/util/BasicThreadFactory.java 
b/mina-core/src/main/java/org/apache/mina/util/BasicThreadFactory.java
new file mode 100644
index 0000000..7e73017
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/util/BasicThreadFactory.java
@@ -0,0 +1,62 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Utility for creating thread factories
+ * 
+ * @author <a href="http://mina.apache.org";>Apache MINA Project</a>
+ * @author Jonathan Valliere
+ */
+public class BasicThreadFactory implements java.util.concurrent.ThreadFactory {
+       public final AtomicInteger count = new AtomicInteger(0);
+       public final String name;
+
+       public final boolean deamon;
+       public final int priority;
+
+       public BasicThreadFactory(String basename, boolean daemon, int 
priority) {
+               this.name = basename;
+               this.deamon = daemon;
+               this.priority = priority;
+       }
+
+       public BasicThreadFactory(String basename, boolean daemon) {
+               this(basename, daemon, Thread.NORM_PRIORITY);
+       }
+
+       public BasicThreadFactory(String basename) {
+               this(basename, false, Thread.NORM_PRIORITY);
+       }
+
+       @Override
+       public Thread newThread(Runnable pool) {
+               Thread t = new Thread(pool);
+
+               t.setName(this.name + "-" + this.count.getAndIncrement());
+               t.setPriority(this.priority);
+               t.setDaemon(this.deamon);
+
+               return t;
+       }
+
+}
diff --git a/mina-core/src/main/java/org/apache/mina/util/StackInspector.java 
b/mina-core/src/main/java/org/apache/mina/util/StackInspector.java
new file mode 100644
index 0000000..45b3414
--- /dev/null
+++ b/mina-core/src/main/java/org/apache/mina/util/StackInspector.java
@@ -0,0 +1,78 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+package org.apache.mina.util;
+
+/**
+ * Utility to retrieving the thread stack debug information
+ * 
+ * @author <a href="http://mina.apache.org";>Apache MINA Project</a>
+ * @author Jonathan Valliere
+ */
+public class StackInspector extends RuntimeException {
+       static public final StackTraceElement callee() {
+               return Thread.currentThread().getStackTrace()[3];
+       }
+
+       static public final StackInspector get(String message) {
+               try {
+                       throw new StackInspector(message);
+               } catch (StackInspector e0) {
+                       return e0;
+               }
+       }
+
+       static public final StackInspector get(Throwable cause) {
+               try {
+                       throw new StackInspector(cause);
+               } catch (StackInspector e0) {
+                       return e0;
+               }
+       }
+
+       static public final StackInspector get() {
+               try {
+                       throw new StackInspector("Stack from Thread: " + 
Thread.currentThread().getName());
+               } catch (StackInspector e0) {
+                       return e0;
+               }
+       }
+
+       static private final long serialVersionUID = 1L;
+
+       StackInspector() {
+
+       }
+
+       StackInspector(String message) {
+               super(message);
+       }
+
+       StackInspector(Throwable cause) {
+               super(cause);
+       }
+
+       StackInspector(String message, Throwable cause) {
+               super(message, cause);
+       }
+
+       StackInspector(String message, Throwable cause, boolean 
enableSuppression, boolean writableStackTrace) {
+               super(message, cause, enableSuppression, writableStackTrace);
+       }
+}
diff --git 
a/mina-core/src/test/java/org/apache/mina/filter/ssl2/SSL2SimpleTest.java 
b/mina-core/src/test/java/org/apache/mina/filter/ssl2/SSL2SimpleTest.java
new file mode 100644
index 0000000..d198459
--- /dev/null
+++ b/mina-core/src/test/java/org/apache/mina/filter/ssl2/SSL2SimpleTest.java
@@ -0,0 +1,114 @@
+package org.apache.mina.filter.ssl2;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.apache.mina.core.buffer.IoBuffer;
+import org.apache.mina.core.future.IoFuture;
+import org.apache.mina.core.service.IoAcceptor;
+import org.apache.mina.core.service.IoConnector;
+import org.apache.mina.core.service.IoHandlerAdapter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.core.write.DefaultWriteRequest;
+import org.apache.mina.core.write.WriteRequest;
+import org.apache.mina.filter.ssl.SslDIRMINA937Test;
+import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
+import org.apache.mina.transport.socket.nio.NioSocketConnector;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SSL2SimpleTest {
+
+       public static void main(String[] args) throws NoSuchAlgorithmException, 
KeyManagementException, KeyStoreException,
+                       UnrecoverableKeyException, CertificateException, 
IOException {
+               // System.setProperty("javax.net.debug", "all");
+
+               KeyManagerFactory kmf = 
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
+               TrustManagerFactory tmf = 
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+
+               KeyStore ks = KeyStore.getInstance("JKS");
+               KeyStore ts = KeyStore.getInstance("JKS");
+
+               
ks.load(SslDIRMINA937Test.class.getResourceAsStream("keystore.sslTest"), 
"password".toCharArray());
+               
ts.load(SslDIRMINA937Test.class.getResourceAsStream("truststore.sslTest"), 
"password".toCharArray());
+
+               kmf.init(ks, "password".toCharArray());
+               tmf.init(ts);
+
+               final SSLContext context = SSLContext.getInstance("TLS");
+               context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new 
SecureRandom());
+
+               final SSL2Filter filter = new SSL2Filter(context);
+               filter.setEnabledCipherSuites(new String[] { 
"TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384" });
+               filter.setEnabledProtocols(new String[] { "TLSv1.3" });
+
+               final IoAcceptor socket_acceptor = new NioSocketAcceptor();
+
+               socket_acceptor.getFilterChain().addFirst("ssl", filter);
+               socket_acceptor.setHandler(new DebugFilter());
+
+               final IoConnector socket_connector = new NioSocketConnector();
+
+               socket_connector.getFilterChain().addFirst("ssl", filter);
+               socket_connector.setHandler(new DebugFilter());
+
+               final InetSocketAddress server_address = new 
InetSocketAddress("0.0.0.0", 53301);
+               socket_acceptor.bind(server_address);
+
+               final IoFuture connect_future = 
socket_connector.connect(server_address);
+               connect_future.awaitUninterruptibly();
+
+               final IoSession client_socket = connect_future.getSession();
+               
+               
client_socket.write(createWriteRequest()).awaitUninterruptibly();
+
+               try {
+                       Thread.sleep(1000);
+               } catch (InterruptedException e) {
+
+               }
+
+               client_socket.closeOnFlush().awaitUninterruptibly();
+
+               socket_connector.dispose();
+
+               socket_acceptor.unbind();
+               socket_acceptor.dispose();
+       }
+
+       public static class DebugFilter extends IoHandlerAdapter {
+               protected static final Logger LOGGER = 
LoggerFactory.getLogger(DebugFilter.class);
+
+               @Override
+               public void messageReceived(IoSession session, Object message) 
throws Exception {
+
+                       IoBuffer b = IoBuffer.class.cast(message);
+                       LOGGER.debug("received clear-text message\n" + 
b.getHexDump(true));
+               }
+       }
+
+       public static WriteRequest createWriteRequest() {
+               // HTTP request
+               StringBuilder http = new StringBuilder();
+               http.append("GET / HTTP/1.0\r\n");
+               http.append("Connection: close\r\n");
+               http.append("\r\n");
+
+               IoBuffer message = IoBuffer.allocate(1024);
+               message.put(http.toString().getBytes());
+               message.flip();
+
+               return new DefaultWriteRequest(message);
+       }
+}
diff --git 
a/mina-core/src/test/resources/org/apache/mina/filter/ssl2/keystore.sslTest 
b/mina-core/src/test/resources/org/apache/mina/filter/ssl2/keystore.sslTest
new file mode 100644
index 0000000..36190ba
Binary files /dev/null and 
b/mina-core/src/test/resources/org/apache/mina/filter/ssl2/keystore.sslTest 
differ
diff --git 
a/mina-core/src/test/resources/org/apache/mina/filter/ssl2/truststore.sslTest 
b/mina-core/src/test/resources/org/apache/mina/filter/ssl2/truststore.sslTest
new file mode 100644
index 0000000..48c5963
Binary files /dev/null and 
b/mina-core/src/test/resources/org/apache/mina/filter/ssl2/truststore.sslTest 
differ

Reply via email to