Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/configuration/IgniteConfiguration.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -52,6 +52,7 @@ import org.apache.ignite.spi.loadbalancing.roundrobin.*; import org.apache.ignite.spi.swapspace.*; import org.apache.ignite.spi.swapspace.file.*; +import org.apache.ignite.ssl.*; import javax.cache.configuration.*; import javax.cache.event.*; @@ -59,6 +60,7 @@ import javax.cache.integration.*; import javax.cache.processor.*; import javax.management.*; +import javax.net.ssl.*; import java.lang.management.*; import java.util.*; @@ -400,6 +402,9 @@ /** Cache store session listeners. */ private Factory[] storeSesLsnrs; + /** SSL connection factory. */ + private Factory sslCtxFactory; + /** * Creates valid grid configuration with all default values. */ @@ -480,6 +485,7 @@ segResolvers = cfg.getSegmentationResolvers(); sndRetryCnt = cfg.getNetworkSendRetryCount(); sndRetryDelay = cfg.getNetworkSendRetryDelay(); + sslCtxFactory = cfg.getSslContextFactory(); storeSesLsnrs = cfg.getCacheStoreSessionListenerFactories(); svcCfgs = cfg.getServiceConfiguration(); sysPoolSize = cfg.getSystemThreadPoolSize(); @@ -1307,6 +1313,28 @@ this.lifecycleBeans = lifecycleBeans; return this; + } + + /** + * Sets SSL context factory that will be used for creating a secure socket layer. + * + * @param sslCtxFactory Ssl context factory. + * @see SslContextFactory + */ + public IgniteConfiguration setSslContextFactory(Factory sslCtxFactory) { + this.sslCtxFactory = sslCtxFactory; + + return this; + } + + /** + * Returns SSL context factory that will be used for creating a secure socket layer. + * + * @return SSL connection factory. + * @see SslContextFactory + */ + public Factory getSslContextFactory() { + return sslCtxFactory; } /** Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestProtocol.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestProtocol.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/processors/rest/protocols/tcp/GridTcpRestProtocol.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -35,6 +35,7 @@ import org.apache.ignite.spi.*; import org.jetbrains.annotations.*; +import javax.cache.configuration.*; import javax.net.ssl.*; import java.io.*; import java.net.*; @@ -106,13 +107,23 @@ SSLContext sslCtx = null; if (cfg.isSslEnabled()) { - GridSslContextFactory factory = cfg.getSslContextFactory(); + Factory igniteFactory = ctx.config().getSslContextFactory(); - if (factory == null) + Factory factory = cfg.getSslFactory(); + + // This factory deprecated and will be removed. + GridSslContextFactory depFactory = cfg.getSslContextFactory(); + + if (factory == null && depFactory == null && igniteFactory == null) // Thrown SSL exception instead of IgniteCheckedException for writing correct warning message into log. throw new SSLException("SSL is enabled, but SSL context factory is not specified."); - sslCtx = factory.createSslContext(); + if (factory != null) + sslCtx = factory.create(); + else if (depFactory != null) + sslCtx = depFactory.createSslContext(); + else + sslCtx = igniteFactory.create(); } int lastPort = cfg.getPort() + cfg.getPortRange() - 1; Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioSessionMetaKey.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -42,7 +42,10 @@ MARSHALLER_ID, /** Message writer. */ - MSG_WRITER; + MSG_WRITER, + + /** SSL engine. */ + SSL_ENGINE; /** Maximum count of NIO session keys in system. */ public static final int MAX_KEYS_CNT = 64; Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslFilter.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslFilter.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslFilter.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -393,6 +393,8 @@ public static ByteBuffer expandBuffer(ByteBuffer original, int cap) { ByteBuffer res = ByteBuffer.allocate(cap); + res.order(ByteOrder.nativeOrder()); + original.flip(); res.put(original); Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslHandler.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslHandler.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/GridNioSslHandler.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -29,6 +29,7 @@ import static javax.net.ssl.SSLEngineResult.*; import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*; import static javax.net.ssl.SSLEngineResult.Status.*; +import static org.apache.ignite.internal.util.nio.GridNioSessionMetaKey.*; import static org.apache.ignite.internal.util.nio.ssl.GridNioSslFilter.*; /** @@ -96,8 +97,15 @@ sslEngine = engine; + if (ses.meta(SSL_ENGINE.ordinal()) == null) - sslEngine.beginHandshake(); + sslEngine.beginHandshake(); + else { + sslEngine = ses.meta(SSL_ENGINE.ordinal()); + handshakeFinished = true; + initHandshakeComplete = true; + } + handshakeStatus = sslEngine.getHandshakeStatus(); // Allocate a little bit more so SSL engine would not return buffer overflow status. @@ -113,6 +121,8 @@ int appBufSize = Math.max(sslEngine.getSession().getApplicationBufferSize() + 50, netBufSize * 2); appBuf = ByteBuffer.allocate(appBufSize); + + appBuf.order(ByteOrder.nativeOrder()); if (log.isDebugEnabled()) log.debug("Started SSL session [netBufSize=" + netBufSize + ", appBufSize=" + appBufSize + ']'); Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpi.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -40,6 +40,7 @@ import org.apache.ignite.spi.discovery.tcp.messages.*; import org.jetbrains.annotations.*; +import javax.net.ssl.*; import java.io.*; import java.net.*; import java.util.*; @@ -305,6 +306,12 @@ /** Node authenticator. */ protected DiscoverySpiNodeAuthenticator nodeAuth; + /** SSL server socket factory. */ + protected SSLServerSocketFactory sslSrvSocketFactory; + + /** SSL socket factory. */ + protected SSLSocketFactory sslSocketFactory; + /** Context initialization latch. */ @GridToStringExclude private final CountDownLatch ctxInitLatch = new CountDownLatch(1); @@ -1106,8 +1113,13 @@ assert addr != null; - Socket sock = new Socket(); + Socket sock; + if (isSslEnabled()) + sock = sslSocketFactory.createSocket(); + else + sock = new Socket(); + sock.bind(new InetSocketAddress(locHost, 0)); sock.setTcpNoDelay(true); @@ -1578,7 +1590,20 @@ assertParameter(threadPri > 0, "threadPri > 0"); assertParameter(statsPrintFreq >= 0, "statsPrintFreq >= 0"); + if (isSslEnabled()) { - try { + try { + SSLContext sslCtx = ignite().configuration().getSslContextFactory().create(); + + sslSocketFactory = sslCtx.getSocketFactory(); + sslSrvSocketFactory = sslCtx.getServerSocketFactory(); + } + catch (IgniteException e) { + throw new IgniteSpiException("Failed to create SSL context. SSL factory: " + + ignite.configuration().getSslContextFactory(), e); + } + } + + try { locHost = U.resolveLocalHost(locAddr); } catch (IOException e) { @@ -1692,6 +1717,13 @@ } return false; + } + + /** + * @return {@code True} if ssl enabled. + */ + boolean isSslEnabled() { + return ignite().configuration().getSslContextFactory() != null; } /** Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/ssl/package-info.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/ssl/package-info.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/ssl/package-info.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * + * Contains classes for working with SSL. + */ +package org.apache.ignite.ssl; \ No newline at end of file Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/GridAbstractCommunicationSelfTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/GridAbstractCommunicationSelfTest.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/GridAbstractCommunicationSelfTest.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -19,6 +19,7 @@ import org.apache.ignite.*; import org.apache.ignite.cluster.*; +import org.apache.ignite.configuration.*; import org.apache.ignite.internal.managers.communication.*; import org.apache.ignite.internal.util.typedef.*; import org.apache.ignite.internal.util.typedef.internal.*; @@ -59,6 +60,9 @@ /** */ private static final Object mux = new Object(); + /** */ + protected boolean useSsl = false; + /** * */ @@ -298,6 +302,15 @@ spiRsrcs.add(rsrcs); rsrcs.inject(spi); + + if (useSsl) { + IgniteMock ignite = GridTestUtils.getFieldValue(spi, IgniteSpiAdapter.class, "ignite"); + + IgniteConfiguration cfg = ignite.configuration() + .setSslContextFactory(GridTestUtils.sslFactory()); + + ignite.setStaticCfg(cfg); + } spi.setListener(new MessageListener(rsrcs.getNodeId())); Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/GridTcpCommunicationSpiSslSelfTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/GridTcpCommunicationSpiSslSelfTest.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/GridTcpCommunicationSpiSslSelfTest.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.spi.communication.tcp; + +import org.apache.ignite.testframework.junits.spi.*; + +/** + * + */ +@GridSpiTest(spi = TcpCommunicationSpi.class, group = "Communication SPI") +public class GridTcpCommunicationSpiSslSelfTest extends GridTcpCommunicationSpiAbstractTest { + /** */ + public GridTcpCommunicationSpiSslSelfTest() { + super(false); + + this.useSsl = true; + } + + /** {@inheritDoc} */ + @Override protected boolean tcpNoDelay() { + return true; + } +} Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/AbstractDiscoverySelfTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/AbstractDiscoverySelfTest.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/AbstractDiscoverySelfTest.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -19,6 +19,7 @@ import mx4j.tools.adaptor.http.*; import org.apache.ignite.cluster.*; +import org.apache.ignite.configuration.*; import org.apache.ignite.internal.util.typedef.internal.*; import org.apache.ignite.marshaller.*; import org.apache.ignite.spi.*; @@ -58,6 +59,9 @@ private static final String TEST_ATTRIBUTE_NAME = "test.node.prop"; /** */ + protected boolean useSsl = false; + + /** */ protected AbstractDiscoverySelfTest() { super(false); } @@ -393,6 +397,15 @@ GridSpiTestContext ctx = initSpiContext(); GridTestUtils.setFieldValue(spi, IgniteSpiAdapter.class, "spiCtx", ctx); + + if (useSsl) { + IgniteMock ignite = GridTestUtils.getFieldValue(spi, IgniteSpiAdapter.class, "ignite"); + + IgniteConfiguration cfg = ignite.configuration() + .setSslContextFactory(GridTestUtils.sslFactory()); + + ignite.setStaticCfg(cfg); + } spi.spiStart(getTestGridName() + i); Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiSslSelfTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiSslSelfTest.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySpiSslSelfTest.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.spi.discovery.tcp; + +/** + * TCP discovery spi test with SSL. + */ +public class TcpDiscoverySpiSslSelfTest extends TcpDiscoverySpiSelfTest { + /** */ + public TcpDiscoverySpiSslSelfTest() { + useSsl = true; + } +} Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySslSelfTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySslSelfTest.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/discovery/tcp/TcpDiscoverySslSelfTest.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.spi.discovery.tcp; + +import org.apache.ignite.configuration.*; +import org.apache.ignite.testframework.*; + +/** + * Test for {@link TcpDiscoverySpi} with SSL. + */ +public class TcpDiscoverySslSelfTest extends TcpDiscoverySelfTest { + /** + * @throws Exception If fails. + */ + public TcpDiscoverySslSelfTest() throws Exception { + super(); + } + + /** {@inheritDoc} */ + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + cfg.setSslContextFactory(GridTestUtils.sslFactory()); + + return cfg; + } +} Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testframework/GridTestUtils.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -32,10 +32,12 @@ import org.apache.ignite.internal.util.typedef.*; import org.apache.ignite.internal.util.typedef.internal.*; import org.apache.ignite.lang.*; +import org.apache.ignite.ssl.*; import org.apache.ignite.testframework.config.*; import org.jetbrains.annotations.*; import javax.cache.*; +import javax.cache.configuration.*; import javax.net.ssl.*; import java.io.*; import java.lang.annotation.*; @@ -1328,6 +1330,24 @@ */ public static GridSslContextFactory sslContextFactory() { GridSslBasicContextFactory factory = new GridSslBasicContextFactory(); + + factory.setKeyStoreFilePath( + U.resolveIgnitePath(GridTestProperties.getProperty("ssl.keystore.path")).getAbsolutePath()); + factory.setKeyStorePassword(GridTestProperties.getProperty("ssl.keystore.password").toCharArray()); + + factory.setTrustManagers(GridSslBasicContextFactory.getDisabledTrustManager()); + + return factory; + } + + + /** + * Creates test-purposed SSL context factory from test key store with disabled trust manager. + * + * @return SSL context factory used in test. + */ + public static Factory sslFactory() { + SslContextFactory factory = new SslContextFactory(); factory.setKeyStoreFilePath( U.resolveIgnitePath(GridTestProperties.getProperty("ssl.keystore.path")).getAbsolutePath()); Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testframework/junits/IgniteMock.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testframework/junits/IgniteMock.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testframework/junits/IgniteMock.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -52,6 +52,9 @@ /** */ private final String home; + /** */ + private IgniteConfiguration staticCfg; + /** * Mock values * @@ -84,6 +87,9 @@ /** {@inheritDoc} */ @Override public IgniteConfiguration configuration() { + if (staticCfg != null) + return staticCfg; + IgniteConfiguration cfg = new IgniteConfiguration(); cfg.setMarshaller(marshaller); @@ -297,5 +303,12 @@ /** {@inheritDoc} */ @Override public Affinity affinity(String cacheName) { return null; + } + + /** + * @param staticCfg Configuration. + */ + public void setStaticCfg(IgniteConfiguration staticCfg) { + this.staticCfg = staticCfg; } } Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteSpiDiscoverySelfTestSuite.java (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) @@ -60,6 +60,9 @@ suite.addTest(new TestSuite(TcpDiscoveryRestartTest.class)); suite.addTest(new TestSuite(TcpDiscoveryMultiThreadedTest.class)); + // SSL. + suite.addTest(new TestSuite(TcpDiscoverySslSelfTest.class)); + return suite; } } Index: ../incubator-ignite/parent/pom.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/parent/pom.xml (revision 1fd31892c7a87ecd1c5584b45aecd0b30e3411fb) +++ ../incubator-ignite/parent/pom.xml (revision 36dc9209441fa28c83b7e6f0d24156f679160a2e) @@ -244,7 +244,7 @@ Security APIs - org.apache.ignite.plugin.security + org.apache.ignite.plugin.security:org.apache.ignite.ssl MXBeans classes Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/ssl/SslContextFactory.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/ssl/SslContextFactory.java (revision f7317eaca8b0d39f044af6bb7bfa10b24adcd44a) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/ssl/SslContextFactory.java (revision f7317eaca8b0d39f044af6bb7bfa10b24adcd44a) @@ -0,0 +1,458 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.ssl; + +import org.apache.ignite.*; +import org.apache.ignite.internal.util.typedef.internal.*; + +import javax.cache.configuration.*; +import javax.net.ssl.*; +import java.io.*; +import java.security.*; +import java.security.cert.*; +import java.util.*; + +/** + * This SSL context factory that provides ssl context configuration with specified key + * and trust stores. + *

+ * In some cases it is useful to disable certificate validation of client side (e.g. when connecting + * to a server with self-signed certificate). This can be achieved by setting a disabled trust manager + * to this factory, which can be obtained by {@link #getDisabledTrustManager()} method: + *

+ *     SslContextFactory factory = new SslContextFactory();
+ *     factory.setTrustManagers(SslContextFactory.getDisabledTrustManager());
+ *     // Rest of initialization.
+ * 
+ */ +public class SslContextFactory implements Factory { + /** */ + private static final long serialVersionUID = 0L; + + /** Default key store type. */ + public static final String DFLT_STORE_TYPE = "JKS"; + + /** Default SSL protocol. */ + public static final String DFLT_SSL_PROTOCOL = "TLS"; + + /** Default key manager algorithm. */ + public static final String DFLT_KEY_ALGORITHM = "SunX509"; + + /** SSL protocol. */ + private String proto = DFLT_SSL_PROTOCOL; + + /** Key manager algorithm. */ + private String keyAlgorithm = DFLT_KEY_ALGORITHM; + + /** Key store type. */ + private String keyStoreType = DFLT_STORE_TYPE; + + /** Path to key store file */ + private String keyStoreFilePath; + + /** Key store password */ + private char[] keyStorePwd; + + /** Trust store type. */ + private String trustStoreType = DFLT_STORE_TYPE; + + /** Path to trust store. */ + private String trustStoreFilePath; + + /** Trust store password */ + private char[] trustStorePwd; + + /** Trust managers. */ + private TrustManager[] trustMgrs; + + /** + * Gets key store type used for context creation. + * + * @return Key store type. + */ + public String getKeyStoreType() { + return keyStoreType; + } + + /** + * Sets key store type used in context initialization. If not provided, {@link #DFLT_STORE_TYPE} will + * be used. + * + * @param keyStoreType Key store type. + */ + public void setKeyStoreType(String keyStoreType) { + A.notNull(keyStoreType, "keyStoreType"); + + this.keyStoreType = keyStoreType; + } + + /** + * Gets trust store type used for context creation. + * + * @return trust store type. + */ + public String getTrustStoreType() { + return trustStoreType; + } + + /** + * Sets trust store type used in context initialization. If not provided, {@link #DFLT_STORE_TYPE} will + * be used. + * + * @param trustStoreType Trust store type. + */ + public void setTrustStoreType(String trustStoreType) { + A.notNull(trustStoreType, "trustStoreType"); + + this.trustStoreType = trustStoreType; + } + + /** + * Gets protocol for secure transport. + * + * @return SSL protocol name. + */ + public String getProtocol() { + return proto; + } + + /** + * Sets protocol for secure transport. If not specified, {@link #DFLT_SSL_PROTOCOL} will be used. + * + * @param proto SSL protocol name. + */ + public void setProtocol(String proto) { + A.notNull(proto, "proto"); + + this.proto = proto; + } + + /** + * Gets algorithm that will be used to create a key manager. If not specified, {@link #DFLT_KEY_ALGORITHM} + * will be used. + * + * @return Key manager algorithm. + */ + public String getKeyAlgorithm() { + return keyAlgorithm; + } + + /** + * Sets key manager algorithm that will be used to create a key manager. Notice that in most cased default value + * suites well, however, on Android platform this value need to be set to X509. + * + * @param keyAlgorithm Key algorithm name. + */ + public void setKeyAlgorithm(String keyAlgorithm) { + A.notNull(keyAlgorithm, "keyAlgorithm"); + + this.keyAlgorithm = keyAlgorithm; + } + + /** + * Gets path to the key store file. + * + * @return Path to key store file. + */ + public String getKeyStoreFilePath() { + return keyStoreFilePath; + } + + /** + * Sets path to the key store file. This is a mandatory parameter since + * ssl context could not be initialized without key manager. + * + * @param keyStoreFilePath Path to key store file. + */ + public void setKeyStoreFilePath(String keyStoreFilePath) { + A.notNull(keyStoreFilePath, "keyStoreFilePath"); + + this.keyStoreFilePath = keyStoreFilePath; + } + + /** + * Gets key store password. + * + * @return Key store password. + */ + public char[] getKeyStorePassword() { + return keyStorePwd; + } + + /** + * Sets key store password. + * + * @param keyStorePwd Key store password. + */ + public void setKeyStorePassword(char[] keyStorePwd) { + A.notNull(keyStorePwd, "keyStorePwd"); + + this.keyStorePwd = keyStorePwd; + } + + /** + * Gets path to the trust store file. + * + * @return Path to the trust store file. + */ + public String getTrustStoreFilePath() { + return trustStoreFilePath; + } + + /** + * Sets path to the trust store file. This is an optional parameter, + * however one of the {@code setTrustStoreFilePath(String)}, {@link #setTrustManagers(TrustManager[])} + * properties must be set. + * + * @param trustStoreFilePath Path to the trust store file. + */ + public void setTrustStoreFilePath(String trustStoreFilePath) { + this.trustStoreFilePath = trustStoreFilePath; + } + + /** + * Gets trust store password. + * + * @return Trust store password. + */ + public char[] getTrustStorePassword() { + return trustStorePwd; + } + + /** + * Sets trust store password. + * + * @param trustStorePwd Trust store password. + */ + public void setTrustStorePassword(char[] trustStorePwd) { + this.trustStorePwd = trustStorePwd; + } + + /** + * Gets pre-configured trust managers. + * + * @return Trust managers. + */ + public TrustManager[] getTrustManagers() { + return trustMgrs; + } + + /** + * Sets pre-configured trust managers. This is an optional parameter, + * however one of the {@link #setTrustStoreFilePath(String)}, {@code #setTrustManagers(TrustManager[])} + * + * @param trustMgrs Pre-configured trust managers. + */ + public void setTrustManagers(TrustManager... trustMgrs) { + this.trustMgrs = trustMgrs; + } + + /** + * Returns an instance of trust manager that will always succeed regardless of certificate provided. + * + * @return Trust manager instance. + */ + public static TrustManager getDisabledTrustManager() { + return new DisabledX509TrustManager(); + } + + /** + * Creates SSL context based on factory settings. + * + * @return Initialized SSL context. + * @throws SSLException If SSL context could not be created. + */ + private SSLContext createSslContext() throws SSLException { + checkParameters(); + + try { + KeyManagerFactory keyMgrFactory = KeyManagerFactory.getInstance(keyAlgorithm); + + KeyStore keyStore = loadKeyStore(keyStoreType, keyStoreFilePath, keyStorePwd); + + keyMgrFactory.init(keyStore, keyStorePwd); + + TrustManager[] mgrs = trustMgrs; + + if (mgrs == null) { + TrustManagerFactory trustMgrFactory = TrustManagerFactory.getInstance(keyAlgorithm); + + KeyStore trustStore = loadKeyStore(trustStoreType, trustStoreFilePath, trustStorePwd); + + trustMgrFactory.init(trustStore); + + mgrs = trustMgrFactory.getTrustManagers(); + } + + SSLContext ctx = SSLContext.getInstance(proto); + + ctx.init(keyMgrFactory.getKeyManagers(), mgrs, null); + + return ctx; + } + catch (GeneralSecurityException e) { + throw new SSLException("Failed to initialize SSL context " + parameters(), e); + } + } + + /** + * Builds human-readable string with factory parameters. + * + * @return Parameters string. + */ + private String parameters() { + StringBuilder buf = new StringBuilder("[keyStoreType=").append(keyStoreType); + + buf.append(", proto=").append(proto).append(", keyStoreFile=").append(keyStoreFilePath); + + if (trustMgrs != null) + buf.append(", trustMgrs=").append(Arrays.toString(trustMgrs)); + else + buf.append(", trustStoreFile=").append(trustStoreFilePath); + + buf.append(']'); + + return buf.toString(); + } + + /** + * Checks that all required parameters are set. + * + * @throws SSLException If any of required parameters is missing. + */ + private void checkParameters() throws SSLException { + assert keyStoreType != null; + assert proto != null; + + checkNullParameter(keyStoreFilePath, "keyStoreFilePath"); + checkNullParameter(keyStorePwd, "keyStorePwd"); + + if (trustMgrs == null) { + if (trustStoreFilePath == null) + throw new SSLException("Failed to initialize SSL context (either trustStoreFilePath or " + + "trustManagers must be provided)"); + else + checkNullParameter(trustStorePwd, "trustStorePwd"); + } + } + + /** + * @param param Value. + * @param name Name. + * @throws SSLException If {@code null}. + */ + private void checkNullParameter(Object param, String name) throws SSLException { + if (param == null) + throw new SSLException("Failed to initialize SSL context (parameter cannot be null): " + name); + } + + /** + * By default, this method simply opens a raw file input stream. Subclasses may override this method + * if some specific location should be handled (this may be a case for Android users). + * + * @param filePath Path to the file. + * @return Opened input stream. + * @throws IOException If stream could not be opened. + */ + protected InputStream openFileInputStream(String filePath) throws IOException { + return new FileInputStream(filePath); + } + + /** + * Loads key store with configured parameters. + * + * @param keyStoreType Type of key store. + * @param storeFilePath Path to key store file. + * @param keyStorePwd Store password. + * @return Initialized key store. + * @throws SSLException If key store could not be initialized. + */ + private KeyStore loadKeyStore(String keyStoreType, String storeFilePath, char[] keyStorePwd) throws SSLException { + InputStream input = null; + + try { + KeyStore keyStore = KeyStore.getInstance(keyStoreType); + + input = openFileInputStream(storeFilePath); + + keyStore.load(input, keyStorePwd); + + return keyStore; + } + catch (GeneralSecurityException e) { + throw new SSLException("Failed to initialize key store (security exception occurred) [type=" + + keyStoreType + ", keyStorePath=" + storeFilePath + ']', e); + } + catch (FileNotFoundException e) { + throw new SSLException("Failed to initialize key store (key store file was not found): [path=" + + storeFilePath + ", msg=" + e.getMessage() + ']'); + } + catch (IOException e) { + throw new SSLException("Failed to initialize key store (I/O error occurred): " + storeFilePath, e); + } + finally { + if (input != null) { + try { + input.close(); + } + catch (IOException ignored) { + } + } + } + } + + /** {@inheritDoc} */ + public String toString() { + return getClass().getSimpleName() + parameters(); + } + + /** + * Disabled trust manager, will skip all certificate checks. + */ + private static class DisabledX509TrustManager implements X509TrustManager { + /** Empty certificate array. */ + private static final X509Certificate[] CERTS = new X509Certificate[0]; + + /** {@inheritDoc} */ + @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) + throws CertificateException { + // No-op, all clients are trusted. + } + + /** {@inheritDoc} */ + @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) + throws CertificateException { + // No-op, all servers are trusted. + } + + /** {@inheritDoc} */ + @Override public X509Certificate[] getAcceptedIssuers() { + return CERTS; + } + } + + /** {@inheritDoc} */ + @Override public SSLContext create() { + try { + return createSslContext(); + } + catch (SSLException e) { + throw new IgniteException(e); + } + } +} Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/configuration/ConnectorConfiguration.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/configuration/ConnectorConfiguration.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/configuration/ConnectorConfiguration.java (revision 2650845e7b3770bfc22491fb5abc5f026e33c745) @@ -19,8 +19,12 @@ import org.apache.ignite.*; import org.apache.ignite.internal.client.ssl.*; +import org.apache.ignite.ssl.*; + import org.jetbrains.annotations.*; +import javax.cache.configuration.*; +import javax.net.ssl.*; import java.net.*; /** @@ -99,6 +103,9 @@ /** SSL context factory for rest binary server. */ private GridSslContextFactory sslCtxFactory; + /** SSL context factory for rest binary server. */ + private Factory sslFactory; + /** Port range */ private int portRange = DFLT_PORT_RANGE; @@ -429,7 +436,9 @@ * * @return SslContextFactory instance. * @see GridSslContextFactory + * @deprecated Use {@link #getSslFactory()} instead. */ + @Deprecated public GridSslContextFactory getSslContextFactory() { return sslCtxFactory; } @@ -440,9 +449,32 @@ * {@link #setSslEnabled(boolean)} is set to {@code true}. * * @param sslCtxFactory Instance of {@link GridSslContextFactory} + * @deprecated Use {@link #setSslFactory(Factory)} instead. */ + @Deprecated public void setSslContextFactory(GridSslContextFactory sslCtxFactory) { this.sslCtxFactory = sslCtxFactory; + } + + /** + * Gets context factory that will be used for creating a secure socket layer of rest binary server. + * + * @return SSL context factory instance. + * @see SslContextFactory + */ + public Factory getSslFactory() { + return sslFactory; + } + + /** + * Sets instance of {@link Factory} that will be used to create an instance of {@code SSLContext} + * for Secure Socket Layer on TCP binary protocol. This factory will only be used if + * {@link #setSslEnabled(boolean)} is set to {@code true}. + * + * @param sslFactory Instance of {@link Factory} + */ + public void setSslFactory(Factory sslFactory) { + this.sslFactory = sslFactory; } /** Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/client/ssl/GridSslBasicContextFactory.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/client/ssl/GridSslBasicContextFactory.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/client/ssl/GridSslBasicContextFactory.java (revision 2650845e7b3770bfc22491fb5abc5f026e33c745) @@ -19,6 +19,7 @@ import org.apache.ignite.internal.util.typedef.internal.*; +import javax.cache.configuration.*; import javax.net.ssl.*; import java.io.*; import java.security.*; @@ -37,7 +38,9 @@ * factory.setTrustManagers(GridSslBasicContextFactory.getDisabledTrustManager()); * // Rest of initialization. * + * @deprecated Use {@link Factory} instead. */ +@Deprecated public class GridSslBasicContextFactory implements GridSslContextFactory { /** Default key store type. */ public static final String DFLT_STORE_TYPE = "JKS"; Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/client/ssl/GridSslContextFactory.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/client/ssl/GridSslContextFactory.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/client/ssl/GridSslContextFactory.java (revision 2650845e7b3770bfc22491fb5abc5f026e33c745) @@ -17,14 +17,17 @@ package org.apache.ignite.internal.client.ssl; +import javax.cache.configuration.*; import javax.net.ssl.*; /** * This interface provides creation of SSL context both for server and client use. *

* Usually, it is enough to configure context from a particular key and trust stores, this functionality is provided - * in {@link org.apache.ignite.internal.client.ssl.GridSslBasicContextFactory}. + * in {@link GridSslBasicContextFactory}. + * @deprecated Use {@link Factory} instead. */ +@Deprecated public interface GridSslContextFactory { /** * Creates SSL context based on factory settings. Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/IgniteKernal.java (revision e37efa3357d96e7831068eaec29627bd1bcc2ba0) @@ -2064,8 +2064,8 @@ private void ackSecurity() { assert log != null; - if (log.isInfoEnabled()) - log.info("Security status [authentication=" + onOff(ctx.security().enabled()) + ']'); + U.quietAndInfo(log, "Security status [authentication=" + onOff(ctx.security().enabled()) + + ", communication encrypted=" + onOff(ctx.config().getSslContextFactory() != null) + ']'); } /** Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/BlockingSslHandler.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/BlockingSslHandler.java (revision e37efa3357d96e7831068eaec29627bd1bcc2ba0) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/ssl/BlockingSslHandler.java (revision e37efa3357d96e7831068eaec29627bd1bcc2ba0) @@ -0,0 +1,486 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.util.nio.ssl; + +import org.apache.ignite.*; +import org.apache.ignite.internal.util.future.*; +import org.apache.ignite.internal.util.nio.*; +import org.apache.ignite.internal.util.typedef.internal.*; + +import javax.net.ssl.*; +import javax.net.ssl.SSLEngineResult.*; +import java.io.*; +import java.nio.*; +import java.nio.channels.*; + +import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*; +import static javax.net.ssl.SSLEngineResult.Status.*; +import static org.apache.ignite.internal.util.nio.ssl.GridNioSslFilter.*; + +/** + * + */ +public class BlockingSslHandler { + /** Logger. */ + private IgniteLogger log; + + /** Socket channel. */ + private SocketChannel ch; + + /** Order. */ + private final ByteOrder order; + + /** SSL engine. */ + private final SSLEngine sslEngine; + + /** Handshake completion flag. */ + private boolean handshakeFinished; + + /** Engine handshake status. */ + private HandshakeStatus handshakeStatus; + + /** Output buffer into which encrypted data will be written. */ + private ByteBuffer outNetBuf; + + /** Input buffer from which SSL engine will decrypt data. */ + private ByteBuffer inNetBuf; + + /** Empty buffer used in handshake procedure. */ + private ByteBuffer handshakeBuf = ByteBuffer.allocate(0); + + /** Application buffer. */ + private ByteBuffer appBuf; + + /** + * @param sslEngine SSLEngine. + * @param ch Socket channel. + * @param directBuf Direct buffer flag. + * @param order Byte order. + * @param log Logger. + */ + public BlockingSslHandler(SSLEngine sslEngine, + SocketChannel ch, + boolean directBuf, + ByteOrder order, + IgniteLogger log) + throws SSLException { + this.ch = ch; + this.log = log; + this.sslEngine = sslEngine; + this.order = order; + + // Allocate a little bit more so SSL engine would not return buffer overflow status. + int netBufSize = sslEngine.getSession().getPacketBufferSize() + 50; + + outNetBuf = directBuf ? ByteBuffer.allocateDirect(netBufSize) : ByteBuffer.allocate(netBufSize); + outNetBuf.order(order); + + // Initially buffer is empty. + outNetBuf.position(0); + outNetBuf.limit(0); + + inNetBuf = directBuf ? ByteBuffer.allocateDirect(netBufSize) : ByteBuffer.allocate(netBufSize); + inNetBuf.order(order); + + appBuf = allocateAppBuff(); + + handshakeStatus = sslEngine.getHandshakeStatus(); + + if (log.isDebugEnabled()) + log.debug("Started SSL session [netBufSize=" + netBufSize + ", appBufSize=" + appBuf.capacity() + ']'); + } + + /** + * Performs handshake procedure with remote peer. + * + * @throws GridNioException If filter processing has thrown an exception. + * @throws SSLException If failed to process SSL data. + */ + public boolean handshake() throws IgniteCheckedException, SSLException { + if (log.isDebugEnabled()) + log.debug("Entered handshake. Handshake status: " + handshakeStatus + '.'); + + sslEngine.beginHandshake(); + + handshakeStatus = sslEngine.getHandshakeStatus(); + + boolean loop = true; + + while (loop) { + switch (handshakeStatus) { + case NOT_HANDSHAKING: + case FINISHED: { + handshakeFinished = true; + + loop = false; + + break; + } + + case NEED_TASK: { + handshakeStatus = runTasks(); + + break; + } + + case NEED_UNWRAP: { + Status status = unwrapHandshake(); + + handshakeStatus = sslEngine.getHandshakeStatus(); + + if (status == BUFFER_UNDERFLOW && sslEngine.isInboundDone()) + // Either there is no enough data in buffer or session was closed. + loop = false; + + break; + } + + case NEED_WRAP: { + // If the output buffer has remaining data, clear it. + if (outNetBuf.hasRemaining()) + U.warn(log, "Output net buffer has unsent bytes during handshake (will clear). "); + + outNetBuf.clear(); + + SSLEngineResult res = sslEngine.wrap(handshakeBuf, outNetBuf); + + outNetBuf.flip(); + + handshakeStatus = res.getHandshakeStatus(); + + if (log.isDebugEnabled()) + log.debug("Wrapped handshake data [status=" + res.getStatus() + ", handshakeStatus=" + + handshakeStatus + ']'); + + writeNetBuffer(); + + break; + } + + default: { + throw new IllegalStateException("Invalid handshake status in handshake method [handshakeStatus=" + + handshakeStatus + ']'); + } + } + } + + if (log.isDebugEnabled()) + log.debug("Leaved handshake. Handshake status:" + handshakeStatus + '.'); + + return handshakeFinished; + } + + /** + * @return Application buffer with decoded data. + */ + public ByteBuffer applicationBuffer() { + appBuf.flip(); + + return appBuf; + } + + /** + * Encrypts data to be written to the network. + * + * @param src data to encrypt. + * @throws SSLException on errors. + * @return Output buffer with encrypted data. + */ + public ByteBuffer encrypt(ByteBuffer src) throws SSLException { + assert handshakeFinished; + + // The data buffer is (must be) empty, we can reuse the entire + // buffer. + outNetBuf.clear(); + + // Loop until there is no more data in src + while (src.hasRemaining()) { + int outNetRemaining = outNetBuf.capacity() - outNetBuf.position(); + + if (outNetRemaining < src.remaining() * 2) { + outNetBuf = expandBuffer(outNetBuf, Math.max( + outNetBuf.position() + src.remaining() * 2, outNetBuf.capacity() * 2)); + + if (log.isDebugEnabled()) + log.debug("Expanded output net buffer: " + outNetBuf.capacity()); + } + + SSLEngineResult res = sslEngine.wrap(src, outNetBuf); + + if (log.isDebugEnabled()) + log.debug("Encrypted data [status=" + res.getStatus() + ", handshakeStaus=" + + res.getHandshakeStatus() + ']'); + + if (res.getStatus() == OK) { + if (res.getHandshakeStatus() == NEED_TASK) + runTasks(); + } + else + throw new SSLException("Failed to encrypt data (SSL engine error) [status=" + res.getStatus() + + ", handshakeStatus=" + res.getHandshakeStatus() + ']'); + } + + outNetBuf.flip(); + + return outNetBuf; + } + + /** + * Called by SSL filter when new message was received. + * + * @param buf Received message. + * @throws GridNioException If exception occurred while forwarding events to underlying filter. + * @throws SSLException If failed to process SSL data. + */ + public ByteBuffer decode(ByteBuffer buf) throws IgniteCheckedException, SSLException { + inNetBuf.clear(); + + if (buf.limit() > inNetBuf.remaining()) { + inNetBuf = expandBuffer(inNetBuf, inNetBuf.capacity() + buf.limit() * 2); + + appBuf = expandBuffer(appBuf, inNetBuf.capacity() * 2); + + if (log.isDebugEnabled()) + log.debug("Expanded buffers [inNetBufCapacity=" + inNetBuf.capacity() + ", appBufCapacity=" + + appBuf.capacity() + ']'); + } + + // append buf to inNetBuffer + inNetBuf.put(buf); + + if (!handshakeFinished) + handshake(); + else + unwrapData(); + + if (isInboundDone()) { + int newPosition = buf.position() - inNetBuf.position(); + + if (newPosition >= 0) { + buf.position(newPosition); + + // If we received close_notify but not all bytes has been read by SSL engine, print a warning. + if (buf.hasRemaining()) + U.warn(log, "Got unread bytes after receiving close_notify message (will ignore)."); + } + + inNetBuf.clear(); + } + + appBuf.flip(); + + return appBuf; + } + + /** + * @return {@code True} if inbound data stream has ended, i.e. SSL engine received + * close_notify message. + */ + boolean isInboundDone() { + return sslEngine.isInboundDone(); + } + + /** + * Unwraps user data to the application buffer. + * + * @throws SSLException If failed to process SSL data. + * @throws GridNioException If failed to pass events to the next filter. + */ + private void unwrapData() throws IgniteCheckedException, SSLException { + if (log.isDebugEnabled()) + log.debug("Unwrapping received data."); + + // Flip buffer so we can read it. + inNetBuf.flip(); + + SSLEngineResult res = unwrap0(); + + // prepare to be written again + inNetBuf.compact(); + + checkStatus(res); + + renegotiateIfNeeded(res); + } + + /** + * Runs all tasks needed to continue SSL work. + * + * @return Handshake status after running all tasks. + */ + private HandshakeStatus runTasks() { + Runnable runnable; + + while ((runnable = sslEngine.getDelegatedTask()) != null) { + if (log.isDebugEnabled()) + log.debug("Running SSL engine task: " + runnable + '.'); + + runnable.run(); + } + + if (log.isDebugEnabled()) + log.debug("Finished running SSL engine tasks. HandshakeStatus: " + sslEngine.getHandshakeStatus()); + + return sslEngine.getHandshakeStatus(); + } + + + /** + * Unwraps handshake data and processes it. + * + * @return Status. + * @throws SSLException If SSL exception occurred while unwrapping. + * @throws GridNioException If failed to pass event to the next filter. + */ + private Status unwrapHandshake() throws SSLException, IgniteCheckedException { + // Flip input buffer so we can read the collected data. + readFromNet(); + + inNetBuf.flip(); + + SSLEngineResult res = unwrap0(); + handshakeStatus = res.getHandshakeStatus(); + + checkStatus(res); + + // If handshake finished, no data was produced, and the status is still ok, + // try to unwrap more + if (handshakeStatus == FINISHED && res.getStatus() == OK && inNetBuf.hasRemaining()) { + res = unwrap0(); + + handshakeStatus = res.getHandshakeStatus(); + + // prepare to be written again + inNetBuf.compact(); + + renegotiateIfNeeded(res); + } + else + // prepare to be written again + inNetBuf.compact(); + + return res.getStatus(); + } + + /** + * Performs raw unwrap from network read buffer. + * + * @return Result. + * @throws SSLException If SSL exception occurs. + */ + private SSLEngineResult unwrap0() throws SSLException { + SSLEngineResult res; + + do { + res = sslEngine.unwrap(inNetBuf, appBuf); + + if (log.isDebugEnabled()) + log.debug("Unwrapped raw data [status=" + res.getStatus() + ", handshakeStatus=" + + res.getHandshakeStatus() + ']'); + + if (res.getStatus() == Status.BUFFER_OVERFLOW) + appBuf = expandBuffer(appBuf, appBuf.capacity() * 2); + } + while ((res.getStatus() == OK || res.getStatus() == Status.BUFFER_OVERFLOW) && + (handshakeFinished && res.getHandshakeStatus() == NOT_HANDSHAKING + || res.getHandshakeStatus() == NEED_UNWRAP)); + + return res; + } + + /** + * @param res SSL engine result. + * @throws SSLException If status is not acceptable. + */ + private void checkStatus(SSLEngineResult res) + throws SSLException { + + Status status = res.getStatus(); + + if (status != OK && status != CLOSED && status != BUFFER_UNDERFLOW) + throw new SSLException("Failed to unwrap incoming data (SSL engine error). Status: " + status); + } + + /** + * Check status and retry the negotiation process if needed. + * + * @param res Result. + * @throws GridNioException If exception occurred during handshake. + * @throws SSLException If failed to process SSL data + */ + private void renegotiateIfNeeded(SSLEngineResult res) throws IgniteCheckedException, SSLException { + if (res.getStatus() != CLOSED && res.getStatus() != BUFFER_UNDERFLOW + && res.getHandshakeStatus() != NOT_HANDSHAKING) { + // Renegotiation required. + handshakeStatus = res.getHandshakeStatus(); + + if (log.isDebugEnabled()) + log.debug("Renegotiation requested [status=" + res.getStatus() + ", handshakeStatus = " + + handshakeStatus + ']'); + + handshakeFinished = false; + + handshake(); + } + } + + /** + * Allocate application buffer. + */ + private ByteBuffer allocateAppBuff() { + int netBufSize = sslEngine.getSession().getPacketBufferSize() + 50; + + int appBufSize = Math.max(sslEngine.getSession().getApplicationBufferSize() + 50, netBufSize * 2); + + ByteBuffer buf = ByteBuffer.allocate(appBufSize); + buf.order(order); + + return buf; + } + + /** + * Read data from net buffer. + */ + private void readFromNet() throws IgniteCheckedException { + try { + inNetBuf.clear(); + + int read = ch.read(inNetBuf); + + if (read == -1) + throw new IgniteCheckedException("Failed to read remote node ID (connection closed)."); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to write byte to socket.", e); + } + } + + /** + * Copies data from out net buffer and passes it to the underlying chain. + * + * @throws GridNioException If send failed. + */ + private void writeNetBuffer() throws IgniteCheckedException { + try { + ch.write(outNetBuf); + } + catch (IOException e) { + throw new IgniteCheckedException("Failed to write byte to socket.", e); + } + } +} Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/discovery/tcp/ServerImpl.java (revision e37efa3357d96e7831068eaec29627bd1bcc2ba0) @@ -39,6 +39,7 @@ import org.jetbrains.annotations.*; import org.jsr166.*; +import javax.net.ssl.*; import java.io.*; import java.net.*; import java.util.*; @@ -3975,7 +3976,10 @@ for (port = spi.locPort; port < spi.locPort + spi.locPortRange; port++) { try { + if (spi.isSslEnabled()) + srvrSock = spi.sslSrvSocketFactory.createServerSocket(port, 0, spi.locHost); + else - srvrSock = new ServerSocket(port, 0, spi.locHost); + srvrSock = new ServerSocket(port, 0, spi.locHost); break; } @@ -4120,12 +4124,16 @@ if (!Arrays.equals(buf, U.IGNITE_HEADER)) { if (log.isDebugEnabled()) log.debug("Unknown connection detected (is some other software connecting to " + - "this Ignite port?) " + + "this Ignite port?" + + (!spi.isSslEnabled() ? " missed SSL configuration?" : "" ) + + ") " + "[rmtAddr=" + sock.getRemoteSocketAddress() + ", locAddr=" + sock.getLocalSocketAddress() + ']'); LT.warn(log, null, "Unknown connection detected (is some other software connecting to " + - "this Ignite port?) [rmtAddr=" + sock.getRemoteSocketAddress() + + "this Ignite port?" + + (!spi.isSslEnabled() ? " missed SSL configuration?" : "" ) + + ") [rmtAddr=" + sock.getRemoteSocketAddress() + ", locAddr=" + sock.getLocalSocketAddress() + ']'); return; @@ -4234,7 +4242,11 @@ if (log.isDebugEnabled()) U.error(log, "Caught exception on handshake [err=" + e +", sock=" + sock + ']', e); - if (X.hasCause(e, ObjectStreamException.class) || !sock.isClosed()) { + if (X.hasCause(e, SSLException.class) && spi.isSslEnabled() && !spi.isNodeStopping0()) + LT.warn(log, null, "Failed to initialize connection. Not encrypted data received. " + + "Missed SSL configuration on node? [sock=" + sock + ']'); + else if ((X.hasCause(e, ObjectStreamException.class) || !sock.isClosed()) + && !spi.isNodeStopping0()) { if (U.isMacInvalidArgumentError(e)) LT.error(log, e, "Failed to initialize connection [sock=" + sock + "]\n\t" + U.MAC_INVALID_ARG_MSG); Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/IgniteCacheSslStartStopSelfTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/IgniteCacheSslStartStopSelfTest.java (revision e37efa3357d96e7831068eaec29627bd1bcc2ba0) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/spi/communication/tcp/IgniteCacheSslStartStopSelfTest.java (revision e37efa3357d96e7831068eaec29627bd1bcc2ba0) @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.spi.communication.tcp; + +import org.apache.ignite.cache.*; +import org.apache.ignite.configuration.*; +import org.apache.ignite.internal.processors.cache.distributed.dht.*; +import org.apache.ignite.testframework.*; + +/** + * + */ +public class IgniteCacheSslStartStopSelfTest extends IgniteCachePutRetryAbstractSelfTest { + @Override protected IgniteConfiguration getConfiguration(String gridName) throws Exception { + IgniteConfiguration cfg = super.getConfiguration(gridName); + + cfg.setSslContextFactory(GridTestUtils.sslFactory()); + + return cfg; + } + + /** {@inheritDoc} */ + @Override protected CacheAtomicityMode atomicityMode() { + return CacheAtomicityMode.ATOMIC; + } + + /** {@inheritDoc} */ + @Override protected int keysCount() { + return 60_000; + } +} Index: ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFailoverTestSuite.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFailoverTestSuite.java (revision c5dc4926aaa44178dad6cdad5fdd7f273d4da6ed) +++ ../incubator-ignite/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheFailoverTestSuite.java (revision e37efa3357d96e7831068eaec29627bd1bcc2ba0) @@ -23,7 +23,7 @@ import org.apache.ignite.internal.processors.cache.distributed.dht.*; import org.apache.ignite.internal.processors.cache.distributed.dht.atomic.*; import org.apache.ignite.internal.processors.cache.distributed.near.*; -import org.apache.ignite.internal.processors.cache.distributed.replicated.*; +import org.apache.ignite.spi.communication.tcp.*; import org.apache.ignite.testframework.*; import java.util.*; @@ -74,6 +74,8 @@ suite.addTestSuite(IgniteCachePutRetryAtomicSelfTest.class); suite.addTestSuite(IgniteCachePutRetryTransactionalSelfTest.class); + + suite.addTestSuite(IgniteCacheSslStartStopSelfTest.class); return suite; } Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/communication/tcp/TcpCommunicationSpi.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/communication/tcp/TcpCommunicationSpi.java (revision 9cab55adab3cc23c20cbf86512e8faa446ac6929) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/spi/communication/tcp/TcpCommunicationSpi.java (revision f49db319d7ff29259b46c4fb9850ac61c3394e83) @@ -29,6 +29,7 @@ import org.apache.ignite.internal.util.ipc.shmem.*; import org.apache.ignite.internal.util.lang.*; import org.apache.ignite.internal.util.nio.*; +import org.apache.ignite.internal.util.nio.ssl.*; import org.apache.ignite.internal.util.typedef.*; import org.apache.ignite.internal.util.typedef.internal.*; import org.apache.ignite.internal.util.worker.*; @@ -41,6 +42,7 @@ import org.jetbrains.annotations.*; import org.jsr166.*; +import javax.net.ssl.*; import java.io.*; import java.net.*; import java.nio.*; @@ -747,6 +749,13 @@ }; /** + * @return {@code True} if ssl enabled. + */ + private boolean isSslEnabled() { + return ignite.configuration().getSslContextFactory() != null; + } + + /** * Sets address resolver. * * @param addrRslvr Address resolver. @@ -1465,6 +1474,29 @@ } : null; + GridNioFilter[] filters; + + if (isSslEnabled()) { + GridNioSslFilter sslFilter = + new GridNioSslFilter(ignite.configuration().getSslContextFactory().create(), log); + + sslFilter.directMode(true); + + sslFilter.wantClientAuth(true); + sslFilter.needClientAuth(true); + + filters = new GridNioFilter[] { + new GridNioCodecFilter(parser, log, true), + new GridConnectionBytesVerifyFilter(log), + sslFilter + }; + } + else + filters = new GridNioFilter[] { + new GridNioCodecFilter(parser, log, true), + new GridConnectionBytesVerifyFilter(log) + }; + GridNioServer srvr = GridNioServer.builder() .address(locHost) @@ -1482,8 +1514,7 @@ .directMode(true) .metricsListener(metricsLsnr) .writeTimeout(sockWriteTimeout) - .filters(new GridNioCodecFilter(parser, log, true), - new GridConnectionBytesVerifyFilter(log)) + .filters(filters) .messageFormatter(msgFormatter) .skipRecoveryPredicate(skipRecoveryPred) .messageQueueSizeListener(queueSizeMonitor) @@ -1501,6 +1532,10 @@ return srvr; } catch (IgniteCheckedException e) { + if (X.hasCause(e, SSLException.class)) + throw new IgniteSpiException("Failed to create SSL context. SSL factory: " + + ignite.configuration().getSslContextFactory() + '.', e); + lastEx = e; if (log.isDebugEnabled()) @@ -1872,7 +1907,7 @@ } try { - safeHandshake(client, null, node.id(), connTimeout0); + safeHandshake(client, null, node.id(), connTimeout0, null); } catch (HandshakeTimeoutException e) { if (log.isDebugEnabled()) @@ -2019,11 +2054,19 @@ long rcvCnt = -1; + SSLEngine sslEngine = null; + try { ch.socket().connect(addr, (int)connTimeout); - rcvCnt = safeHandshake(ch, recoveryDesc, node.id(), connTimeout0); + if (isSslEnabled()) { + sslEngine = ignite.configuration().getSslContextFactory().create().createSSLEngine(); + sslEngine.setUseClientMode(true); + } + + rcvCnt = safeHandshake(ch, recoveryDesc, node.id(), connTimeout0, sslEngine); + if (rcvCnt == -1) return null; } @@ -2037,6 +2080,11 @@ meta.put(NODE_ID_META, node.id()); + if (isSslEnabled()) { + assert sslEngine != null; + + meta.put(GridNioSessionMetaKey.SSL_ENGINE.ordinal(), sslEngine); + } if (recoveryDesc != null) { recoveryDesc.onHandshake(rcvCnt); @@ -2161,6 +2209,7 @@ * @param recovery Recovery descriptor if use recovery handshake, otherwise {@code null}. * @param rmtNodeId Remote node. * @param timeout Timeout for handshake. + * @param ssl SSL engine if used cryptography, otherwise {@code null}. * @throws IgniteCheckedException If handshake failed or wasn't completed withing timeout. * @return Handshake response. */ @@ -2169,7 +2218,8 @@ T client, @Nullable GridNioRecoveryDescriptor recovery, UUID rmtNodeId, - long timeout + long timeout, + @Nullable SSLEngine ssl ) throws IgniteCheckedException { HandshakeTimeoutObject obj = new HandshakeTimeoutObject<>(client, U.currentTimeMillis() + timeout); @@ -2186,16 +2236,45 @@ boolean success = false; try { - ByteBuffer buf = ByteBuffer.allocate(17); + BlockingSslHandler sslHnd = null; + ByteBuffer buf; + + if (isSslEnabled()) { + sslHnd = new BlockingSslHandler(ssl, ch, directBuf, ByteOrder.nativeOrder(), log); + + if (!sslHnd.handshake()) + throw new IgniteCheckedException("SSL handshake is not completed."); + + ByteBuffer handBuff = sslHnd.applicationBuffer(); + + if (handBuff.remaining() < 17) { + buf = ByteBuffer.allocate(1000); + + int read = ch.read(buf); + + if (read == -1) + throw new IgniteCheckedException("Failed to read remote node ID (connection closed)."); + + buf.flip(); + + buf = sslHnd.decode(buf); + } + else + buf = handBuff; + } + else { + buf = ByteBuffer.allocate(17); + - for (int i = 0; i < 17; ) { - int read = ch.read(buf); + for (int i = 0; i < 17; ) { + int read = ch.read(buf); - if (read == -1) - throw new IgniteCheckedException("Failed to read remote node ID (connection closed)."); + if (read == -1) + throw new IgniteCheckedException("Failed to read remote node ID (connection closed)."); - i += read; - } + i += read; + } + } UUID rmtNodeId0 = U.bytesToUuid(buf.array(), 1); @@ -2205,7 +2284,13 @@ else if (log.isDebugEnabled()) log.debug("Received remote node ID: " + rmtNodeId0); + if (isSslEnabled() ) { + assert sslHnd != null; + + ch.write(sslHnd.encrypt(ByteBuffer.wrap(U.IGNITE_HEADER))); + } + else - ch.write(ByteBuffer.wrap(U.IGNITE_HEADER)); + ch.write(ByteBuffer.wrap(U.IGNITE_HEADER)); if (recovery != null) { HandshakeMessage msg = new HandshakeMessage(getLocalNodeId(), @@ -2225,30 +2310,73 @@ buf.flip(); + if (isSslEnabled()) { + assert sslHnd != null; + + ch.write(sslHnd.encrypt(buf)); + } + else - ch.write(buf); + ch.write(buf); } + else { + if (isSslEnabled()) { + assert sslHnd != null; + + ch.write(sslHnd.encrypt(ByteBuffer.wrap(nodeIdMsg.nodeIdBytesWithType))); + } - else - ch.write(ByteBuffer.wrap(nodeIdMsg.nodeIdBytesWithType)); + else + ch.write(ByteBuffer.wrap(nodeIdMsg.nodeIdBytesWithType)); + } if (recovery != null) { if (log.isDebugEnabled()) log.debug("Waiting for handshake [rmtNode=" + rmtNodeId + ']'); + if (isSslEnabled()) { + assert sslHnd != null; + + buf = ByteBuffer.allocate(1000); + + ByteBuffer decode = null; + + buf.order(ByteOrder.nativeOrder()); + + for (int i = 0; i < 9; ) { + int read = ch.read(buf); + + if (read == -1) + throw new IgniteCheckedException("Failed to read remote node recovery handshake " + + "(connection closed)."); + + buf.flip(); + + decode = sslHnd.decode(buf); + + i += decode.remaining(); + + buf.flip(); + buf.compact(); + } + + rcvCnt = decode.getLong(1); + } + else { - buf = ByteBuffer.allocate(9); + buf = ByteBuffer.allocate(9); - buf.order(ByteOrder.nativeOrder()); + buf.order(ByteOrder.nativeOrder()); - for (int i = 0; i < 9; ) { - int read = ch.read(buf); + for (int i = 0; i < 9; ) { + int read = ch.read(buf); - if (read == -1) - throw new IgniteCheckedException("Failed to read remote node recovery handshake " + - "(connection closed)."); + if (read == -1) + throw new IgniteCheckedException("Failed to read remote node recovery handshake " + + "(connection closed)."); - i += read; - } + i += read; + } - rcvCnt = buf.getLong(1); + rcvCnt = buf.getLong(1); + } if (log.isDebugEnabled()) log.debug("Received handshake message [rmtNode=" + rmtNodeId + ", rcvCnt=" + rcvCnt + ']'); Index: ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java (revision 6b84399d77f0a7cb9e9aa7866353fcd4837a7d2b) +++ ../incubator-ignite/modules/core/src/main/java/org/apache/ignite/internal/util/nio/GridNioServer.java (revision 58c5a1222b3d2cec413801cb415bb45ffc290f3c) @@ -70,6 +70,9 @@ /** SSL system data buffer metadata key. */ private static final int BUF_SSL_SYSTEM_META_KEY = GridNioSessionMetaKey.nextUniqueKey(); + /** SSL write buf limit. */ + private static final int WRITE_BUF_LIMIT = GridNioSessionMetaKey.nextUniqueKey(); + /** Accept worker thread. */ @GridToStringExclude private final IgniteThread acceptThread; @@ -920,6 +923,10 @@ } ByteBuffer buf = ses.writeBuffer(); + + if (ses.meta(WRITE_BUF_LIMIT) != null) + buf.limit((int)ses.meta(WRITE_BUF_LIMIT)); + NioOperationFuture req = ses.removeMeta(NIO_OPERATION.ordinal()); List> doneFuts = null; @@ -971,14 +978,25 @@ writer.reset(); } + int sesBufLimit = buf.limit(); + int sesCap = buf.capacity(); + buf.flip(); + buf = sslFilter.encrypt(ses, buf); + ByteBuffer sesBuf = ses.writeBuffer(); - buf = sslFilter.encrypt(ses, sesBuf); - sesBuf.clear(); + if (sesCap - buf.limit() < 0) { + int limit = sesBufLimit + (sesCap - buf.limit()) - 100; + + ses.addMeta(WRITE_BUF_LIMIT, limit); + + sesBuf.limit(limit); + } + assert buf.hasRemaining(); if (!skipWrite) { @@ -1016,8 +1034,12 @@ break; } - else + else { buf = ses.writeBuffer(); + + if (ses.meta(WRITE_BUF_LIMIT) != null) + buf.limit((int)ses.meta(WRITE_BUF_LIMIT)); + } } } finally {