You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by jo...@apache.org on 2021/07/24 15:20:40 UTC
[mina] 01/04: formatted mina.filter.ssl
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 aa500a68ab8f3b94ae5510d6b5da21da83287b23
Author: Jonathan Valliere <jo...@apache.org>
AuthorDate: Wed Sep 23 13:43:42 2020 -0400
formatted mina.filter.ssl
---
.../mina/filter/ssl/BogusTrustManagerFactory.java | 112 +-
.../apache/mina/filter/ssl/KeyStoreFactory.java | 292 ++--
.../apache/mina/filter/ssl/SslContextFactory.java | 782 +++++-----
.../java/org/apache/mina/filter/ssl/SslEvent.java | 6 +-
.../java/org/apache/mina/filter/ssl/SslFilter.java | 1643 ++++++++++----------
.../org/apache/mina/filter/ssl/SslHandler.java | 1615 +++++++++----------
.../org/apache/mina/filter/ssl/package-info.java | 3 +-
7 files changed, 2233 insertions(+), 2220 deletions(-)
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
index 5984e69..e0402c2 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/BogusTrustManagerFactory.java
@@ -39,68 +39,68 @@ import javax.net.ssl.X509TrustManager;
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
public class BogusTrustManagerFactory extends TrustManagerFactory {
- private static final X509TrustManager X509 = new X509TrustManager() {
- /**
- * {@inheritDoc}
- */
- @Override
- public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
- // Do nothing
- }
+ private static final X509TrustManager X509 = new X509TrustManager() {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+ // Do nothing
+ }
- /**
- * {@inheritDoc}
- */
- @Override
- public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
- // Do nothing
- }
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
+ // Do nothing
+ }
- /**
- * {@inheritDoc}
- */
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- return new X509Certificate[0];
- }
- };
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+ };
- private static final TrustManager[] X509_MANAGERS = new TrustManager[] { X509 };
+ private static final TrustManager[] X509_MANAGERS = new TrustManager[] { X509 };
- /**
- * Creates a new BogusTrustManagerFactory instance
- */
- public BogusTrustManagerFactory() {
- super(new BogusTrustManagerFactorySpi(), new Provider("MinaBogus", 1.0, "") {
- private static final long serialVersionUID = -4024169055312053827L;
- }, "MinaBogus");
- }
+ /**
+ * Creates a new BogusTrustManagerFactory instance
+ */
+ public BogusTrustManagerFactory() {
+ super(new BogusTrustManagerFactorySpi(), new Provider("MinaBogus", 1.0, "") {
+ private static final long serialVersionUID = -4024169055312053827L;
+ }, "MinaBogus");
+ }
- private static class BogusTrustManagerFactorySpi extends TrustManagerFactorySpi {
- /**
- * {@inheritDoc}
- */
- @Override
- protected TrustManager[] engineGetTrustManagers() {
- return X509_MANAGERS;
- }
+ private static class BogusTrustManagerFactorySpi extends TrustManagerFactorySpi {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected TrustManager[] engineGetTrustManagers() {
+ return X509_MANAGERS;
+ }
- /**
- * {@inheritDoc}
- */
- @Override
- protected void engineInit(KeyStore keystore) throws KeyStoreException {
- // noop
- }
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void engineInit(KeyStore keystore) throws KeyStoreException {
+ // noop
+ }
- /**
- * {@inheritDoc}
- */
- @Override
- protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
- throws InvalidAlgorithmParameterException {
- // noop
- }
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void engineInit(ManagerFactoryParameters managerFactoryParameters)
+ throws InvalidAlgorithmParameterException {
+ // noop
+ }
- }
+ }
}
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
index b0903fa..875f039 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/KeyStoreFactory.java
@@ -40,150 +40,150 @@ import java.security.cert.CertificateException;
*/
public class KeyStoreFactory {
- private String type = "JKS";
-
- private String provider = null;
-
- private char[] password = null;
-
- private byte[] data = null;
-
- /**
- * Creates a new {@link KeyStore}. This method will be called
- * by the base class when Spring creates a bean using this FactoryBean.
- *
- * @return a new {@link KeyStore} instance.
- * @throws KeyStoreException If we can't create an instance of the KeyStore for the given type
- * @throws NoSuchProviderException If we don't have the provider registered to create the KeyStore
- * @throws NoSuchAlgorithmException If the KeyStore algorithm cannot be used
- * @throws CertificateException If the KeyStore certificate cannot be loaded
- * @throws IOException If the KeyStore cannot be loaded
- */
- public KeyStore newInstance() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
- CertificateException, IOException {
- if (data == null) {
- throw new IllegalStateException("data property is not set.");
- }
-
- KeyStore ks;
- if (provider == null) {
- ks = KeyStore.getInstance(type);
- } else {
- ks = KeyStore.getInstance(type, provider);
- }
-
- InputStream is = new ByteArrayInputStream(data);
-
- try {
- ks.load(is, password);
- } finally {
- try {
- is.close();
- } catch (IOException ignored) {
- // Do nothing
- }
- }
-
- return ks;
- }
-
- /**
- * Sets the type of key store to create. The default is to create a
- * JKS key store.
- *
- * @param type the type to use when creating the key store.
- * @throws IllegalArgumentException if the specified value is
- * <code>null</code>.
- */
- public void setType(String type) {
- if (type == null) {
- throw new IllegalArgumentException("type");
- }
- this.type = type;
- }
-
- /**
- * Sets the key store password. If this value is <code>null</code> no
- * password will be used to check the integrity of the key store.
- *
- * @param password the password or <code>null</code> if no password is
- * needed.
- */
- public void setPassword(String password) {
- if (password != null) {
- this.password = password.toCharArray();
- } else {
- this.password = null;
- }
- }
-
- /**
- * Sets the name of the provider to use when creating the key store. The
- * default is to use the platform default provider.
- *
- * @param provider the name of the provider, e.g. <tt>"SUN"</tt>.
- */
- public void setProvider(String provider) {
- this.provider = provider;
- }
-
- /**
- * Sets the data which contains the key store.
- *
- * @param data the byte array that contains the key store
- */
- public void setData(byte[] data) {
- byte[] copy = new byte[data.length];
- System.arraycopy(data, 0, copy, 0, data.length);
- this.data = copy;
- }
-
- /**
- * Sets the data which contains the key store.
- *
- * @param dataStream the {@link InputStream} that contains the key store
- * @throws IOException If we can't process the stream
- */
- private void setData(InputStream dataStream) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- try {
- for (;;) {
- int readByte = dataStream.read();
-
- if (readByte < 0) {
- break;
- }
-
- out.write(readByte);
- }
-
- setData(out.toByteArray());
- } finally {
- try {
- dataStream.close();
- } catch (IOException e) {
- // Ignore.
- }
- }
- }
-
- /**
- * Sets the data which contains the key store.
- *
- * @param dataFile the {@link File} that contains the key store
- * @throws IOException If we can't process the file
- */
- public void setDataFile(File dataFile) throws IOException {
- setData(new BufferedInputStream(new FileInputStream(dataFile)));
- }
-
- /**
- * Sets the data which contains the key store.
- *
- * @param dataUrl the {@link URL} that contains the key store.
- * @throws IOException If we can't process the URL
- */
- public void setDataUrl(URL dataUrl) throws IOException {
- setData(dataUrl.openStream());
- }
+ private String type = "JKS";
+
+ private String provider = null;
+
+ private char[] password = null;
+
+ private byte[] data = null;
+
+ /**
+ * Creates a new {@link KeyStore}. This method will be called by the base class
+ * when Spring creates a bean using this FactoryBean.
+ *
+ * @return a new {@link KeyStore} instance.
+ * @throws KeyStoreException If we can't create an instance of the
+ * KeyStore for the given type
+ * @throws NoSuchProviderException If we don't have the provider registered to
+ * create the KeyStore
+ * @throws NoSuchAlgorithmException If the KeyStore algorithm cannot be used
+ * @throws CertificateException If the KeyStore certificate cannot be loaded
+ * @throws IOException If the KeyStore cannot be loaded
+ */
+ public KeyStore newInstance() throws KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException,
+ CertificateException, IOException {
+ if (data == null) {
+ throw new IllegalStateException("data property is not set.");
+ }
+
+ KeyStore ks;
+ if (provider == null) {
+ ks = KeyStore.getInstance(type);
+ } else {
+ ks = KeyStore.getInstance(type, provider);
+ }
+
+ InputStream is = new ByteArrayInputStream(data);
+
+ try {
+ ks.load(is, password);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException ignored) {
+ // Do nothing
+ }
+ }
+
+ return ks;
+ }
+
+ /**
+ * Sets the type of key store to create. The default is to create a JKS key
+ * store.
+ *
+ * @param type the type to use when creating the key store.
+ * @throws IllegalArgumentException if the specified value is <code>null</code>.
+ */
+ public void setType(String type) {
+ if (type == null) {
+ throw new IllegalArgumentException("type");
+ }
+ this.type = type;
+ }
+
+ /**
+ * Sets the key store password. If this value is <code>null</code> no password
+ * will be used to check the integrity of the key store.
+ *
+ * @param password the password or <code>null</code> if no password is needed.
+ */
+ public void setPassword(String password) {
+ if (password != null) {
+ this.password = password.toCharArray();
+ } else {
+ this.password = null;
+ }
+ }
+
+ /**
+ * Sets the name of the provider to use when creating the key store. The default
+ * is to use the platform default provider.
+ *
+ * @param provider the name of the provider, e.g. <tt>"SUN"</tt>.
+ */
+ public void setProvider(String provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Sets the data which contains the key store.
+ *
+ * @param data the byte array that contains the key store
+ */
+ public void setData(byte[] data) {
+ byte[] copy = new byte[data.length];
+ System.arraycopy(data, 0, copy, 0, data.length);
+ this.data = copy;
+ }
+
+ /**
+ * Sets the data which contains the key store.
+ *
+ * @param dataStream the {@link InputStream} that contains the key store
+ * @throws IOException If we can't process the stream
+ */
+ private void setData(InputStream dataStream) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ for (;;) {
+ int readByte = dataStream.read();
+
+ if (readByte < 0) {
+ break;
+ }
+
+ out.write(readByte);
+ }
+
+ setData(out.toByteArray());
+ } finally {
+ try {
+ dataStream.close();
+ } catch (IOException e) {
+ // Ignore.
+ }
+ }
+ }
+
+ /**
+ * Sets the data which contains the key store.
+ *
+ * @param dataFile the {@link File} that contains the key store
+ * @throws IOException If we can't process the file
+ */
+ public void setDataFile(File dataFile) throws IOException {
+ setData(new BufferedInputStream(new FileInputStream(dataFile)));
+ }
+
+ /**
+ * Sets the data which contains the key store.
+ *
+ * @param dataUrl the {@link URL} that contains the key store.
+ * @throws IOException If we can't process the URL
+ */
+ public void setDataUrl(URL dataUrl) throws IOException {
+ setData(dataUrl.openStream());
+ }
}
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
index e05d3d6..255eeb5 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslContextFactory.java
@@ -33,11 +33,12 @@ import javax.net.ssl.TrustManagerFactory;
/**
* A factory that creates and configures a new {@link SSLContext}.
* <p>
- * If no properties are set the returned {@link SSLContext} will
- * be equivalent to what the following creates:
+ * If no properties are set the returned {@link SSLContext} will be equivalent
+ * to what the following creates:
+ *
* <pre>
- * SSLContext c = SSLContext.getInstance( "TLSv1.2" );
- * c.init(null, null, null);
+ * SSLContext c = SSLContext.getInstance("TLSv1.2");
+ * c.init(null, null, null);
* </pre>
* <p>
* Use the properties prefixed with <code>keyManagerFactory</code> to control
@@ -50,388 +51,393 @@ import javax.net.ssl.TrustManagerFactory;
*/
public class SslContextFactory {
- private String provider = null;
-
- private String protocol = "TLSv1.2";
-
- private SecureRandom secureRandom = null;
-
- private KeyStore keyManagerFactoryKeyStore = null;
-
- private char[] keyManagerFactoryKeyStorePassword = null;
-
- private KeyManagerFactory keyManagerFactory = null;
-
- private String keyManagerFactoryAlgorithm = null;
-
- private String keyManagerFactoryProvider = null;
-
- private boolean keyManagerFactoryAlgorithmUseDefault = true;
-
- private KeyStore trustManagerFactoryKeyStore = null;
-
- private TrustManagerFactory trustManagerFactory = null;
-
- private String trustManagerFactoryAlgorithm = null;
-
- private String trustManagerFactoryProvider = null;
-
- private boolean trustManagerFactoryAlgorithmUseDefault = true;
-
- private ManagerFactoryParameters trustManagerFactoryParameters = null;
-
- private int clientSessionCacheSize = -1;
-
- private int clientSessionTimeout = -1;
-
- private int serverSessionCacheSize = -1;
-
- private int serverSessionTimeout = -1;
-
- /**
- * Create a new SSLContext instance,using the {@link KeyManagerFactory} and the
- * {@link TrustManagerFactory}.
- *
- * @return The created instance
- * @throws Exception If we weren't able to create the SSLContext insyance
- */
- public SSLContext newInstance() throws Exception {
- KeyManagerFactory kmf = this.keyManagerFactory;
- TrustManagerFactory tmf = this.trustManagerFactory;
-
- if (kmf == null) {
- String algorithm = keyManagerFactoryAlgorithm;
-
- if (algorithm == null && keyManagerFactoryAlgorithmUseDefault) {
- algorithm = KeyManagerFactory.getDefaultAlgorithm();
- }
-
- if (algorithm != null) {
- if (keyManagerFactoryProvider == null) {
- kmf = KeyManagerFactory.getInstance(algorithm);
- } else {
- kmf = KeyManagerFactory.getInstance(algorithm, keyManagerFactoryProvider);
- }
- }
- }
-
- if (tmf == null) {
- String algorithm = trustManagerFactoryAlgorithm;
-
- if (algorithm == null && trustManagerFactoryAlgorithmUseDefault) {
- algorithm = TrustManagerFactory.getDefaultAlgorithm();
- }
-
- if (algorithm != null) {
- if (trustManagerFactoryProvider == null) {
- tmf = TrustManagerFactory.getInstance(algorithm);
- } else {
- tmf = TrustManagerFactory.getInstance(algorithm, trustManagerFactoryProvider);
- }
- }
- }
-
- KeyManager[] keyManagers = null;
-
- if (kmf != null) {
- kmf.init(keyManagerFactoryKeyStore, keyManagerFactoryKeyStorePassword);
- keyManagers = kmf.getKeyManagers();
- }
-
- TrustManager[] trustManagers = null;
-
- if (tmf != null) {
- if (trustManagerFactoryParameters != null) {
- tmf.init(trustManagerFactoryParameters);
- } else {
- tmf.init(trustManagerFactoryKeyStore);
- }
-
- trustManagers = tmf.getTrustManagers();
- }
-
- SSLContext context;
-
- if (provider == null) {
- context = SSLContext.getInstance(protocol);
- } else {
- context = SSLContext.getInstance(protocol, provider);
- }
-
- context.init(keyManagers, trustManagers, secureRandom);
-
- if (clientSessionCacheSize >= 0) {
- context.getClientSessionContext().setSessionCacheSize(clientSessionCacheSize);
- }
-
- if (clientSessionTimeout >= 0) {
- context.getClientSessionContext().setSessionTimeout(clientSessionTimeout);
- }
-
- if (serverSessionCacheSize >= 0) {
- context.getServerSessionContext().setSessionCacheSize(serverSessionCacheSize);
- }
-
- if (serverSessionTimeout >= 0) {
- context.getServerSessionContext().setSessionTimeout(serverSessionTimeout);
- }
-
- return context;
- }
-
- /**
- * Sets the provider of the new {@link SSLContext}. The default value is
- * <tt>null</tt>, which means the default provider will be used.
- *
- * @param provider the name of the {@link SSLContext} provider
- */
- public void setProvider(String provider) {
- this.provider = provider;
- }
-
- /**
- * Sets the protocol to use when creating the {@link SSLContext}. The
- * default is <code>TLS</code>.
- *
- * @param protocol the name of the protocol.
- */
- public void setProtocol(String protocol) {
- if (protocol == null) {
- throw new IllegalArgumentException("protocol");
- }
-
- this.protocol = protocol;
- }
-
- /**
- * If this is set to <tt>true</tt> while no {@link KeyManagerFactory}
- * has been set using {@link #setKeyManagerFactory(KeyManagerFactory)} and
- * no algorithm has been set using
- * {@link #setKeyManagerFactoryAlgorithm(String)} the default algorithm
- * return by {@link KeyManagerFactory#getDefaultAlgorithm()} will be used.
- * The default value of this property is <tt>true</tt>.
- *
- * @param useDefault <tt>true</tt> or <tt>false</tt>.
- */
- public void setKeyManagerFactoryAlgorithmUseDefault(boolean useDefault) {
- this.keyManagerFactoryAlgorithmUseDefault = useDefault;
- }
-
- /**
- * If this is set to <tt>true</tt> while no {@link TrustManagerFactory}
- * has been set using {@link #setTrustManagerFactory(TrustManagerFactory)} and
- * no algorithm has been set using
- * {@link #setTrustManagerFactoryAlgorithm(String)} the default algorithm
- * return by {@link TrustManagerFactory#getDefaultAlgorithm()} will be used.
- * The default value of this property is <tt>true</tt>.
- *
- * @param useDefault <tt>true</tt> or <tt>false</tt>.
- */
- public void setTrustManagerFactoryAlgorithmUseDefault(boolean useDefault) {
- this.trustManagerFactoryAlgorithmUseDefault = useDefault;
- }
-
- /**
- * Sets the {@link KeyManagerFactory} to use. If this is set the properties
- * which are used by this factory bean to create a {@link KeyManagerFactory}
- * will all be ignored.
- *
- * @param factory the factory.
- */
- public void setKeyManagerFactory(KeyManagerFactory factory) {
- this.keyManagerFactory = factory;
- }
-
- /**
- * Sets the algorithm to use when creating the {@link KeyManagerFactory}
- * using {@link KeyManagerFactory#getInstance(java.lang.String)} or
- * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
- * <p>
- * This property will be ignored if a {@link KeyManagerFactory} has been
- * set directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
- * <p>
- * If this property isn't set while no {@link KeyManagerFactory} has been
- * set using {@link #setKeyManagerFactory(KeyManagerFactory)} and
- * {@link #setKeyManagerFactoryAlgorithmUseDefault(boolean)} has been set to
- * <tt>true</tt> the value returned
- * by {@link KeyManagerFactory#getDefaultAlgorithm()} will be used instead.
- *
- * @param algorithm the algorithm to use.
- */
- public void setKeyManagerFactoryAlgorithm(String algorithm) {
- this.keyManagerFactoryAlgorithm = algorithm;
- }
-
- /**
- * Sets the provider to use when creating the {@link KeyManagerFactory}
- * using
- * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
- * <p>
- * This property will be ignored if a {@link KeyManagerFactory} has been
- * set directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
- * <p>
- * If this property isn't set and no {@link KeyManagerFactory} has been set
- * using {@link #setKeyManagerFactory(KeyManagerFactory)}
- * {@link KeyManagerFactory#getInstance(java.lang.String)} will be used
- * to create the {@link KeyManagerFactory}.
- *
- * @param provider the name of the provider.
- */
- public void setKeyManagerFactoryProvider(String provider) {
- this.keyManagerFactoryProvider = provider;
- }
-
- /**
- * Sets the {@link KeyStore} which will be used in the call to
- * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when
- * the {@link SSLContext} is created.
- *
- * @param keyStore the key store.
- */
- public void setKeyManagerFactoryKeyStore(KeyStore keyStore) {
- this.keyManagerFactoryKeyStore = keyStore;
- }
-
- /**
- * Sets the password which will be used in the call to
- * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when
- * the {@link SSLContext} is created.
- *
- * @param password the password. Use <code>null</code> to disable password.
- */
- public void setKeyManagerFactoryKeyStorePassword(String password) {
- if (password != null) {
- this.keyManagerFactoryKeyStorePassword = password.toCharArray();
- } else {
- this.keyManagerFactoryKeyStorePassword = null;
- }
- }
-
- /**
- * Sets the {@link TrustManagerFactory} to use. If this is set the
- * properties which are used by this factory bean to create a
- * {@link TrustManagerFactory} will all be ignored.
- *
- * @param factory
- * the factory.
- */
- public void setTrustManagerFactory(TrustManagerFactory factory) {
- this.trustManagerFactory = factory;
- }
-
- /**
- * Sets the algorithm to use when creating the {@link TrustManagerFactory}
- * using {@link TrustManagerFactory#getInstance(java.lang.String)} or
- * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
- * <p>
- * This property will be ignored if a {@link TrustManagerFactory} has been
- * set directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
- * <p>
- * If this property isn't set while no {@link TrustManagerFactory} has been
- * set using {@link #setTrustManagerFactory(TrustManagerFactory)} and
- * {@link #setTrustManagerFactoryAlgorithmUseDefault(boolean)} has been set to
- * <tt>true</tt> the value returned
- * by {@link TrustManagerFactory#getDefaultAlgorithm()} will be used instead.
- *
- * @param algorithm the algorithm to use.
- */
- public void setTrustManagerFactoryAlgorithm(String algorithm) {
- this.trustManagerFactoryAlgorithm = algorithm;
- }
-
- /**
- * Sets the {@link KeyStore} which will be used in the call to
- * {@link TrustManagerFactory#init(java.security.KeyStore)} when
- * the {@link SSLContext} is created.
- * <p>
- * This property will be ignored if {@link ManagerFactoryParameters} has been
- * set directly using {@link #setTrustManagerFactoryParameters(ManagerFactoryParameters)}.
- *
- * @param keyStore the key store.
- */
- public void setTrustManagerFactoryKeyStore(KeyStore keyStore) {
- this.trustManagerFactoryKeyStore = keyStore;
- }
-
- /**
- * Sets the {@link ManagerFactoryParameters} which will be used in the call to
- * {@link TrustManagerFactory#init(javax.net.ssl.ManagerFactoryParameters)} when
- * the {@link SSLContext} is created.
- *
- * @param parameters describing provider-specific trust material.
- */
- public void setTrustManagerFactoryParameters(ManagerFactoryParameters parameters) {
- this.trustManagerFactoryParameters = parameters;
- }
-
- /**
- * Sets the provider to use when creating the {@link TrustManagerFactory}
- * using
- * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
- * <p>
- * This property will be ignored if a {@link TrustManagerFactory} has been
- * set directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
- * <p>
- * If this property isn't set and no {@link TrustManagerFactory} has been set
- * using {@link #setTrustManagerFactory(TrustManagerFactory)}
- * {@link TrustManagerFactory#getInstance(java.lang.String)} will be used
- * to create the {@link TrustManagerFactory}.
- *
- * @param provider the name of the provider.
- */
- public void setTrustManagerFactoryProvider(String provider) {
- this.trustManagerFactoryProvider = provider;
- }
-
- /**
- * Sets the {@link SecureRandom} to use when initializing the
- * {@link SSLContext}. The JVM's default will be used if this isn't set.
- *
- * @param secureRandom the {@link SecureRandom} or <code>null</code> if the
- * JVM's default should be used.
- * @see SSLContext#init(javax.net.ssl.KeyManager[], javax.net.ssl.TrustManager[], java.security.SecureRandom)
- */
- public void setSecureRandom(SecureRandom secureRandom) {
- this.secureRandom = secureRandom;
- }
-
- /**
- * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in client mode.
- *
- * @param size the new session cache size limit; zero means there is no limit.
- * @see SSLSessionContext#setSessionCacheSize(int size)
- */
- public void setClientSessionCacheSize(int size) {
- this.clientSessionCacheSize = size;
- }
-
- /**
- * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in client mode.
- *
- * @param seconds the new session timeout limit in seconds; zero means there is no limit.
- * @see SSLSessionContext#setSessionTimeout(int seconds)
- */
- public void setClientSessionTimeout(int seconds) {
- this.clientSessionTimeout = seconds;
- }
-
- /**
- * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in server mode.
- *
- * @param serverSessionCacheSize the new session cache size limit; zero means there is no limit.
- * @see SSLSessionContext#setSessionCacheSize(int)
- */
- public void setServerSessionCacheSize(int serverSessionCacheSize) {
- this.serverSessionCacheSize = serverSessionCacheSize;
- }
-
- /**
- * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in server mode.
- *
- * @param serverSessionTimeout the new session timeout limit in seconds; zero means there is no limit.
- * @see SSLSessionContext#setSessionTimeout(int)
- */
- public void setServerSessionTimeout(int serverSessionTimeout) {
- this.serverSessionTimeout = serverSessionTimeout;
- }
+ private String provider = null;
+
+ private String protocol = "TLSv1.2";
+
+ private SecureRandom secureRandom = null;
+
+ private KeyStore keyManagerFactoryKeyStore = null;
+
+ private char[] keyManagerFactoryKeyStorePassword = null;
+
+ private KeyManagerFactory keyManagerFactory = null;
+
+ private String keyManagerFactoryAlgorithm = null;
+
+ private String keyManagerFactoryProvider = null;
+
+ private boolean keyManagerFactoryAlgorithmUseDefault = true;
+
+ private KeyStore trustManagerFactoryKeyStore = null;
+
+ private TrustManagerFactory trustManagerFactory = null;
+
+ private String trustManagerFactoryAlgorithm = null;
+
+ private String trustManagerFactoryProvider = null;
+
+ private boolean trustManagerFactoryAlgorithmUseDefault = true;
+
+ private ManagerFactoryParameters trustManagerFactoryParameters = null;
+
+ private int clientSessionCacheSize = -1;
+
+ private int clientSessionTimeout = -1;
+
+ private int serverSessionCacheSize = -1;
+
+ private int serverSessionTimeout = -1;
+
+ /**
+ * Create a new SSLContext instance,using the {@link KeyManagerFactory} and the
+ * {@link TrustManagerFactory}.
+ *
+ * @return The created instance
+ * @throws Exception If we weren't able to create the SSLContext insyance
+ */
+ public SSLContext newInstance() throws Exception {
+ KeyManagerFactory kmf = this.keyManagerFactory;
+ TrustManagerFactory tmf = this.trustManagerFactory;
+
+ if (kmf == null) {
+ String algorithm = keyManagerFactoryAlgorithm;
+
+ if (algorithm == null && keyManagerFactoryAlgorithmUseDefault) {
+ algorithm = KeyManagerFactory.getDefaultAlgorithm();
+ }
+
+ if (algorithm != null) {
+ if (keyManagerFactoryProvider == null) {
+ kmf = KeyManagerFactory.getInstance(algorithm);
+ } else {
+ kmf = KeyManagerFactory.getInstance(algorithm, keyManagerFactoryProvider);
+ }
+ }
+ }
+
+ if (tmf == null) {
+ String algorithm = trustManagerFactoryAlgorithm;
+
+ if (algorithm == null && trustManagerFactoryAlgorithmUseDefault) {
+ algorithm = TrustManagerFactory.getDefaultAlgorithm();
+ }
+
+ if (algorithm != null) {
+ if (trustManagerFactoryProvider == null) {
+ tmf = TrustManagerFactory.getInstance(algorithm);
+ } else {
+ tmf = TrustManagerFactory.getInstance(algorithm, trustManagerFactoryProvider);
+ }
+ }
+ }
+
+ KeyManager[] keyManagers = null;
+
+ if (kmf != null) {
+ kmf.init(keyManagerFactoryKeyStore, keyManagerFactoryKeyStorePassword);
+ keyManagers = kmf.getKeyManagers();
+ }
+
+ TrustManager[] trustManagers = null;
+
+ if (tmf != null) {
+ if (trustManagerFactoryParameters != null) {
+ tmf.init(trustManagerFactoryParameters);
+ } else {
+ tmf.init(trustManagerFactoryKeyStore);
+ }
+
+ trustManagers = tmf.getTrustManagers();
+ }
+
+ SSLContext context;
+
+ if (provider == null) {
+ context = SSLContext.getInstance(protocol);
+ } else {
+ context = SSLContext.getInstance(protocol, provider);
+ }
+
+ context.init(keyManagers, trustManagers, secureRandom);
+
+ if (clientSessionCacheSize >= 0) {
+ context.getClientSessionContext().setSessionCacheSize(clientSessionCacheSize);
+ }
+
+ if (clientSessionTimeout >= 0) {
+ context.getClientSessionContext().setSessionTimeout(clientSessionTimeout);
+ }
+
+ if (serverSessionCacheSize >= 0) {
+ context.getServerSessionContext().setSessionCacheSize(serverSessionCacheSize);
+ }
+
+ if (serverSessionTimeout >= 0) {
+ context.getServerSessionContext().setSessionTimeout(serverSessionTimeout);
+ }
+
+ return context;
+ }
+
+ /**
+ * Sets the provider of the new {@link SSLContext}. The default value is
+ * <tt>null</tt>, which means the default provider will be used.
+ *
+ * @param provider the name of the {@link SSLContext} provider
+ */
+ public void setProvider(String provider) {
+ this.provider = provider;
+ }
+
+ /**
+ * Sets the protocol to use when creating the {@link SSLContext}. The default is
+ * <code>TLS</code>.
+ *
+ * @param protocol the name of the protocol.
+ */
+ public void setProtocol(String protocol) {
+ if (protocol == null) {
+ throw new IllegalArgumentException("protocol");
+ }
+
+ this.protocol = protocol;
+ }
+
+ /**
+ * If this is set to <tt>true</tt> while no {@link KeyManagerFactory} has been
+ * set using {@link #setKeyManagerFactory(KeyManagerFactory)} and no algorithm
+ * has been set using {@link #setKeyManagerFactoryAlgorithm(String)} the default
+ * algorithm return by {@link KeyManagerFactory#getDefaultAlgorithm()} will be
+ * used. The default value of this property is <tt>true</tt>.
+ *
+ * @param useDefault <tt>true</tt> or <tt>false</tt>.
+ */
+ public void setKeyManagerFactoryAlgorithmUseDefault(boolean useDefault) {
+ this.keyManagerFactoryAlgorithmUseDefault = useDefault;
+ }
+
+ /**
+ * If this is set to <tt>true</tt> while no {@link TrustManagerFactory} has been
+ * set using {@link #setTrustManagerFactory(TrustManagerFactory)} and no
+ * algorithm has been set using {@link #setTrustManagerFactoryAlgorithm(String)}
+ * the default algorithm return by
+ * {@link TrustManagerFactory#getDefaultAlgorithm()} will be used. The default
+ * value of this property is <tt>true</tt>.
+ *
+ * @param useDefault <tt>true</tt> or <tt>false</tt>.
+ */
+ public void setTrustManagerFactoryAlgorithmUseDefault(boolean useDefault) {
+ this.trustManagerFactoryAlgorithmUseDefault = useDefault;
+ }
+
+ /**
+ * Sets the {@link KeyManagerFactory} to use. If this is set the properties
+ * which are used by this factory bean to create a {@link KeyManagerFactory}
+ * will all be ignored.
+ *
+ * @param factory the factory.
+ */
+ public void setKeyManagerFactory(KeyManagerFactory factory) {
+ this.keyManagerFactory = factory;
+ }
+
+ /**
+ * Sets the algorithm to use when creating the {@link KeyManagerFactory} using
+ * {@link KeyManagerFactory#getInstance(java.lang.String)} or
+ * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+ * <p>
+ * This property will be ignored if a {@link KeyManagerFactory} has been set
+ * directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
+ * <p>
+ * If this property isn't set while no {@link KeyManagerFactory} has been set
+ * using {@link #setKeyManagerFactory(KeyManagerFactory)} and
+ * {@link #setKeyManagerFactoryAlgorithmUseDefault(boolean)} has been set to
+ * <tt>true</tt> the value returned by
+ * {@link KeyManagerFactory#getDefaultAlgorithm()} will be used instead.
+ *
+ * @param algorithm the algorithm to use.
+ */
+ public void setKeyManagerFactoryAlgorithm(String algorithm) {
+ this.keyManagerFactoryAlgorithm = algorithm;
+ }
+
+ /**
+ * Sets the provider to use when creating the {@link KeyManagerFactory} using
+ * {@link KeyManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+ * <p>
+ * This property will be ignored if a {@link KeyManagerFactory} has been set
+ * directly using {@link #setKeyManagerFactory(KeyManagerFactory)}.
+ * <p>
+ * If this property isn't set and no {@link KeyManagerFactory} has been set
+ * using {@link #setKeyManagerFactory(KeyManagerFactory)}
+ * {@link KeyManagerFactory#getInstance(java.lang.String)} will be used to
+ * create the {@link KeyManagerFactory}.
+ *
+ * @param provider the name of the provider.
+ */
+ public void setKeyManagerFactoryProvider(String provider) {
+ this.keyManagerFactoryProvider = provider;
+ }
+
+ /**
+ * Sets the {@link KeyStore} which will be used in the call to
+ * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when the
+ * {@link SSLContext} is created.
+ *
+ * @param keyStore the key store.
+ */
+ public void setKeyManagerFactoryKeyStore(KeyStore keyStore) {
+ this.keyManagerFactoryKeyStore = keyStore;
+ }
+
+ /**
+ * Sets the password which will be used in the call to
+ * {@link KeyManagerFactory#init(java.security.KeyStore, char[])} when the
+ * {@link SSLContext} is created.
+ *
+ * @param password the password. Use <code>null</code> to disable password.
+ */
+ public void setKeyManagerFactoryKeyStorePassword(String password) {
+ if (password != null) {
+ this.keyManagerFactoryKeyStorePassword = password.toCharArray();
+ } else {
+ this.keyManagerFactoryKeyStorePassword = null;
+ }
+ }
+
+ /**
+ * Sets the {@link TrustManagerFactory} to use. If this is set the properties
+ * which are used by this factory bean to create a {@link TrustManagerFactory}
+ * will all be ignored.
+ *
+ * @param factory the factory.
+ */
+ public void setTrustManagerFactory(TrustManagerFactory factory) {
+ this.trustManagerFactory = factory;
+ }
+
+ /**
+ * Sets the algorithm to use when creating the {@link TrustManagerFactory} using
+ * {@link TrustManagerFactory#getInstance(java.lang.String)} or
+ * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+ * <p>
+ * This property will be ignored if a {@link TrustManagerFactory} has been set
+ * directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
+ * <p>
+ * If this property isn't set while no {@link TrustManagerFactory} has been set
+ * using {@link #setTrustManagerFactory(TrustManagerFactory)} and
+ * {@link #setTrustManagerFactoryAlgorithmUseDefault(boolean)} has been set to
+ * <tt>true</tt> the value returned by
+ * {@link TrustManagerFactory#getDefaultAlgorithm()} will be used instead.
+ *
+ * @param algorithm the algorithm to use.
+ */
+ public void setTrustManagerFactoryAlgorithm(String algorithm) {
+ this.trustManagerFactoryAlgorithm = algorithm;
+ }
+
+ /**
+ * Sets the {@link KeyStore} which will be used in the call to
+ * {@link TrustManagerFactory#init(java.security.KeyStore)} when the
+ * {@link SSLContext} is created.
+ * <p>
+ * This property will be ignored if {@link ManagerFactoryParameters} has been
+ * set directly using
+ * {@link #setTrustManagerFactoryParameters(ManagerFactoryParameters)}.
+ *
+ * @param keyStore the key store.
+ */
+ public void setTrustManagerFactoryKeyStore(KeyStore keyStore) {
+ this.trustManagerFactoryKeyStore = keyStore;
+ }
+
+ /**
+ * Sets the {@link ManagerFactoryParameters} which will be used in the call to
+ * {@link TrustManagerFactory#init(javax.net.ssl.ManagerFactoryParameters)} when
+ * the {@link SSLContext} is created.
+ *
+ * @param parameters describing provider-specific trust material.
+ */
+ public void setTrustManagerFactoryParameters(ManagerFactoryParameters parameters) {
+ this.trustManagerFactoryParameters = parameters;
+ }
+
+ /**
+ * Sets the provider to use when creating the {@link TrustManagerFactory} using
+ * {@link TrustManagerFactory#getInstance(java.lang.String, java.lang.String)}.
+ * <p>
+ * This property will be ignored if a {@link TrustManagerFactory} has been set
+ * directly using {@link #setTrustManagerFactory(TrustManagerFactory)}.
+ * <p>
+ * If this property isn't set and no {@link TrustManagerFactory} has been set
+ * using {@link #setTrustManagerFactory(TrustManagerFactory)}
+ * {@link TrustManagerFactory#getInstance(java.lang.String)} will be used to
+ * create the {@link TrustManagerFactory}.
+ *
+ * @param provider the name of the provider.
+ */
+ public void setTrustManagerFactoryProvider(String provider) {
+ this.trustManagerFactoryProvider = provider;
+ }
+
+ /**
+ * Sets the {@link SecureRandom} to use when initializing the
+ * {@link SSLContext}. The JVM's default will be used if this isn't set.
+ *
+ * @param secureRandom the {@link SecureRandom} or <code>null</code> if the
+ * JVM's default should be used.
+ * @see SSLContext#init(javax.net.ssl.KeyManager[],
+ * javax.net.ssl.TrustManager[], java.security.SecureRandom)
+ */
+ public void setSecureRandom(SecureRandom secureRandom) {
+ this.secureRandom = secureRandom;
+ }
+
+ /**
+ * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in
+ * client mode.
+ *
+ * @param size the new session cache size limit; zero means there is no limit.
+ * @see SSLSessionContext#setSessionCacheSize(int size)
+ */
+ public void setClientSessionCacheSize(int size) {
+ this.clientSessionCacheSize = size;
+ }
+
+ /**
+ * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in
+ * client mode.
+ *
+ * @param seconds the new session timeout limit in seconds; zero means there is
+ * no limit.
+ * @see SSLSessionContext#setSessionTimeout(int seconds)
+ */
+ public void setClientSessionTimeout(int seconds) {
+ this.clientSessionTimeout = seconds;
+ }
+
+ /**
+ * Sets the SSLSession cache size for the {@link SSLSessionContext} for use in
+ * server mode.
+ *
+ * @param serverSessionCacheSize the new session cache size limit; zero means
+ * there is no limit.
+ * @see SSLSessionContext#setSessionCacheSize(int)
+ */
+ public void setServerSessionCacheSize(int serverSessionCacheSize) {
+ this.serverSessionCacheSize = serverSessionCacheSize;
+ }
+
+ /**
+ * Set the SSLSession timeout limit for the {@link SSLSessionContext} for use in
+ * server mode.
+ *
+ * @param serverSessionTimeout the new session timeout limit in seconds; zero
+ * means there is no limit.
+ * @see SSLSessionContext#setSessionTimeout(int)
+ */
+ public void setServerSessionTimeout(int serverSessionTimeout) {
+ this.serverSessionTimeout = serverSessionTimeout;
+ }
}
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
index 060d313..e1c497d 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslEvent.java
@@ -22,11 +22,11 @@ package org.apache.mina.filter.ssl;
import org.apache.mina.filter.FilterEvent;
/**
- * A SSL event sent by {@link SslFilter} when the session is secured or not secured.
+ * A SSL event sent by {@link SslFilter} when the session is secured or not
+ * secured.
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
public enum SslEvent implements FilterEvent {
- SECURED,
- UNSECURED
+ SECURED, UNSECURED
}
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
index 45d124e..a55995a 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslFilter.java
@@ -49,37 +49,37 @@ 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.
+ * 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.
+ * 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.
*
* <h2>Implementing StartTLS</h2>
* <p>
* You can use {@link #DISABLE_ENCRYPTION_ONCE} attribute to implement StartTLS:
+ *
* <pre>
* public void messageReceived(IoSession session, Object message) {
- * if (message instanceof MyStartTLSRequest) {
- * // Insert SSLFilter to get ready for handshaking
- * session.getFilterChain().addFirst(sslFilter);
+ * if (message instanceof MyStartTLSRequest) {
+ * // Insert SSLFilter to get ready for handshaking
+ * session.getFilterChain().addFirst(sslFilter);
*
- * // Disable encryption temporarily.
- * // This attribute will be removed by SSLFilter
- * // inside the Session.write() call below.
- * session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
+ * // Disable encryption temporarily.
+ * // This attribute will be removed by SSLFilter
+ * // inside the Session.write() call below.
+ * session.setAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE, Boolean.TRUE);
*
- * // Write StartTLSResponse which won't be encrypted.
- * session.write(new MyStartTLSResponse(OK));
+ * // Write StartTLSResponse which won't be encrypted.
+ * session.write(new MyStartTLSResponse(OK));
*
- * // Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
- * assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
- * }
+ * // Now DISABLE_ENCRYPTION_ONCE attribute is cleared.
+ * assert session.getAttribute(SSLFilter.DISABLE_ENCRYPTION_ONCE) == null;
+ * }
* }
* </pre>
*
@@ -87,812 +87,817 @@ import org.slf4j.LoggerFactory;
* @org.apache.xbean.XBean
*/
public class SslFilter extends IoFilterAdapter {
- /** The logger */
- private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class);
-
- /**
- * A session attribute key that stores underlying {@link SSLSession}
- * for each session.
- */
- public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
-
- /**
- * A session attribute key that makes next one write request bypass
- * this filter (not encrypting the data). This is a marker attribute,
- * which means that you can put whatever as its value. ({@link Boolean#TRUE}
- * is preferred.) The attribute is automatically removed from the session
- * attribute map as soon as {@link IoSession#write(Object)} is invoked,
- * and therefore should be put again if you want to make more messages
- * bypass this filter. This is especially useful when you implement
- * StartTLS.
- */
- public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
-
- /**
- * A session attribute key that makes this filter to emit a
- * {@link IoHandler#messageReceived(IoSession, Object)} event with a
- * special message ({@link SslEvent#SECURED} or {@link SslEvent#UNSECURED}).
- * This is a marker attribute, which means that you can put whatever as its
- * value. ({@link Boolean#TRUE} is preferred.) By default, this filter
- * doesn't emit any events related with SSL session flow control.
- */
- public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
-
- /**
- * A session attribute key that should be set to an {@link InetSocketAddress}.
- * Setting this attribute causes
- * {@link SSLContext#createSSLEngine(String, int)} to be called passing the
- * hostname and port of the {@link InetSocketAddress} to get an
- * {@link SSLEngine} instance. If not set {@link SSLContext#createSSLEngine()}
- * will be called.
- * <br>
- * Using this feature {@link SSLSession} objects may be cached and reused
- * when in client mode.
- *
- * @see SSLContext#createSSLEngine(String, int)
- */
- public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
-
- /** An attribute containing the next filter */
- private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
-
- private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
-
- /** The SslContext used */
- /* No qualifier */final SSLContext sslContext;
-
- /** A flag used to tell the filter to start the handshake immediately */
- private final boolean autoStart;
-
- /** A flag used to determinate if the handshake should start immediately */
- public static final boolean START_HANDSHAKE = true;
-
- /** A flag used to determinate if the handshake should wait for the client to initiate the handshake */
- public static final boolean CLIENT_HANDSHAKE = false;
-
- private boolean client;
-
- private boolean needClientAuth;
-
- private boolean wantClientAuth;
-
- private String[] enabledCipherSuites;
-
- private String[] enabledProtocols;
-
- /**
- * Creates a new SSL filter using the specified {@link SSLContext}.
- * The handshake will start immediately after the filter has been added
- * to the chain.
- *
- * @param sslContext The SSLContext to use
- */
- public SslFilter(SSLContext sslContext) {
- this(sslContext, START_HANDSHAKE);
- }
-
- /**
- * Creates a new SSL filter using the specified {@link SSLContext}.
- * If the <tt>autostart</tt> flag is set to <tt>true</tt>, the
- * handshake will start immediately after the filter has been added
- * to the chain.
- *
- * @param sslContext The SSLContext to use
- * @param autoStart The flag used to tell the filter to start the handshake immediately
- */
- public SslFilter(SSLContext sslContext, boolean autoStart) {
- if (sslContext == null) {
- throw new IllegalArgumentException("sslContext");
- }
-
- this.sslContext = sslContext;
- this.autoStart = autoStart;
- }
-
- /**
- * Returns the underlying {@link SSLSession} for the specified session.
- *
- * @param session The current session
- * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
- */
- public SSLSession getSslSession(IoSession session) {
- return (SSLSession) session.getAttribute(SSL_SESSION);
- }
-
- /**
- * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
- * Please note that SSL session is automatically started by default, and therefore
- * you don't need to call this method unless you've used TLS closure.
- *
- * @param session The session that will be switched to SSL mode
- * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if already started.
- * @throws SSLException if failed to start the SSL session
- */
- public boolean startSsl(IoSession session) throws SSLException {
- SslHandler sslHandler = getSslSessionHandler(session);
- boolean started;
-
- try {
- synchronized (sslHandler) {
- if (sslHandler.isOutboundDone()) {
- NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
- sslHandler.destroy();
- sslHandler.init();
- sslHandler.handshake(nextFilter);
- started = true;
- } else {
- started = false;
- }
- sslHandler.flushFilterWrite();
- }
- sslHandler.flushMessageReceived();
- } catch (SSLException se) {
- sslHandler.release();
- throw se;
- }
-
- return started;
- }
-
- /**
- * An extended toString() method for sessions. If the SSL handshake
- * is not yet completed, we will print (ssl) in small caps. Once it's
- * completed, we will use SSL capitalized.
- */
- /* no qualifier */String getSessionInfo(IoSession session) {
- StringBuilder sb = new StringBuilder();
-
- if (session.getService() instanceof IoAcceptor) {
- sb.append("Session Server");
-
- } else {
- sb.append("Session Client");
- }
-
- sb.append('[').append(session.getId()).append(']');
-
- SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
- if (sslHandler == null) {
- sb.append("(no sslEngine)");
- } else if (isSslStarted(session)) {
- if (sslHandler.isHandshakeComplete()) {
- sb.append("(SSL)");
- } else {
- sb.append("(ssl...)");
- }
- }
-
- return sb.toString();
- }
-
- /**
- * @return <tt>true</tt> if and only if the specified <tt>session</tt> is
- * encrypted/decrypted over SSL/TLS currently. This method will start
- * to return <tt>false</tt> after TLS <tt>close_notify</tt> message
- * is sent and any messages written after then is not going to get encrypted.
- *
- * @param session the session we want to check
- */
- public boolean isSslStarted(IoSession session) {
- SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
- if (sslHandler == null) {
- return false;
- }
-
- synchronized (sslHandler) {
- return !sslHandler.isOutboundDone();
- }
- }
-
- /**
- * @return <tt>true</tt> if and only if the conditions for
- * {@link #isSslStarted(IoSession)} are met, and the handhake has
- * completed.
- *
- * @param session the session we want to check
- */
- public boolean isSecured(IoSession session) {
- SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
- if (sslHandler == null) {
- return false;
- }
-
- synchronized (sslHandler) {
- return !sslHandler.isOutboundDone() && sslHandler.isHandshakeComplete();
- }
- }
-
-
- /**
- * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
- * initiate TLS closure.
- *
- * @param session the {@link IoSession} to initiate TLS closure
- * @return The Future for the initiated closure
- * @throws SSLException if failed to initiate TLS closure
- */
- public WriteFuture stopSsl(IoSession session) throws SSLException {
- SslHandler sslHandler = getSslSessionHandler(session);
- NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
- WriteFuture future;
-
- try {
- synchronized (sslHandler) {
- future = initiateClosure(nextFilter, session);
- sslHandler.flushFilterWrite();
- }
- } catch (SSLException se) {
- sslHandler.release();
- throw se;
- }
-
- return future;
- }
-
- /**
- * @return <tt>true</tt> if the engine is set to use client mode
- * when handshaking.
- */
- public boolean isUseClientMode() {
- return client;
- }
-
- /**
- * Configures the engine to use client (or server) mode when handshaking.
- *
- * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt> when in server mode
- */
- public void setUseClientMode(boolean clientMode) {
- this.client = clientMode;
- }
-
- /**
- * @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 needClientAuth;
- }
-
- /**
- * 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.needClientAuth = 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 wantClientAuth;
- }
-
- /**
- * 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.wantClientAuth = 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 enabledCipherSuites;
- }
-
- /**
- * 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.enabledCipherSuites = 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 enabledProtocols;
- }
-
- /**
- * 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.enabledProtocols = 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 nextFilter) throws SSLException {
- // Check that we don't have a SSL filter already present in the chain
- if (parent.contains(SslFilter.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);
- }
-
- IoSession session = parent.getSession();
- session.setAttribute(NEXT_FILTER, nextFilter);
-
- // Create a SSL handler and start handshake.
- SslHandler sslHandler = new SslHandler(this, session);
-
- // Adding the supported ciphers in the SSLHandler
- if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) {
- enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
- }
-
- sslHandler.init();
-
- session.setAttribute(SSL_HANDLER, sslHandler);
- }
-
- @Override
- public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
- if (autoStart == START_HANDSHAKE) {
- initiateHandshake(nextFilter, parent.getSession());
- }
- }
-
- @Override
- public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
- IoSession session = parent.getSession();
- stopSsl(session);
- session.removeAttribute(NEXT_FILTER);
- session.removeAttribute(SSL_HANDLER);
- }
-
- // IoFilter impl.
- @Override
- public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
- SslHandler sslHandler = getSslSessionHandler(session);
-
- try {
- synchronized (sslHandler) {
- // release resources
- sslHandler.destroy();
- }
- } finally {
- // notify closed session
- nextFilter.sessionClosed(session);
- }
- }
-
- @Override
- public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
- }
-
- SslHandler sslHandler = getSslSessionHandler(session);
-
- synchronized (sslHandler) {
- if (!isSslStarted(session) && sslHandler.isInboundDone()) {
- // The SSL session must be established first before we
- // can push data to the application. Store the incoming
- // data into a queue for a later processing
- sslHandler.scheduleMessageReceived(nextFilter, message);
- } else {
- IoBuffer buf = (IoBuffer) message;
+ /** The logger */
+ private static final Logger LOGGER = LoggerFactory.getLogger(SslFilter.class);
+
+ /**
+ * A session attribute key that stores underlying {@link SSLSession} for each
+ * session.
+ */
+ public static final AttributeKey SSL_SESSION = new AttributeKey(SslFilter.class, "session");
+
+ /**
+ * A session attribute key that makes next one write request bypass this filter
+ * (not encrypting the data). This is a marker attribute, which means that you
+ * can put whatever as its value. ({@link Boolean#TRUE} is preferred.) The
+ * attribute is automatically removed from the session attribute map as soon as
+ * {@link IoSession#write(Object)} is invoked, and therefore should be put again
+ * if you want to make more messages bypass this filter. This is especially
+ * useful when you implement StartTLS.
+ */
+ public static final AttributeKey DISABLE_ENCRYPTION_ONCE = new AttributeKey(SslFilter.class, "disableOnce");
+
+ /**
+ * A session attribute key that makes this filter to emit a
+ * {@link IoHandler#messageReceived(IoSession, Object)} event with a special
+ * message ({@link SslEvent#SECURED} or {@link SslEvent#UNSECURED}). This is a
+ * marker attribute, which means that you can put whatever as its value.
+ * ({@link Boolean#TRUE} is preferred.) By default, this filter doesn't emit any
+ * events related with SSL session flow control.
+ */
+ public static final AttributeKey USE_NOTIFICATION = new AttributeKey(SslFilter.class, "useNotification");
+
+ /**
+ * A session attribute key that should be set to an {@link InetSocketAddress}.
+ * Setting this attribute causes {@link SSLContext#createSSLEngine(String, int)}
+ * to be called passing the hostname and port of the {@link InetSocketAddress}
+ * to get an {@link SSLEngine} instance. If not set
+ * {@link SSLContext#createSSLEngine()} will be called. <br>
+ * Using this feature {@link SSLSession} objects may be cached and reused when
+ * in client mode.
+ *
+ * @see SSLContext#createSSLEngine(String, int)
+ */
+ public static final AttributeKey PEER_ADDRESS = new AttributeKey(SslFilter.class, "peerAddress");
+
+ /** An attribute containing the next filter */
+ private static final AttributeKey NEXT_FILTER = new AttributeKey(SslFilter.class, "nextFilter");
+
+ private static final AttributeKey SSL_HANDLER = new AttributeKey(SslFilter.class, "handler");
+
+ /** The SslContext used */
+ /* No qualifier */final SSLContext sslContext;
+
+ /** A flag used to tell the filter to start the handshake immediately */
+ private final boolean autoStart;
+
+ /** A flag used to determinate if the handshake should start immediately */
+ public static final boolean START_HANDSHAKE = true;
+
+ /**
+ * A flag used to determinate if the handshake should wait for the client to
+ * initiate the handshake
+ */
+ public static final boolean CLIENT_HANDSHAKE = false;
+
+ private boolean client;
+
+ private boolean needClientAuth;
+
+ private boolean wantClientAuth;
+
+ private String[] enabledCipherSuites;
+
+ private String[] enabledProtocols;
+
+ /**
+ * Creates a new SSL filter using the specified {@link SSLContext}. The
+ * handshake will start immediately after the filter has been added to the
+ * chain.
+ *
+ * @param sslContext The SSLContext to use
+ */
+ public SslFilter(SSLContext sslContext) {
+ this(sslContext, START_HANDSHAKE);
+ }
+
+ /**
+ * Creates a new SSL filter using the specified {@link SSLContext}. If the
+ * <tt>autostart</tt> flag is set to <tt>true</tt>, the handshake will start
+ * immediately after the filter has been added to the chain.
+ *
+ * @param sslContext The SSLContext to use
+ * @param autoStart The flag used to tell the filter to start the handshake
+ * immediately
+ */
+ public SslFilter(SSLContext sslContext, boolean autoStart) {
+ if (sslContext == null) {
+ throw new IllegalArgumentException("sslContext");
+ }
+
+ this.sslContext = sslContext;
+ this.autoStart = autoStart;
+ }
+
+ /**
+ * Returns the underlying {@link SSLSession} for the specified session.
+ *
+ * @param session The current session
+ * @return <tt>null</tt> if no {@link SSLSession} is initialized yet.
+ */
+ public SSLSession getSslSession(IoSession session) {
+ return (SSLSession) session.getAttribute(SSL_SESSION);
+ }
+
+ /**
+ * (Re)starts SSL session for the specified <tt>session</tt> if not started yet.
+ * Please note that SSL session is automatically started by default, and
+ * therefore you don't need to call this method unless you've used TLS closure.
+ *
+ * @param session The session that will be switched to SSL mode
+ * @return <tt>true</tt> if the SSL session has been started, <tt>false</tt> if
+ * already started.
+ * @throws SSLException if failed to start the SSL session
+ */
+ public boolean startSsl(IoSession session) throws SSLException {
+ SslHandler sslHandler = getSslSessionHandler(session);
+ boolean started;
+
+ try {
+ synchronized (sslHandler) {
+ if (sslHandler.isOutboundDone()) {
+ NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
+ sslHandler.destroy();
+ sslHandler.init();
+ sslHandler.handshake(nextFilter);
+ started = true;
+ } else {
+ started = false;
+ }
+ sslHandler.flushFilterWrite();
+ }
+ sslHandler.flushMessageReceived();
+ } catch (SSLException se) {
+ sslHandler.release();
+ throw se;
+ }
+
+ return started;
+ }
+
+ /**
+ * An extended toString() method for sessions. If the SSL handshake is not yet
+ * completed, we will print (ssl) in small caps. Once it's completed, we will
+ * use SSL capitalized.
+ */
+ /* no qualifier */String getSessionInfo(IoSession session) {
+ StringBuilder sb = new StringBuilder();
+
+ if (session.getService() instanceof IoAcceptor) {
+ sb.append("Session Server");
+
+ } else {
+ sb.append("Session Client");
+ }
+
+ sb.append('[').append(session.getId()).append(']');
+
+ SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+ if (sslHandler == null) {
+ sb.append("(no sslEngine)");
+ } else if (isSslStarted(session)) {
+ if (sslHandler.isHandshakeComplete()) {
+ sb.append("(SSL)");
+ } else {
+ sb.append("(ssl...)");
+ }
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * @return <tt>true</tt> if and only if the specified <tt>session</tt> is
+ * encrypted/decrypted over SSL/TLS currently. This method will start to
+ * return <tt>false</tt> after TLS <tt>close_notify</tt> message is sent
+ * and any messages written after then is not going to get encrypted.
+ *
+ * @param session the session we want to check
+ */
+ public boolean isSslStarted(IoSession session) {
+ SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+ if (sslHandler == null) {
+ return false;
+ }
+
+ synchronized (sslHandler) {
+ return !sslHandler.isOutboundDone();
+ }
+ }
+
+ /**
+ * @return <tt>true</tt> if and only if the conditions for
+ * {@link #isSslStarted(IoSession)} are met, and the handhake has
+ * completed.
+ *
+ * @param session the session we want to check
+ */
+ public boolean isSecured(IoSession session) {
+ SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+ if (sslHandler == null) {
+ return false;
+ }
+
+ synchronized (sslHandler) {
+ return !sslHandler.isOutboundDone() && sslHandler.isHandshakeComplete();
+ }
+ }
+
+ /**
+ * Stops the SSL session by sending TLS <tt>close_notify</tt> message to
+ * initiate TLS closure.
+ *
+ * @param session the {@link IoSession} to initiate TLS closure
+ * @return The Future for the initiated closure
+ * @throws SSLException if failed to initiate TLS closure
+ */
+ public WriteFuture stopSsl(IoSession session) throws SSLException {
+ SslHandler sslHandler = getSslSessionHandler(session);
+ NextFilter nextFilter = (NextFilter) session.getAttribute(NEXT_FILTER);
+ WriteFuture future;
+
+ try {
+ synchronized (sslHandler) {
+ future = initiateClosure(nextFilter, session);
+ sslHandler.flushFilterWrite();
+ }
+ } catch (SSLException se) {
+ sslHandler.release();
+ throw se;
+ }
+
+ return future;
+ }
+
+ /**
+ * @return <tt>true</tt> if the engine is set to use client mode when
+ * handshaking.
+ */
+ public boolean isUseClientMode() {
+ return client;
+ }
+
+ /**
+ * Configures the engine to use client (or server) mode when handshaking.
+ *
+ * @param clientMode <tt>true</tt> when we are in client mode, <tt>false</tt>
+ * when in server mode
+ */
+ public void setUseClientMode(boolean clientMode) {
+ this.client = clientMode;
+ }
+
+ /**
+ * @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 needClientAuth;
+ }
+
+ /**
+ * 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.needClientAuth = 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 wantClientAuth;
+ }
+
+ /**
+ * 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.wantClientAuth = 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 enabledCipherSuites;
+ }
+
+ /**
+ * 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.enabledCipherSuites = 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 enabledProtocols;
+ }
+
+ /**
+ * 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.enabledProtocols = 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 nextFilter) throws SSLException {
+ // Check that we don't have a SSL filter already present in the chain
+ if (parent.contains(SslFilter.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);
+ }
+
+ IoSession session = parent.getSession();
+ session.setAttribute(NEXT_FILTER, nextFilter);
+
+ // Create a SSL handler and start handshake.
+ SslHandler sslHandler = new SslHandler(this, session);
+
+ // Adding the supported ciphers in the SSLHandler
+ if ((enabledCipherSuites == null) || (enabledCipherSuites.length == 0)) {
+ enabledCipherSuites = sslContext.getServerSocketFactory().getSupportedCipherSuites();
+ }
+
+ sslHandler.init();
+
+ session.setAttribute(SSL_HANDLER, sslHandler);
+ }
+
+ @Override
+ public void onPostAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
+ if (autoStart == START_HANDSHAKE) {
+ initiateHandshake(nextFilter, parent.getSession());
+ }
+ }
+
+ @Override
+ public void onPreRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws SSLException {
+ IoSession session = parent.getSession();
+ stopSsl(session);
+ session.removeAttribute(NEXT_FILTER);
+ session.removeAttribute(SSL_HANDLER);
+ }
+
+ // IoFilter impl.
+ @Override
+ public void sessionClosed(NextFilter nextFilter, IoSession session) throws SSLException {
+ SslHandler sslHandler = getSslSessionHandler(session);
try {
- if (sslHandler.isOutboundDone()) {
- sslHandler.destroy();
- throw new SSLException("Outbound done");
- }
+ synchronized (sslHandler) {
+ // release resources
+ sslHandler.destroy();
+ }
+ } finally {
+ // notify closed session
+ nextFilter.sessionClosed(session);
+ }
+ }
- // forward read encrypted data to SSL handler
- sslHandler.messageReceived(nextFilter, buf.buf());
+ @Override
+ public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws SSLException {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{}: Message received : {}", getSessionInfo(session), message);
+ }
- // Handle data to be forwarded to application or written to net
- handleSslData(nextFilter, sslHandler);
+ SslHandler sslHandler = getSslSessionHandler(session);
- if (sslHandler.isInboundDone()) {
- if (sslHandler.isOutboundDone()) {
- sslHandler.destroy();
+ synchronized (sslHandler) {
+ if (!isSslStarted(session) && sslHandler.isInboundDone()) {
+ // The SSL session must be established first before we
+ // can push data to the application. Store the incoming
+ // data into a queue for a later processing
+ sslHandler.scheduleMessageReceived(nextFilter, message);
} else {
- initiateClosure(nextFilter, session);
+ IoBuffer buf = (IoBuffer) message;
+
+ try {
+ if (sslHandler.isOutboundDone()) {
+ sslHandler.destroy();
+ throw new SSLException("Outbound done");
+ }
+
+ // forward read encrypted data to SSL handler
+ sslHandler.messageReceived(nextFilter, buf.buf());
+
+ // Handle data to be forwarded to application or written to net
+ handleSslData(nextFilter, sslHandler);
+
+ if (sslHandler.isInboundDone()) {
+ if (sslHandler.isOutboundDone()) {
+ sslHandler.destroy();
+ } else {
+ initiateClosure(nextFilter, session);
+ }
+
+ if (buf.hasRemaining()) {
+ // Forward the data received after closure.
+ sslHandler.scheduleMessageReceived(nextFilter, buf);
+ }
+ }
+ } catch (SSLException ssle) {
+ if (!sslHandler.isHandshakeComplete()) {
+ SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
+ newSsle.initCause(ssle);
+ ssle = newSsle;
+
+ // Close the session immediately, the handshake has failed
+ session.closeNow();
+ } else {
+ // Free the SSL Handler buffers
+ sslHandler.release();
+ }
+
+ throw ssle;
+ }
+ }
+ }
+
+ sslHandler.flushMessageReceived();
+ }
+
+ @Override
+ public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
+ if (writeRequest instanceof EncryptedWriteRequest) {
+ EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
+ nextFilter.messageSent(session, wrappedRequest.getParentRequest());
+ } else {
+ // ignore extra buffers used for handshaking
+ }
+ }
+
+ @Override
+ public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
+
+ if (cause instanceof WriteToClosedSessionException) {
+ // Filter out SSL close notify, which is likely to fail to flush
+ // due to disconnection.
+ WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
+ List<WriteRequest> failedRequests = e.getRequests();
+ boolean containsCloseNotify = false;
+
+ for (WriteRequest r : failedRequests) {
+ if (isCloseNotify(r.getMessage())) {
+ containsCloseNotify = true;
+ break;
+ }
+ }
+
+ if (containsCloseNotify) {
+ if (failedRequests.size() == 1) {
+ // close notify is the only failed request; bail out.
+ return;
+ }
+
+ List<WriteRequest> newFailedRequests = new ArrayList<>(failedRequests.size() - 1);
+
+ for (WriteRequest r : failedRequests) {
+ if (!isCloseNotify(r.getMessage())) {
+ newFailedRequests.add(r);
+ }
+ }
+
+ if (newFailedRequests.isEmpty()) {
+ // the failedRequests were full with close notify; bail out.
+ return;
+ }
+
+ cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
+ }
+ }
+
+ nextFilter.exceptionCaught(session, cause);
+ }
+
+ private boolean isCloseNotify(Object message) {
+ if (!(message instanceof IoBuffer)) {
+ return false;
+ }
+
+ IoBuffer buf = (IoBuffer) message;
+ int offset = buf.position();
+
+ return (buf.get(offset + 0) == 0x15) /* Alert */
+ && (buf.get(offset + 1) == 0x03) /* TLS/SSL */
+ && ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */
+ || (buf.get(offset + 2) == 0x01) /* TLS 1.0 */
+ || (buf.get(offset + 2) == 0x02) /* TLS 1.1 */
+ || (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */
+ && (buf.get(offset + 3) == 0x00); /* close_notify */
+ }
+
+ @Override
+ public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
+ }
+
+ boolean needsFlush = true;
+ SslHandler sslHandler = getSslSessionHandler(session);
+
+ try {
+ synchronized (sslHandler) {
+ if (!isSslStarted(session)) {
+ sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+ }
+ // Don't encrypt the data if encryption is disabled.
+ else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
+ // Remove the marker attribute because it is temporary.
+ session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
+ sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+ } else {
+ // Otherwise, encrypt the buffer.
+ IoBuffer buf = (IoBuffer) writeRequest.getMessage();
+
+ if (sslHandler.isWritingEncryptedData()) {
+ // data already encrypted; simply return buffer
+ sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
+ } else if (sslHandler.isHandshakeComplete()) {
+ // SSL encrypt
+ sslHandler.encrypt(buf.buf());
+ IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
+ writeRequest.setMessage(encryptedBuffer);
+ sslHandler.scheduleFilterWrite(nextFilter,
+ new EncryptedWriteRequest(writeRequest, encryptedBuffer));
+ } else {
+ if (session.isConnected()) {
+ // Handshake not complete yet.
+ sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
+ }
+
+ needsFlush = false;
+ }
+ }
+ if (needsFlush) {
+ sslHandler.flushFilterWrite();
+ }
+ }
+ } catch (SSLException se) {
+ sslHandler.release();
+ throw se;
+ }
+ }
+
+ @Override
+ public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
+ SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+ if (sslHandler == null) {
+ // The connection might already have closed, or
+ // SSL might have not started yet.
+ nextFilter.filterClose(session);
+ return;
+ }
+
+ WriteFuture future = null;
+
+ try {
+ synchronized (sslHandler) {
+ if (isSslStarted(session)) {
+ future = initiateClosure(nextFilter, session);
+ future.addListener(new IoFutureListener<IoFuture>() {
+ @Override
+ public void operationComplete(IoFuture future) {
+ nextFilter.filterClose(session);
+ }
+ });
+ }
+ sslHandler.flushFilterWrite();
+ }
+ } catch (SSLException se) {
+ sslHandler.release();
+ throw se;
+ } finally {
+ if (future == null) {
+ nextFilter.filterClose(session);
+ }
+ }
+ }
+
+ /**
+ * Initiate the SSL handshake. This can be invoked if you have set the
+ * 'autoStart' to false when creating the SslFilter instance.
+ *
+ * @param session The session for which the SSL handshake should be done
+ * @throws SSLException If the handshake failed
+ */
+ public void initiateHandshake(IoSession session) throws SSLException {
+ IoFilterChain filterChain = session.getFilterChain();
+
+ if (filterChain == null) {
+ throw new SSLException("No filter chain");
+ }
+
+ IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class);
+
+ if (nextFilter == null) {
+ throw new SSLException("No SSL next filter in the chain");
+ }
+
+ initiateHandshake(nextFilter, session);
+ }
+
+ private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
+ }
+
+ SslHandler sslHandler = getSslSessionHandler(session);
+
+ try {
+ synchronized (sslHandler) {
+ sslHandler.handshake(nextFilter);
+ sslHandler.flushFilterWrite();
}
+ sslHandler.flushMessageReceived();
+ } catch (SSLException se) {
+ sslHandler.release();
+ throw se;
+ }
+ }
+
+ private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
+ SslHandler sslHandler = getSslSessionHandler(session);
+ WriteFuture future = null;
- if (buf.hasRemaining()) {
- // Forward the data received after closure.
- sslHandler.scheduleMessageReceived(nextFilter, buf);
+ // if already shut down
+ try {
+ synchronized (sslHandler) {
+ if (!sslHandler.closeOutbound()) {
+ return DefaultWriteFuture.newNotWrittenFuture(session,
+ new IllegalStateException("SSL session is shut down already."));
+ }
+
+ // there might be data to write out here?
+ future = sslHandler.writeNetBuffer(nextFilter);
+
+ if (future == null) {
+ future = DefaultWriteFuture.newWrittenFuture(session);
+ }
+
+ if (sslHandler.isInboundDone()) {
+ sslHandler.destroy();
+ }
}
- }
- } catch (SSLException ssle) {
- if (!sslHandler.isHandshakeComplete()) {
- SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
- newSsle.initCause(ssle);
- ssle = newSsle;
-
- // Close the session immediately, the handshake has failed
- session.closeNow();
- } else {
- // Free the SSL Handler buffers
+
+ // Inform that the session is not any more secured
+ session.getFilterChain().fireEvent(SslEvent.UNSECURED);
+ } catch (SSLException se) {
sslHandler.release();
- }
-
- throw ssle;
- }
- }
- }
-
- sslHandler.flushMessageReceived();
- }
-
- @Override
- public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) {
- if (writeRequest instanceof EncryptedWriteRequest) {
- EncryptedWriteRequest wrappedRequest = (EncryptedWriteRequest) writeRequest;
- nextFilter.messageSent(session, wrappedRequest.getParentRequest());
- } else {
- // ignore extra buffers used for handshaking
- }
- }
-
- @Override
- public void exceptionCaught(NextFilter nextFilter, IoSession session, Throwable cause) throws Exception {
-
- if (cause instanceof WriteToClosedSessionException) {
- // Filter out SSL close notify, which is likely to fail to flush
- // due to disconnection.
- WriteToClosedSessionException e = (WriteToClosedSessionException) cause;
- List<WriteRequest> failedRequests = e.getRequests();
- boolean containsCloseNotify = false;
-
- for (WriteRequest r : failedRequests) {
- if (isCloseNotify(r.getMessage())) {
- containsCloseNotify = true;
- break;
- }
- }
-
- if (containsCloseNotify) {
- if (failedRequests.size() == 1) {
- // close notify is the only failed request; bail out.
- return;
- }
-
- List<WriteRequest> newFailedRequests = new ArrayList<>(failedRequests.size() - 1);
-
- for (WriteRequest r : failedRequests) {
- if (!isCloseNotify(r.getMessage())) {
- newFailedRequests.add(r);
- }
- }
-
- if (newFailedRequests.isEmpty()) {
- // the failedRequests were full with close notify; bail out.
- return;
- }
-
- cause = new WriteToClosedSessionException(newFailedRequests, cause.getMessage(), cause.getCause());
- }
- }
-
- nextFilter.exceptionCaught(session, cause);
- }
-
- private boolean isCloseNotify(Object message) {
- if (!(message instanceof IoBuffer)) {
- return false;
- }
-
- IoBuffer buf = (IoBuffer) message;
- int offset = buf.position();
-
- return (buf.get(offset + 0) == 0x15) /* Alert */
- && (buf.get(offset + 1) == 0x03) /* TLS/SSL */
- && ((buf.get(offset + 2) == 0x00) /* SSL 3.0 */
- || (buf.get(offset + 2) == 0x01) /* TLS 1.0 */
- || (buf.get(offset + 2) == 0x02) /* TLS 1.1 */
- || (buf.get(offset + 2) == 0x03)) /* TLS 1.2 */
- && (buf.get(offset + 3) == 0x00); /* close_notify */
- }
-
- @Override
- public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws SSLException {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{}: Writing Message : {}", getSessionInfo(session), writeRequest);
- }
-
- boolean needsFlush = true;
- SslHandler sslHandler = getSslSessionHandler(session);
-
- try {
- synchronized (sslHandler) {
- if (!isSslStarted(session)) {
- sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
- }
- // Don't encrypt the data if encryption is disabled.
- else if (session.containsAttribute(DISABLE_ENCRYPTION_ONCE)) {
- // Remove the marker attribute because it is temporary.
- session.removeAttribute(DISABLE_ENCRYPTION_ONCE);
- sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
- } else {
- // Otherwise, encrypt the buffer.
- IoBuffer buf = (IoBuffer) writeRequest.getMessage();
-
- if (sslHandler.isWritingEncryptedData()) {
- // data already encrypted; simply return buffer
- sslHandler.scheduleFilterWrite(nextFilter, writeRequest);
- } else if (sslHandler.isHandshakeComplete()) {
- // SSL encrypt
- sslHandler.encrypt(buf.buf());
- IoBuffer encryptedBuffer = sslHandler.fetchOutNetBuffer();
- writeRequest.setMessage( encryptedBuffer );
- sslHandler.scheduleFilterWrite(nextFilter, new EncryptedWriteRequest(writeRequest,
- encryptedBuffer));
- } else {
- if (session.isConnected()) {
- // Handshake not complete yet.
- sslHandler.schedulePreHandshakeWriteRequest(nextFilter, writeRequest);
- }
-
- needsFlush = false;
- }
- }
- if (needsFlush) {
- sslHandler.flushFilterWrite();
- }
- }
- } catch (SSLException se) {
- sslHandler.release();
- throw se;
- }
- }
-
- @Override
- public void filterClose(final NextFilter nextFilter, final IoSession session) throws SSLException {
- SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
- if (sslHandler == null) {
- // The connection might already have closed, or
- // SSL might have not started yet.
- nextFilter.filterClose(session);
- return;
- }
-
- WriteFuture future = null;
-
- try {
- synchronized (sslHandler) {
- if (isSslStarted(session)) {
- future = initiateClosure(nextFilter, session);
- future.addListener(new IoFutureListener<IoFuture>() {
- @Override
- public void operationComplete(IoFuture future) {
- nextFilter.filterClose(session);
- }
- });
- }
- sslHandler.flushFilterWrite();
- }
- } catch (SSLException se) {
- sslHandler.release();
- throw se;
- } finally {
- if (future == null) {
- nextFilter.filterClose(session);
- }
- }
- }
-
- /**
- * Initiate the SSL handshake. This can be invoked if you have set the 'autoStart' to
- * false when creating the SslFilter instance.
- *
- * @param session The session for which the SSL handshake should be done
- * @throws SSLException If the handshake failed
- */
- public void initiateHandshake(IoSession session) throws SSLException {
- IoFilterChain filterChain = session.getFilterChain();
-
- if (filterChain == null) {
- throw new SSLException("No filter chain");
- }
-
- IoFilter.NextFilter nextFilter = filterChain.getNextFilter(SslFilter.class);
-
- if (nextFilter == null) {
- throw new SSLException("No SSL next filter in the chain");
- }
-
- initiateHandshake(nextFilter, session);
- }
-
- private void initiateHandshake(NextFilter nextFilter, IoSession session) throws SSLException {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{} : Starting the first handshake", getSessionInfo(session));
- }
-
- SslHandler sslHandler = getSslSessionHandler(session);
-
- try {
- synchronized (sslHandler) {
- sslHandler.handshake(nextFilter);
- sslHandler.flushFilterWrite();
- }
- sslHandler.flushMessageReceived();
- } catch (SSLException se) {
- sslHandler.release();
- throw se;
- }
- }
-
- private WriteFuture initiateClosure(NextFilter nextFilter, IoSession session) throws SSLException {
- SslHandler sslHandler = getSslSessionHandler(session);
- WriteFuture future = null;
-
- // if already shut down
- try {
- synchronized(sslHandler) {
- if (!sslHandler.closeOutbound()) {
- return DefaultWriteFuture.newNotWrittenFuture(session, new IllegalStateException(
- "SSL session is shut down already."));
- }
-
- // there might be data to write out here?
- future = sslHandler.writeNetBuffer(nextFilter);
-
- if (future == null) {
- future = DefaultWriteFuture.newWrittenFuture(session);
- }
-
- if (sslHandler.isInboundDone()) {
- sslHandler.destroy();
- }
- }
-
- // Inform that the session is not any more secured
- session.getFilterChain().fireEvent(SslEvent.UNSECURED);
- } catch (SSLException se) {
- sslHandler.release();
- throw se;
- }
-
- return future;
- }
-
- // Utilities
- private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
- }
-
- // Flush any buffered write requests occurred before handshaking.
- if (sslHandler.isHandshakeComplete()) {
- sslHandler.flushPreHandshakeEvents();
- }
-
- // Write encrypted data to be written (if any)
- sslHandler.writeNetBuffer(nextFilter);
-
- // handle app. data read (if any)
- handleAppDataRead(nextFilter, sslHandler);
- }
-
- private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
- // forward read app data
- IoBuffer readBuffer = sslHandler.fetchAppBuffer();
-
- if (readBuffer.hasRemaining()) {
- sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
- }
- }
-
- private SslHandler getSslSessionHandler(IoSession session) {
- SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
-
- if (sslHandler == null) {
- throw new IllegalStateException();
- }
-
- synchronized(sslHandler) {
- if (sslHandler.getSslFilter() != this) {
- throw new IllegalArgumentException("Not managed by this filter.");
- }
- }
-
- return sslHandler;
- }
-
- /**
- * A message that is sent from {@link SslFilter} when the connection became
- * secure or is not secure anymore.
- *
- * @author <a href="http://mina.apache.org">Apache MINA Project</a>
- */
- public static class SslFilterMessage {
- private final String name;
-
- private SslFilterMessage(String name) {
- this.name = name;
- }
-
- @Override
- public String toString() {
- return name;
- }
- }
-
- /**
- * A private class used to store encrypted messages. This is necessary
- * to be able to emit the messageSent event with the proper original
- * message, but not for handshake messages, which will be swallowed.
- *
- */
- /* package protected */ static class EncryptedWriteRequest extends DefaultWriteRequest {
- // Thee encrypted messagee
- private final IoBuffer encryptedMessage;
-
- // The original message
- private WriteRequest parentRequest;
-
- /**
- * Create a new instance of an EncryptedWriteRequest
- * @param writeRequest The parent request
- * @param encryptedMessage The encrypted message
- */
- private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
- super(encryptedMessage);
- parentRequest = writeRequest;
- this.encryptedMessage = encryptedMessage;
- }
-
- /**
- * @return teh encrypted message
- */
- @Override
- public Object getMessage() {
- return encryptedMessage;
- }
-
- /**
- * @return The parent WriteRequest
- */
- public WriteRequest getParentRequest() {
- return parentRequest;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public WriteFuture getFuture() {
- return parentRequest.getFuture();
- }
- }
+ throw se;
+ }
+
+ return future;
+ }
+
+ // Utilities
+ private void handleSslData(NextFilter nextFilter, SslHandler sslHandler) throws SSLException {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{}: Processing the SSL Data ", getSessionInfo(sslHandler.getSession()));
+ }
+
+ // Flush any buffered write requests occurred before handshaking.
+ if (sslHandler.isHandshakeComplete()) {
+ sslHandler.flushPreHandshakeEvents();
+ }
+
+ // Write encrypted data to be written (if any)
+ sslHandler.writeNetBuffer(nextFilter);
+
+ // handle app. data read (if any)
+ handleAppDataRead(nextFilter, sslHandler);
+ }
+
+ private void handleAppDataRead(NextFilter nextFilter, SslHandler sslHandler) {
+ // forward read app data
+ IoBuffer readBuffer = sslHandler.fetchAppBuffer();
+
+ if (readBuffer.hasRemaining()) {
+ sslHandler.scheduleMessageReceived(nextFilter, readBuffer);
+ }
+ }
+
+ private SslHandler getSslSessionHandler(IoSession session) {
+ SslHandler sslHandler = (SslHandler) session.getAttribute(SSL_HANDLER);
+
+ if (sslHandler == null) {
+ throw new IllegalStateException();
+ }
+
+ synchronized (sslHandler) {
+ if (sslHandler.getSslFilter() != this) {
+ throw new IllegalArgumentException("Not managed by this filter.");
+ }
+ }
+
+ return sslHandler;
+ }
+
+ /**
+ * A message that is sent from {@link SslFilter} when the connection became
+ * secure or is not secure anymore.
+ *
+ * @author <a href="http://mina.apache.org">Apache MINA Project</a>
+ */
+ public static class SslFilterMessage {
+ private final String name;
+
+ private SslFilterMessage(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ /**
+ * A private class used to store encrypted messages. This is necessary to be
+ * able to emit the messageSent event with the proper original message, but not
+ * for handshake messages, which will be swallowed.
+ *
+ */
+ /* package protected */ static class EncryptedWriteRequest extends DefaultWriteRequest {
+ // Thee encrypted messagee
+ private final IoBuffer encryptedMessage;
+
+ // The original message
+ private WriteRequest parentRequest;
+
+ /**
+ * Create a new instance of an EncryptedWriteRequest
+ *
+ * @param writeRequest The parent request
+ * @param encryptedMessage The encrypted message
+ */
+ private EncryptedWriteRequest(WriteRequest writeRequest, IoBuffer encryptedMessage) {
+ super(encryptedMessage);
+ parentRequest = writeRequest;
+ this.encryptedMessage = encryptedMessage;
+ }
+
+ /**
+ * @return teh encrypted message
+ */
+ @Override
+ public Object getMessage() {
+ return encryptedMessage;
+ }
+
+ /**
+ * @return The parent WriteRequest
+ */
+ public WriteRequest getParentRequest() {
+ return parentRequest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public WriteFuture getFuture() {
+ return parentRequest.getFuture();
+ }
+ }
}
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
index 71ace82..070ee18 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/SslHandler.java
@@ -62,812 +62,813 @@ import org.slf4j.LoggerFactory;
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/
-/** No qualifier*/
+/** No qualifier */
class SslHandler {
- /** A logger for this class */
- private static final Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
-
- /** The SSL Filter which has created this handler */
- private final SslFilter sslFilter;
-
- /** The current session */
- private final IoSession session;
-
- private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<>();
-
- private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<>();
-
- /** A queue used to stack all the incoming data until the SSL session is established */
- private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<>();
-
- private SSLEngine sslEngine;
-
- /**
- * Encrypted data from the net
- */
- private IoBuffer inNetBuffer;
-
- /**
- * Encrypted data to be written to the net
- */
- private IoBuffer outNetBuffer;
-
- /**
- * Application cleartext data to be read by application
- */
- private IoBuffer appBuffer;
-
- /**
- * Empty buffer used during initial handshake and close operations
- */
- private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
-
- private SSLEngineResult.HandshakeStatus handshakeStatus;
-
- /**
- * A flag set to true when the first SSL handshake has been completed
- * This is used to avoid sending a notification to the application handler
- * when we switch to a SECURE or UNSECURE session.
- */
- private boolean firstSSLNegociation;
-
- /** A flag set to true when a SSL Handshake has been completed */
- private boolean handshakeComplete;
-
- /** A flag used to indicate to the SslFilter that the buffer
- * it will write is already encrypted (this will be the case
- * for data being produced during the handshake). */
- private boolean writingEncryptedData;
-
- /**
- * Create a new SSL Handler, and initialize it.
- *
- * @param sslContext
- * @throws SSLException
- */
- /* no qualifier */SslHandler(SslFilter sslFilter, IoSession session) {
- this.sslFilter = sslFilter;
- this.session = session;
- }
-
- /**
- * Initialize the SSL handshake.
- *
- * @throws SSLException If the underlying SSLEngine handshake initialization failed
- */
- /* no qualifier */void init() throws SSLException {
- if (sslEngine != null) {
- // We already have a SSL engine created, no need to create a new one
- return;
- }
-
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
- }
-
- InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
-
- // Create the SSL engine here
- if (peer == null) {
- sslEngine = sslFilter.sslContext.createSSLEngine();
- } else {
- sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
- }
-
- // Initialize the engine in client mode if necessary
- sslEngine.setUseClientMode(sslFilter.isUseClientMode());
-
- // Initialize the different SslEngine modes
- if (!sslEngine.getUseClientMode()) {
- // Those parameters are only valid when in server mode
- if (sslFilter.isWantClientAuth()) {
- sslEngine.setWantClientAuth(true);
- }
-
- if (sslFilter.isNeedClientAuth()) {
- sslEngine.setNeedClientAuth(true);
- }
- }
-
- // Set the cipher suite to use by this SslEngine instance
- if (sslFilter.getEnabledCipherSuites() != null) {
- sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
- }
-
- // Set the list of enabled protocols
- if (sslFilter.getEnabledProtocols() != null) {
- sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
- }
-
- // TODO : we may not need to call this method...
- // However, if we don't call it here, the tests are failing. Why?
- handshakeStatus = sslEngine.getHandshakeStatus();
-
- // Default value
- writingEncryptedData = false;
-
- // We haven't yet started a SSL negotiation
- // set the flags accordingly
- firstSSLNegociation = true;
- handshakeComplete = false;
-
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
- }
- }
-
-
- /**
- * Release allocated buffers.
- */
- /* no qualifier */void destroy() {
- if (sslEngine == null) {
- return;
- }
-
- // Close inbound and flush all remaining data if available.
- try {
- sslEngine.closeInbound();
- } catch (SSLException e) {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
- }
- }
-
- if (outNetBuffer != null) {
- outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
- } else {
- createOutNetBuffer(0);
- }
- try {
- do {
- outNetBuffer.clear();
- } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
- } catch (SSLException e) {
- // Ignore.
- } finally {
- outNetBuffer.free();
- outNetBuffer = null;
- }
-
- sslEngine.closeOutbound();
- sslEngine = null;
-
- preHandshakeEventQueue.clear();
- }
-
- /**
- * @return The SSL filter which has created this handler
- */
- /* no qualifier */SslFilter getSslFilter() {
- return sslFilter;
- }
-
- /* no qualifier */IoSession getSession() {
- return session;
- }
-
- /**
- * Check if we are writing encrypted data.
- */
- /* no qualifier */boolean isWritingEncryptedData() {
- return writingEncryptedData;
- }
-
- /**
- * Check if handshake is completed.
- */
- /* no qualifier */boolean isHandshakeComplete() {
- return handshakeComplete;
- }
-
- /**
- * Check if handshake is on going.
- */
- /* no qualifier */boolean notHandshaking() {
- return handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING;
- }
-
- /* no qualifier */boolean isInboundDone() {
- return sslEngine == null || sslEngine.isInboundDone();
- }
-
- /* no qualifier */boolean isOutboundDone() {
- return sslEngine == null || sslEngine.isOutboundDone();
- }
-
- /**
- * Check if there is any need to complete handshake.
- */
- /* no qualifier */boolean needToCompleteHandshake() {
- return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
- }
-
- /* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
- preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
- }
-
- /* no qualifier */void flushPreHandshakeEvents() throws SSLException {
- IoFilterEvent scheduledWrite;
-
- while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
- sslFilter
- .filterWrite(scheduledWrite.getNextFilter(), session, (WriteRequest) scheduledWrite.getParameter());
- }
- }
-
- /* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
- filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
- }
-
- /* no qualifier */void flushFilterWrite() {
- // Fire events only when the lock is available for this handler.
- IoFilterEvent event;
-
- // We need synchronization here inevitably because filterWrite can be
- // called simultaneously and cause 'bad record MAC' integrity error.
- while ((event = filterWriteEventQueue.poll()) != null) {
- NextFilter nextFilter = event.getNextFilter();
- nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
- }
- }
-
- /**
- * Push the newly received data into a queue, waiting for the SSL session
- * to be fully established
- *
- * @param nextFilter The next filter to call
- * @param message The incoming data
- */
- /* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
- messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
- }
-
- /* no qualifier */void flushMessageReceived() {
- IoFilterEvent event;
-
- while ((event = messageReceivedEventQueue.poll()) != null) {
- NextFilter nextFilter = event.getNextFilter();
- nextFilter.messageReceived(session, event.getParameter());
- }
- }
-
- /**
- * Call when data are read from net. It will perform the initial hanshake or decrypt
- * the data if SSL has been initialiaed.
- *
- * @param buf buffer to decrypt
- * @param nextFilter Next filter in chain
- * @throws SSLException on errors
- */
- /* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
- }
-
- // append buf to inNetBuffer
- if (inNetBuffer == null) {
- inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
- }
-
- inNetBuffer.put(buf);
-
- if (!handshakeComplete) {
- handshake(nextFilter);
- } else {
- // Prepare the net data for reading.
- inNetBuffer.flip();
-
- if (!inNetBuffer.hasRemaining()) {
- return;
- }
-
- SSLEngineResult res = unwrap();
-
- // prepare to be written again
- if (inNetBuffer.hasRemaining()) {
- inNetBuffer.compact();
- } else {
- inNetBuffer.free();
- inNetBuffer = null;
- }
-
- checkStatus(res);
-
- renegotiateIfNeeded(nextFilter, res);
- }
-
- if (isInboundDone()) {
- // Rewind the MINA buffer if not all data is processed and inbound
- // is finished.
- int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
- buf.position(buf.position() - inNetBufferPosition);
-
- if (inNetBuffer != null) {
- inNetBuffer.free();
- inNetBuffer = null;
- }
- }
- }
-
- /**
- * Get decrypted application data.
- *
- * @return buffer with data
- */
- /* no qualifier */IoBuffer fetchAppBuffer() {
- if (appBuffer == null) {
- return IoBuffer.allocate(0);
- } else {
- IoBuffer newAppBuffer = appBuffer.flip();
- appBuffer = null;
-
- return newAppBuffer.shrink();
- }
- }
-
- /**
- * Get encrypted data to be sent.
- *
- * @return buffer with data
- */
- /* no qualifier */IoBuffer fetchOutNetBuffer() {
- IoBuffer answer = outNetBuffer;
-
- if (answer == null) {
- return emptyBuffer;
- }
-
- outNetBuffer = null;
-
- return answer.shrink();
- }
-
- /**
- * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
- *
- * @param src
- * data to encrypt
- * @throws SSLException
- * on errors
- */
- /* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
- if (!handshakeComplete) {
- throw new IllegalStateException();
- }
-
- if (!src.hasRemaining()) {
- if (outNetBuffer == null) {
- outNetBuffer = emptyBuffer;
- }
- return;
- }
-
- createOutNetBuffer(src.remaining());
-
- // Loop until there is no more data in src
- while (src.hasRemaining()) {
-
- SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
-
- if (result.getStatus() == SSLEngineResult.Status.OK) {
- if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
- doTasks();
- }
- } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
- outNetBuffer.capacity(outNetBuffer.capacity() << 1);
- outNetBuffer.limit(outNetBuffer.capacity());
- } else {
- throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
- + "outNetBuffer: " + outNetBuffer);
- }
- }
-
- outNetBuffer.flip();
- }
-
- /**
- * Start SSL shutdown process.
- *
- * @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
- * shutdown process is already finished.
- * @throws SSLException
- * on errors
- */
- /* no qualifier */boolean closeOutbound() throws SSLException {
- if (sslEngine == null || sslEngine.isOutboundDone()) {
- return false;
- }
-
- sslEngine.closeOutbound();
-
- createOutNetBuffer(0);
- SSLEngineResult result;
-
- for (;;) {
- result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
- if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
- outNetBuffer.capacity(outNetBuffer.capacity() << 1);
- outNetBuffer.limit(outNetBuffer.capacity());
- } else {
- break;
- }
- }
-
- if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
- throw new SSLException("Improper close state: " + result);
- }
-
- outNetBuffer.flip();
-
- return true;
- }
-
- /**
- * @param res
- * @throws SSLException
- */
- private void checkStatus(SSLEngineResult res) throws SSLException {
-
- SSLEngineResult.Status status = res.getStatus();
-
- /*
- * The status may be:
- * OK - Normal operation
- * OVERFLOW - Should never happen since the application buffer is sized to hold the maximum
- * packet size.
- * UNDERFLOW - Need to read more data from the socket. It's normal.
- * CLOSED - The other peer closed the socket. Also normal.
- */
- switch (status) {
- case BUFFER_OVERFLOW:
- throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
- + "appBuffer: " + appBuffer);
- case CLOSED:
- Exception exception =new RuntimeIoException("SSL/TLS close_notify received");
-
- // Empty the Ssl queue
- for (IoFilterEvent event:filterWriteEventQueue) {
- EncryptedWriteRequest writeRequest = (EncryptedWriteRequest)event.getParameter();
- WriteFuture writeFuture = writeRequest.getParentRequest().getFuture();
- writeFuture.setException(exception);
- writeFuture.notifyAll();
- }
-
- // Empty the session queue
- WriteRequestQueue queue = session.getWriteRequestQueue();
- WriteRequest request = null;
-
- while ((request = queue.poll(session)) != null) {
- WriteFuture writeFuture = request.getFuture();
- writeFuture.setException(exception);
- writeFuture.notifyAll();
- }
-
- // We *must* shutdown session
- session.closeNow();
- break;
- default:
- break;
- }
- }
-
- /**
- * Perform any handshaking processing.
- */
- /* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
- for (;;) {
- switch (handshakeStatus) {
- case FINISHED:
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
- }
-
- session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
- handshakeComplete = true;
-
- // Send the SECURE message only if it's the first SSL handshake
- if (firstSSLNegociation) {
- firstSSLNegociation = false;
-
- nextFilter.event(session, SslEvent.SECURED);
- }
-
- if (LOGGER.isDebugEnabled()) {
- if (!isOutboundDone()) {
- LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
- } else {
- LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
- }
- }
-
- return;
-
- case NEED_TASK:
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
- }
-
- handshakeStatus = doTasks();
- break;
-
- case NEED_UNWRAP:
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
- }
- // we need more data read
- SSLEngineResult.Status status = unwrapHandshake(nextFilter);
-
- if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
- && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
- // We need more data or the session is closed
- return;
- }
-
- break;
-
- case NEED_WRAP:
- case NOT_HANDSHAKING:
- if (LOGGER.isDebugEnabled()) {
- LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
- }
-
- // First make sure that the out buffer is completely empty.
- // Since we cannot call wrap with data left on the buffer
- if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
- return;
- }
-
- SSLEngineResult result;
- createOutNetBuffer(0);
-
- result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
-
- while ( result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW ) {
- outNetBuffer.capacity(outNetBuffer.capacity() << 1);
- outNetBuffer.limit(outNetBuffer.capacity());
-
- result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
- }
-
- outNetBuffer.flip();
- handshakeStatus = result.getHandshakeStatus();
- writeNetBuffer(nextFilter);
- break;
-
- default:
- String msg = "Invalid Handshaking State" + handshakeStatus
- + " while processing the Handshake for session " + session.getId();
- LOGGER.error(msg);
- throw new IllegalStateException(msg);
- }
- }
- }
-
- private void createOutNetBuffer(int expectedRemaining) {
- // SSLEngine requires us to allocate unnecessarily big buffer
- // even for small data. *Shrug*
- int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
-
- if (outNetBuffer != null) {
- outNetBuffer.capacity(capacity);
- } else {
- outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
- }
- }
-
- /* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
- // Check if any net data needed to be writen
- if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
- // no; bail out
- return null;
- }
-
- // set flag that we are writing encrypted data
- // (used in SSLFilter.filterWrite())
- writingEncryptedData = true;
-
- // write net data
- WriteFuture writeFuture = null;
-
- try {
- IoBuffer writeBuffer = fetchOutNetBuffer();
- writeFuture = new DefaultWriteFuture(session);
- sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
-
- // loop while more writes required to complete handshake
- while (needToCompleteHandshake()) {
- try {
- handshake(nextFilter);
- } catch (SSLException ssle) {
- SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
- newSsle.initCause(ssle);
- throw newSsle;
- }
-
- IoBuffer currentOutNetBuffer = fetchOutNetBuffer();
-
- if (currentOutNetBuffer != null && currentOutNetBuffer.hasRemaining()) {
- writeFuture = new DefaultWriteFuture(session);
- sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(currentOutNetBuffer, writeFuture));
- }
- }
- } finally {
- writingEncryptedData = false;
- }
-
- return writeFuture;
- }
-
- private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
- // Prepare the net data for reading.
- if (inNetBuffer != null) {
- inNetBuffer.flip();
- }
-
- if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
- // Need more data.
- return SSLEngineResult.Status.BUFFER_UNDERFLOW;
- }
-
- SSLEngineResult res = unwrap();
- handshakeStatus = res.getHandshakeStatus();
-
- checkStatus(res);
-
- // If handshake finished, no data was produced, and the status is still
- // ok, try to unwrap more
- if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
- && (res.getStatus() == SSLEngineResult.Status.OK)
- && inNetBuffer.hasRemaining()) {
- res = unwrap();
-
- // prepare to be written again
- if (inNetBuffer.hasRemaining()) {
- inNetBuffer.compact();
- } else {
- inNetBuffer.free();
- inNetBuffer = null;
- }
-
- renegotiateIfNeeded(nextFilter, res);
- } else {
- // prepare to be written again
- if (inNetBuffer.hasRemaining()) {
- inNetBuffer.compact();
- } else {
- inNetBuffer.free();
- inNetBuffer = null;
- }
- }
-
- return res.getStatus();
- }
-
- private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
- if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
- && (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
- && (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
- // Renegotiation required.
- handshakeComplete = false;
- handshakeStatus = res.getHandshakeStatus();
- handshake(nextFilter);
- }
- }
-
- /**
- * Decrypt the incoming buffer and move the decrypted data to an
- * application buffer.
- */
- private SSLEngineResult unwrap() throws SSLException {
- // We first have to create the application buffer if it does not exist
- if (appBuffer == null) {
- appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
- } else {
- // We already have one, just add the new data into it
- appBuffer.expand(inNetBuffer.remaining());
- }
-
- SSLEngineResult res;
- Status status;
- HandshakeStatus localHandshakeStatus;
-
- do {
- // Decode the incoming data
- res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
- status = res.getStatus();
-
- // We can be processing the Handshake
- localHandshakeStatus = res.getHandshakeStatus();
-
- if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
- // We have to grow the target buffer, it's too small.
- // Then we can call the unwrap method again
- int newCapacity = sslEngine.getSession().getApplicationBufferSize();
-
- if (appBuffer.remaining() >= newCapacity) {
- // The buffer is already larger than the max buffer size suggested by the SSL engine.
- // Raising it any more will not make sense and it will end up in an endless loop. Throwing an error is safer
- throw new SSLException("SSL buffer overflow");
- }
-
- appBuffer.expand(newCapacity);
- continue;
- }
- } while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
- && ((localHandshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) ||
- (localHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
-
- return res;
- }
-
- /**
- * Do all the outstanding handshake tasks in the current Thread.
- */
- private SSLEngineResult.HandshakeStatus doTasks() {
- /*
- * We could run this in a separate thread, but I don't see the need for
- * this when used from SSLFilter. Use thread filters in MINA instead?
- */
- Runnable runnable;
- while ((runnable = sslEngine.getDelegatedTask()) != null) {
- // TODO : we may have to use a thread pool here to improve the
- // performances
- runnable.run();
- }
- return sslEngine.getHandshakeStatus();
- }
-
- /**
- * Creates a new MINA buffer that is a deep copy of the remaining bytes in
- * the given buffer (between index buf.position() and buf.limit())
- *
- * @param src
- * the buffer to copy
- * @return the new buffer, ready to read from
- */
- /* no qualifier */static IoBuffer copy(ByteBuffer src) {
- IoBuffer copy = IoBuffer.allocate(src.remaining());
- copy.put(src);
- copy.flip();
- return copy;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder();
-
- sb.append("SSLStatus <");
-
- if (handshakeComplete) {
- sb.append("SSL established");
- } else {
- sb.append("Processing Handshake").append("; ");
- sb.append("Status : ").append(handshakeStatus).append("; ");
- }
-
- sb.append(", ");
- sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
- sb.append(">");
-
- return sb.toString();
- }
-
- /**
- * Free the allocated buffers
- */
- /* no qualifier */void release() {
- if (inNetBuffer != null) {
- inNetBuffer.free();
- inNetBuffer = null;
- }
-
- if (outNetBuffer != null) {
- outNetBuffer.free();
- outNetBuffer = null;
- }
- }
+ /** A logger for this class */
+ private static final Logger LOGGER = LoggerFactory.getLogger(SslHandler.class);
+
+ /** The SSL Filter which has created this handler */
+ private final SslFilter sslFilter;
+
+ /** The current session */
+ private final IoSession session;
+
+ private final Queue<IoFilterEvent> preHandshakeEventQueue = new ConcurrentLinkedQueue<>();
+
+ private final Queue<IoFilterEvent> filterWriteEventQueue = new ConcurrentLinkedQueue<>();
+
+ /**
+ * A queue used to stack all the incoming data until the SSL session is
+ * established
+ */
+ private final Queue<IoFilterEvent> messageReceivedEventQueue = new ConcurrentLinkedQueue<>();
+
+ private SSLEngine sslEngine;
+
+ /**
+ * Encrypted data from the net
+ */
+ private IoBuffer inNetBuffer;
+
+ /**
+ * Encrypted data to be written to the net
+ */
+ private IoBuffer outNetBuffer;
+
+ /**
+ * Application cleartext data to be read by application
+ */
+ private IoBuffer appBuffer;
+
+ /**
+ * Empty buffer used during initial handshake and close operations
+ */
+ private final IoBuffer emptyBuffer = IoBuffer.allocate(0);
+
+ private SSLEngineResult.HandshakeStatus handshakeStatus;
+
+ /**
+ * A flag set to true when the first SSL handshake has been completed This is
+ * used to avoid sending a notification to the application handler when we
+ * switch to a SECURE or UNSECURE session.
+ */
+ private boolean firstSSLNegociation;
+
+ /** A flag set to true when a SSL Handshake has been completed */
+ private boolean handshakeComplete;
+
+ /**
+ * A flag used to indicate to the SslFilter that the buffer it will write is
+ * already encrypted (this will be the case for data being produced during the
+ * handshake).
+ */
+ private boolean writingEncryptedData;
+
+ /**
+ * Create a new SSL Handler, and initialize it.
+ *
+ * @param sslContext
+ * @throws SSLException
+ */
+ /* no qualifier */ SslHandler(SslFilter sslFilter, IoSession session) {
+ this.sslFilter = sslFilter;
+ this.session = session;
+ }
+
+ /**
+ * Initialize the SSL handshake.
+ *
+ * @throws SSLException If the underlying SSLEngine handshake initialization
+ * failed
+ */
+ /* no qualifier */void init() throws SSLException {
+ if (sslEngine != null) {
+ // We already have a SSL engine created, no need to create a new one
+ return;
+ }
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{} Initializing the SSL Handler", sslFilter.getSessionInfo(session));
+ }
+
+ InetSocketAddress peer = (InetSocketAddress) session.getAttribute(SslFilter.PEER_ADDRESS);
+
+ // Create the SSL engine here
+ if (peer == null) {
+ sslEngine = sslFilter.sslContext.createSSLEngine();
+ } else {
+ sslEngine = sslFilter.sslContext.createSSLEngine(peer.getHostName(), peer.getPort());
+ }
+
+ // Initialize the engine in client mode if necessary
+ sslEngine.setUseClientMode(sslFilter.isUseClientMode());
+
+ // Initialize the different SslEngine modes
+ if (!sslEngine.getUseClientMode()) {
+ // Those parameters are only valid when in server mode
+ if (sslFilter.isWantClientAuth()) {
+ sslEngine.setWantClientAuth(true);
+ }
+
+ if (sslFilter.isNeedClientAuth()) {
+ sslEngine.setNeedClientAuth(true);
+ }
+ }
+
+ // Set the cipher suite to use by this SslEngine instance
+ if (sslFilter.getEnabledCipherSuites() != null) {
+ sslEngine.setEnabledCipherSuites(sslFilter.getEnabledCipherSuites());
+ }
+
+ // Set the list of enabled protocols
+ if (sslFilter.getEnabledProtocols() != null) {
+ sslEngine.setEnabledProtocols(sslFilter.getEnabledProtocols());
+ }
+
+ // TODO : we may not need to call this method...
+ // However, if we don't call it here, the tests are failing. Why?
+ handshakeStatus = sslEngine.getHandshakeStatus();
+
+ // Default value
+ writingEncryptedData = false;
+
+ // We haven't yet started a SSL negotiation
+ // set the flags accordingly
+ firstSSLNegociation = true;
+ handshakeComplete = false;
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{} SSL Handler Initialization done.", sslFilter.getSessionInfo(session));
+ }
+ }
+
+ /**
+ * Release allocated buffers.
+ */
+ /* no qualifier */void destroy() {
+ if (sslEngine == null) {
+ return;
+ }
+
+ // Close inbound and flush all remaining data if available.
+ try {
+ sslEngine.closeInbound();
+ } catch (SSLException e) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Unexpected exception from SSLEngine.closeInbound().", e);
+ }
+ }
+
+ if (outNetBuffer != null) {
+ outNetBuffer.capacity(sslEngine.getSession().getPacketBufferSize());
+ } else {
+ createOutNetBuffer(0);
+ }
+ try {
+ do {
+ outNetBuffer.clear();
+ } while (sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf()).bytesProduced() > 0);
+ } catch (SSLException e) {
+ // Ignore.
+ } finally {
+ outNetBuffer.free();
+ outNetBuffer = null;
+ }
+
+ sslEngine.closeOutbound();
+ sslEngine = null;
+
+ preHandshakeEventQueue.clear();
+ }
+
+ /**
+ * @return The SSL filter which has created this handler
+ */
+ /* no qualifier */SslFilter getSslFilter() {
+ return sslFilter;
+ }
+
+ /* no qualifier */IoSession getSession() {
+ return session;
+ }
+
+ /**
+ * Check if we are writing encrypted data.
+ */
+ /* no qualifier */boolean isWritingEncryptedData() {
+ return writingEncryptedData;
+ }
+
+ /**
+ * Check if handshake is completed.
+ */
+ /* no qualifier */boolean isHandshakeComplete() {
+ return handshakeComplete;
+ }
+
+ /**
+ * Check if handshake is on going.
+ */
+ /* no qualifier */boolean notHandshaking() {
+ return handshakeStatus == HandshakeStatus.FINISHED || handshakeStatus == HandshakeStatus.NOT_HANDSHAKING;
+ }
+
+ /* no qualifier */boolean isInboundDone() {
+ return sslEngine == null || sslEngine.isInboundDone();
+ }
+
+ /* no qualifier */boolean isOutboundDone() {
+ return sslEngine == null || sslEngine.isOutboundDone();
+ }
+
+ /**
+ * Check if there is any need to complete handshake.
+ */
+ /* no qualifier */boolean needToCompleteHandshake() {
+ return handshakeStatus == SSLEngineResult.HandshakeStatus.NEED_WRAP && !isInboundDone();
+ }
+
+ /* no qualifier */void schedulePreHandshakeWriteRequest(NextFilter nextFilter, WriteRequest writeRequest) {
+ preHandshakeEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
+ }
+
+ /* no qualifier */void flushPreHandshakeEvents() throws SSLException {
+ IoFilterEvent scheduledWrite;
+
+ while ((scheduledWrite = preHandshakeEventQueue.poll()) != null) {
+ sslFilter.filterWrite(scheduledWrite.getNextFilter(), session,
+ (WriteRequest) scheduledWrite.getParameter());
+ }
+ }
+
+ /* no qualifier */void scheduleFilterWrite(NextFilter nextFilter, WriteRequest writeRequest) {
+ filterWriteEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.WRITE, session, writeRequest));
+ }
+
+ /* no qualifier */void flushFilterWrite() {
+ // Fire events only when the lock is available for this handler.
+ IoFilterEvent event;
+
+ // We need synchronization here inevitably because filterWrite can be
+ // called simultaneously and cause 'bad record MAC' integrity error.
+ while ((event = filterWriteEventQueue.poll()) != null) {
+ NextFilter nextFilter = event.getNextFilter();
+ nextFilter.filterWrite(session, (WriteRequest) event.getParameter());
+ }
+ }
+
+ /**
+ * Push the newly received data into a queue, waiting for the SSL session to be
+ * fully established
+ *
+ * @param nextFilter The next filter to call
+ * @param message The incoming data
+ */
+ /* no qualifier */void scheduleMessageReceived(NextFilter nextFilter, Object message) {
+ messageReceivedEventQueue.add(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_RECEIVED, session, message));
+ }
+
+ /* no qualifier */void flushMessageReceived() {
+ IoFilterEvent event;
+
+ while ((event = messageReceivedEventQueue.poll()) != null) {
+ NextFilter nextFilter = event.getNextFilter();
+ nextFilter.messageReceived(session, event.getParameter());
+ }
+ }
+
+ /**
+ * Call when data are read from net. It will perform the initial hanshake or
+ * decrypt the data if SSL has been initialiaed.
+ *
+ * @param buf buffer to decrypt
+ * @param nextFilter Next filter in chain
+ * @throws SSLException on errors
+ */
+ /* no qualifier */void messageReceived(NextFilter nextFilter, ByteBuffer buf) throws SSLException {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{} Processing the received message", sslFilter.getSessionInfo(session));
+ }
+
+ // append buf to inNetBuffer
+ if (inNetBuffer == null) {
+ inNetBuffer = IoBuffer.allocate(buf.remaining()).setAutoExpand(true);
+ }
+
+ inNetBuffer.put(buf);
+
+ if (!handshakeComplete) {
+ handshake(nextFilter);
+ } else {
+ // Prepare the net data for reading.
+ inNetBuffer.flip();
+
+ if (!inNetBuffer.hasRemaining()) {
+ return;
+ }
+
+ SSLEngineResult res = unwrap();
+
+ // prepare to be written again
+ if (inNetBuffer.hasRemaining()) {
+ inNetBuffer.compact();
+ } else {
+ inNetBuffer.free();
+ inNetBuffer = null;
+ }
+
+ checkStatus(res);
+
+ renegotiateIfNeeded(nextFilter, res);
+ }
+
+ if (isInboundDone()) {
+ // Rewind the MINA buffer if not all data is processed and inbound
+ // is finished.
+ int inNetBufferPosition = inNetBuffer == null ? 0 : inNetBuffer.position();
+ buf.position(buf.position() - inNetBufferPosition);
+
+ if (inNetBuffer != null) {
+ inNetBuffer.free();
+ inNetBuffer = null;
+ }
+ }
+ }
+
+ /**
+ * Get decrypted application data.
+ *
+ * @return buffer with data
+ */
+ /* no qualifier */IoBuffer fetchAppBuffer() {
+ if (appBuffer == null) {
+ return IoBuffer.allocate(0);
+ } else {
+ IoBuffer newAppBuffer = appBuffer.flip();
+ appBuffer = null;
+
+ return newAppBuffer.shrink();
+ }
+ }
+
+ /**
+ * Get encrypted data to be sent.
+ *
+ * @return buffer with data
+ */
+ /* no qualifier */IoBuffer fetchOutNetBuffer() {
+ IoBuffer answer = outNetBuffer;
+
+ if (answer == null) {
+ return emptyBuffer;
+ }
+
+ outNetBuffer = null;
+
+ return answer.shrink();
+ }
+
+ /**
+ * Encrypt provided buffer. Encrypted data returned by getOutNetBuffer().
+ *
+ * @param src data to encrypt
+ * @throws SSLException on errors
+ */
+ /* no qualifier */void encrypt(ByteBuffer src) throws SSLException {
+ if (!handshakeComplete) {
+ throw new IllegalStateException();
+ }
+
+ if (!src.hasRemaining()) {
+ if (outNetBuffer == null) {
+ outNetBuffer = emptyBuffer;
+ }
+ return;
+ }
+
+ createOutNetBuffer(src.remaining());
+
+ // Loop until there is no more data in src
+ while (src.hasRemaining()) {
+
+ SSLEngineResult result = sslEngine.wrap(src, outNetBuffer.buf());
+
+ if (result.getStatus() == SSLEngineResult.Status.OK) {
+ if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
+ doTasks();
+ }
+ } else if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+ outNetBuffer.limit(outNetBuffer.capacity());
+ } else {
+ throw new SSLException("SSLEngine error during encrypt: " + result.getStatus() + " src: " + src
+ + "outNetBuffer: " + outNetBuffer);
+ }
+ }
+
+ outNetBuffer.flip();
+ }
+
+ /**
+ * Start SSL shutdown process.
+ *
+ * @return <tt>true</tt> if shutdown process is started. <tt>false</tt> if
+ * shutdown process is already finished.
+ * @throws SSLException on errors
+ */
+ /* no qualifier */boolean closeOutbound() throws SSLException {
+ if (sslEngine == null || sslEngine.isOutboundDone()) {
+ return false;
+ }
+
+ sslEngine.closeOutbound();
+
+ createOutNetBuffer(0);
+ SSLEngineResult result;
+
+ for (;;) {
+ result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+ if (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+ outNetBuffer.limit(outNetBuffer.capacity());
+ } else {
+ break;
+ }
+ }
+
+ if (result.getStatus() != SSLEngineResult.Status.CLOSED) {
+ throw new SSLException("Improper close state: " + result);
+ }
+
+ outNetBuffer.flip();
+
+ return true;
+ }
+
+ /**
+ * @param res
+ * @throws SSLException
+ */
+ private void checkStatus(SSLEngineResult res) throws SSLException {
+
+ SSLEngineResult.Status status = res.getStatus();
+
+ /*
+ * The status may be: OK - Normal operation OVERFLOW - Should never happen since
+ * the application buffer is sized to hold the maximum packet size. UNDERFLOW -
+ * Need to read more data from the socket. It's normal. CLOSED - The other peer
+ * closed the socket. Also normal.
+ */
+ switch (status) {
+ case BUFFER_OVERFLOW:
+ throw new SSLException("SSLEngine error during decrypt: " + status + " inNetBuffer: " + inNetBuffer
+ + "appBuffer: " + appBuffer);
+ case CLOSED:
+ Exception exception = new RuntimeIoException("SSL/TLS close_notify received");
+
+ // Empty the Ssl queue
+ for (IoFilterEvent event : filterWriteEventQueue) {
+ EncryptedWriteRequest writeRequest = (EncryptedWriteRequest) event.getParameter();
+ WriteFuture writeFuture = writeRequest.getParentRequest().getFuture();
+ writeFuture.setException(exception);
+ writeFuture.notifyAll();
+ }
+
+ // Empty the session queue
+ WriteRequestQueue queue = session.getWriteRequestQueue();
+ WriteRequest request = null;
+
+ while ((request = queue.poll(session)) != null) {
+ WriteFuture writeFuture = request.getFuture();
+ writeFuture.setException(exception);
+ writeFuture.notifyAll();
+ }
+
+ // We *must* shutdown session
+ session.closeNow();
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Perform any handshaking processing.
+ */
+ /* no qualifier */void handshake(NextFilter nextFilter) throws SSLException {
+ for (;;) {
+ switch (handshakeStatus) {
+ case FINISHED:
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{} processing the FINISHED state", sslFilter.getSessionInfo(session));
+ }
+
+ session.setAttribute(SslFilter.SSL_SESSION, sslEngine.getSession());
+ handshakeComplete = true;
+
+ // Send the SECURE message only if it's the first SSL handshake
+ if (firstSSLNegociation) {
+ firstSSLNegociation = false;
+
+ nextFilter.event(session, SslEvent.SECURED);
+ }
+
+ if (LOGGER.isDebugEnabled()) {
+ if (!isOutboundDone()) {
+ LOGGER.debug("{} is now secured", sslFilter.getSessionInfo(session));
+ } else {
+ LOGGER.debug("{} is not secured yet", sslFilter.getSessionInfo(session));
+ }
+ }
+
+ return;
+
+ case NEED_TASK:
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{} processing the NEED_TASK state", sslFilter.getSessionInfo(session));
+ }
+
+ handshakeStatus = doTasks();
+ break;
+
+ case NEED_UNWRAP:
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{} processing the NEED_UNWRAP state", sslFilter.getSessionInfo(session));
+ }
+ // we need more data read
+ SSLEngineResult.Status status = unwrapHandshake(nextFilter);
+
+ if (status == SSLEngineResult.Status.BUFFER_UNDERFLOW
+ && handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED || isInboundDone()) {
+ // We need more data or the session is closed
+ return;
+ }
+
+ break;
+
+ case NEED_WRAP:
+ case NOT_HANDSHAKING:
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("{} processing the NEED_WRAP state", sslFilter.getSessionInfo(session));
+ }
+
+ // First make sure that the out buffer is completely empty.
+ // Since we cannot call wrap with data left on the buffer
+ if (outNetBuffer != null && outNetBuffer.hasRemaining()) {
+ return;
+ }
+
+ SSLEngineResult result;
+ createOutNetBuffer(0);
+
+ result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+
+ while (result.getStatus() == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ outNetBuffer.capacity(outNetBuffer.capacity() << 1);
+ outNetBuffer.limit(outNetBuffer.capacity());
+
+ result = sslEngine.wrap(emptyBuffer.buf(), outNetBuffer.buf());
+ }
+
+ outNetBuffer.flip();
+ handshakeStatus = result.getHandshakeStatus();
+ writeNetBuffer(nextFilter);
+ break;
+
+ default:
+ String msg = "Invalid Handshaking State" + handshakeStatus
+ + " while processing the Handshake for session " + session.getId();
+ LOGGER.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ }
+ }
+
+ private void createOutNetBuffer(int expectedRemaining) {
+ // SSLEngine requires us to allocate unnecessarily big buffer
+ // even for small data. *Shrug*
+ int capacity = Math.max(expectedRemaining, sslEngine.getSession().getPacketBufferSize());
+
+ if (outNetBuffer != null) {
+ outNetBuffer.capacity(capacity);
+ } else {
+ outNetBuffer = IoBuffer.allocate(capacity).minimumCapacity(0);
+ }
+ }
+
+ /* no qualifier */WriteFuture writeNetBuffer(NextFilter nextFilter) throws SSLException {
+ // Check if any net data needed to be writen
+ if (outNetBuffer == null || !outNetBuffer.hasRemaining()) {
+ // no; bail out
+ return null;
+ }
+
+ // set flag that we are writing encrypted data
+ // (used in SSLFilter.filterWrite())
+ writingEncryptedData = true;
+
+ // write net data
+ WriteFuture writeFuture = null;
+
+ try {
+ IoBuffer writeBuffer = fetchOutNetBuffer();
+ writeFuture = new DefaultWriteFuture(session);
+ sslFilter.filterWrite(nextFilter, session, new DefaultWriteRequest(writeBuffer, writeFuture));
+
+ // loop while more writes required to complete handshake
+ while (needToCompleteHandshake()) {
+ try {
+ handshake(nextFilter);
+ } catch (SSLException ssle) {
+ SSLException newSsle = new SSLHandshakeException("SSL handshake failed.");
+ newSsle.initCause(ssle);
+ throw newSsle;
+ }
+
+ IoBuffer currentOutNetBuffer = fetchOutNetBuffer();
+
+ if (currentOutNetBuffer != null && currentOutNetBuffer.hasRemaining()) {
+ writeFuture = new DefaultWriteFuture(session);
+ sslFilter.filterWrite(nextFilter, session,
+ new DefaultWriteRequest(currentOutNetBuffer, writeFuture));
+ }
+ }
+ } finally {
+ writingEncryptedData = false;
+ }
+
+ return writeFuture;
+ }
+
+ private SSLEngineResult.Status unwrapHandshake(NextFilter nextFilter) throws SSLException {
+ // Prepare the net data for reading.
+ if (inNetBuffer != null) {
+ inNetBuffer.flip();
+ }
+
+ if ((inNetBuffer == null) || !inNetBuffer.hasRemaining()) {
+ // Need more data.
+ return SSLEngineResult.Status.BUFFER_UNDERFLOW;
+ }
+
+ SSLEngineResult res = unwrap();
+ handshakeStatus = res.getHandshakeStatus();
+
+ checkStatus(res);
+
+ // If handshake finished, no data was produced, and the status is still
+ // ok, try to unwrap more
+ if ((handshakeStatus == SSLEngineResult.HandshakeStatus.FINISHED)
+ && (res.getStatus() == SSLEngineResult.Status.OK) && inNetBuffer.hasRemaining()) {
+ res = unwrap();
+
+ // prepare to be written again
+ if (inNetBuffer.hasRemaining()) {
+ inNetBuffer.compact();
+ } else {
+ inNetBuffer.free();
+ inNetBuffer = null;
+ }
+
+ renegotiateIfNeeded(nextFilter, res);
+ } else {
+ // prepare to be written again
+ if (inNetBuffer.hasRemaining()) {
+ inNetBuffer.compact();
+ } else {
+ inNetBuffer.free();
+ inNetBuffer = null;
+ }
+ }
+
+ return res.getStatus();
+ }
+
+ private void renegotiateIfNeeded(NextFilter nextFilter, SSLEngineResult res) throws SSLException {
+ if ((res.getStatus() != SSLEngineResult.Status.CLOSED)
+ && (res.getStatus() != SSLEngineResult.Status.BUFFER_UNDERFLOW)
+ && (res.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)) {
+ // Renegotiation required.
+ handshakeComplete = false;
+ handshakeStatus = res.getHandshakeStatus();
+ handshake(nextFilter);
+ }
+ }
+
+ /**
+ * Decrypt the incoming buffer and move the decrypted data to an application
+ * buffer.
+ */
+ private SSLEngineResult unwrap() throws SSLException {
+ // We first have to create the application buffer if it does not exist
+ if (appBuffer == null) {
+ appBuffer = IoBuffer.allocate(inNetBuffer.remaining());
+ } else {
+ // We already have one, just add the new data into it
+ appBuffer.expand(inNetBuffer.remaining());
+ }
+
+ SSLEngineResult res;
+ Status status;
+ HandshakeStatus localHandshakeStatus;
+
+ do {
+ // Decode the incoming data
+ res = sslEngine.unwrap(inNetBuffer.buf(), appBuffer.buf());
+ status = res.getStatus();
+
+ // We can be processing the Handshake
+ localHandshakeStatus = res.getHandshakeStatus();
+
+ if (status == SSLEngineResult.Status.BUFFER_OVERFLOW) {
+ // We have to grow the target buffer, it's too small.
+ // Then we can call the unwrap method again
+ int newCapacity = sslEngine.getSession().getApplicationBufferSize();
+
+ if (appBuffer.remaining() >= newCapacity) {
+ // The buffer is already larger than the max buffer size suggested by the SSL
+ // engine.
+ // Raising it any more will not make sense and it will end up in an endless
+ // loop. Throwing an error is safer
+ throw new SSLException("SSL buffer overflow");
+ }
+
+ appBuffer.expand(newCapacity);
+ continue;
+ }
+ } while (((status == SSLEngineResult.Status.OK) || (status == SSLEngineResult.Status.BUFFER_OVERFLOW))
+ && ((localHandshakeStatus == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
+ || (localHandshakeStatus == SSLEngineResult.HandshakeStatus.NEED_UNWRAP)));
+
+ return res;
+ }
+
+ /**
+ * Do all the outstanding handshake tasks in the current Thread.
+ */
+ private SSLEngineResult.HandshakeStatus doTasks() {
+ /*
+ * We could run this in a separate thread, but I don't see the need for this
+ * when used from SSLFilter. Use thread filters in MINA instead?
+ */
+ Runnable runnable;
+ while ((runnable = sslEngine.getDelegatedTask()) != null) {
+ // TODO : we may have to use a thread pool here to improve the
+ // performances
+ runnable.run();
+ }
+ return sslEngine.getHandshakeStatus();
+ }
+
+ /**
+ * Creates a new MINA buffer that is a deep copy of the remaining bytes in the
+ * given buffer (between index buf.position() and buf.limit())
+ *
+ * @param src the buffer to copy
+ * @return the new buffer, ready to read from
+ */
+ /* no qualifier */static IoBuffer copy(ByteBuffer src) {
+ IoBuffer copy = IoBuffer.allocate(src.remaining());
+ copy.put(src);
+ copy.flip();
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append("SSLStatus <");
+
+ if (handshakeComplete) {
+ sb.append("SSL established");
+ } else {
+ sb.append("Processing Handshake").append("; ");
+ sb.append("Status : ").append(handshakeStatus).append("; ");
+ }
+
+ sb.append(", ");
+ sb.append("HandshakeComplete :").append(handshakeComplete).append(", ");
+ sb.append(">");
+
+ return sb.toString();
+ }
+
+ /**
+ * Free the allocated buffers
+ */
+ /* no qualifier */void release() {
+ if (inNetBuffer != null) {
+ inNetBuffer.free();
+ inNetBuffer = null;
+ }
+
+ if (outNetBuffer != null) {
+ outNetBuffer.free();
+ outNetBuffer = null;
+ }
+ }
}
diff --git a/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java b/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
index 62cce29..73d622f 100644
--- a/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
+++ b/mina-core/src/main/java/org/apache/mina/filter/ssl/package-info.java
@@ -19,7 +19,8 @@
*/
/**
- * Classes that implement IoFilter and provide Secure Sockets Layer functionality.
+ * Classes that implement IoFilter and provide Secure Sockets Layer
+ * functionality.
*
* @author <a href="http://mina.apache.org">Apache MINA Project</a>
*/