From 462a4bae5c8317317e3a194b07ae8a8e9010a8e9 Mon Sep 17 00:00:00 2001 From: randymo Date: Sun, 3 Feb 2019 11:24:12 -0500 Subject: [PATCH 1/2] add remoteaddress ip and port, if available, to SSLEngine and SslHandler --- ...bstractSSLAwareChannelPipelineFactory.java | 26 ++++++++++++------- .../netty/BasicChannelUpstreamHandler.java | 8 +++++- .../james/imapserver/netty/IMAPServer.java | 9 ++++++- .../imapserver/netty/NettyImapSession.java | 9 ++++++- .../ManageSieveChannelUpstreamHandler.java | 8 +++++- .../netty/ManageSieveServer.java | 10 ++++++- 6 files changed, 56 insertions(+), 14 deletions(-) diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java index 8039dc75630..d47325760dc 100644 --- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java +++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.james.protocols.netty; +import java.net.InetSocketAddress; + import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; @@ -28,13 +30,13 @@ import org.jboss.netty.util.HashedWheelTimer; /** - * Abstract base class for {@link ChannelPipeline} implementations which use TLS - * + * Abstract base class for {@link ChannelPipeline} implementations which use TLS + * * */ public abstract class AbstractSSLAwareChannelPipelineFactory extends AbstractChannelPipelineFactory { - + private String[] enabledCipherSuites = null; public AbstractSSLAwareChannelPipelineFactory(int timeout, @@ -47,7 +49,7 @@ public AbstractSSLAwareChannelPipelineFactory(int timeout, int maxConnections, int maxConnectsPerIp, ChannelGroup group, String[] enabledCipherSuites, ExecutionHandler eHandler, ChannelHandlerFactory frameHandlerFactory, HashedWheelTimer hashedWheelTimer) { this(timeout, maxConnections, maxConnectsPerIp, group, eHandler, frameHandlerFactory, hashedWheelTimer); - + // We need to copy the String array becuase of possible security issues. // See https://issues.apache.org/jira/browse/PROTOCOLS-18 if (enabledCipherSuites != null) { @@ -58,7 +60,7 @@ public AbstractSSLAwareChannelPipelineFactory(int timeout, } } - + @Override public ChannelPipeline getPipeline() throws Exception { ChannelPipeline pipeline = super.getPipeline(); @@ -66,7 +68,13 @@ public ChannelPipeline getPipeline() throws Exception { if (isSSLSocket()) { // We need to set clientMode to false. // See https://issues.apache.org/jira/browse/JAMES-1025 - SSLEngine engine = getSSLContext().createSSLEngine(); + SSLEngine engine; + if (pipeline.getChannel().isConnected()){ + InetSocketAddress remoteAddress = (InetSocketAddress) pipeline.getChannel().getRemoteAddress(); + engine = getSSLContext().createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()); + } else { + engine = getSSLContext().createSSLEngine(); + } engine.setUseClientMode(false); if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { engine.setEnabledCipherSuites(enabledCipherSuites); @@ -78,14 +86,14 @@ public ChannelPipeline getPipeline() throws Exception { /** * Return if the socket is using SSL/TLS - * + * * @return isSSL */ protected abstract boolean isSSLSocket(); - + /** * Return the SSL context - * + * * @return context */ protected abstract SSLContext getSSLContext(); diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java index 3b0586a35e6..5751aae8ca2 100644 --- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java +++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java @@ -19,6 +19,7 @@ package org.apache.james.protocols.netty; import java.io.Closeable; +import java.net.InetSocketAddress; import java.nio.channels.ClosedChannelException; import java.util.LinkedList; import java.util.List; @@ -204,7 +205,12 @@ protected void cleanup(ChannelHandlerContext ctx) { protected ProtocolSession createSession(ChannelHandlerContext ctx) throws Exception { SSLEngine engine = null; if (secure != null) { - engine = secure.getContext().createSSLEngine(); + if (ctx.getChannel().isConnected()){ + InetSocketAddress remoteAddress = (InetSocketAddress) ctx.getChannel().getRemoteAddress(); + engine = secure.getContext().createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()); + } else { + engine = secure.getContext().createSSLEngine(); + } String[] enabledCipherSuites = secure.getEnabledCipherSuites(); if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { engine.setEnabledCipherSuites(enabledCipherSuites); diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java index b44df8d5f73..e72f3ccf9d4 100644 --- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java +++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java @@ -20,6 +20,7 @@ import static org.jboss.netty.channel.Channels.pipeline; +import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLEngine; @@ -172,7 +173,13 @@ public ChannelPipeline getPipeline() throws Exception { if (secure != null && !secure.isStartTLS()) { // We need to set clientMode to false. // See https://issues.apache.org/jira/browse/JAMES-1025 - SSLEngine engine = secure.getContext().createSSLEngine(); + SSLEngine engine; + if (pipeline.getChannel().isConnected()){ + InetSocketAddress remoteAddress = (InetSocketAddress) pipeline.getChannel().getRemoteAddress(); + engine = secure.getContext().createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()); + } else { + engine = secure.getContext().createSSLEngine(); + } engine.setUseClientMode(false); pipeline.addFirst(SSL_HANDLER, new SslHandler(engine)); diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java index f10fa1a63cc..382edead816 100644 --- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java +++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.james.imapserver.netty; +import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; @@ -124,7 +125,13 @@ public boolean startTLS() { } channel.setReadable(false); - SslHandler filter = new SslHandler(sslContext.createSSLEngine(), false); + SslHandler filter; + if (channel.isConnected()){ + InetSocketAddress remoteAddress = (InetSocketAddress) channel.getRemoteAddress(); + filter = new SslHandler(sslContext.createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()), false); + } else { + filter = new SslHandler(sslContext.createSSLEngine(), false); + } filter.getEngine().setUseClientMode(false); if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { filter.getEngine().setEnabledCipherSuites(enabledCipherSuites); diff --git a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java index bb7963aa2a7..c324cef410b 100644 --- a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java +++ b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java @@ -133,7 +133,13 @@ public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws private void turnSSLon(Channel channel) { if (sslContext != null) { channel.setReadable(false); - SslHandler filter = new SslHandler(sslContext.createSSLEngine(), false); + SslHandler filter; + if (channel.isConnected()){ + InetSocketAddress remoteAddress = (InetSocketAddress) channel.getRemoteAddress(); + filter = new SslHandler(sslContext.createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()), false); + } else { + filter = new SslHandler(sslContext.createSSLEngine(), false); + } filter.getEngine().setUseClientMode(false); if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { filter.getEngine().setEnabledCipherSuites(enabledCipherSuites); diff --git a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java index 8d89aa2cdc5..b0bdfb8bec4 100644 --- a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java +++ b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java @@ -21,6 +21,8 @@ import static org.jboss.netty.channel.Channels.pipeline; +import java.net.InetSocketAddress; + import javax.net.ssl.SSLEngine; import org.apache.james.managesieve.transcode.ManageSieveProcessor; @@ -104,7 +106,13 @@ public ChannelPipeline getPipeline() throws Exception { if (secure != null && !secure.isStartTLS()) { // We need to set clientMode to false. // See https://issues.apache.org/jira/browse/JAMES-1025 - SSLEngine engine = secure.getContext().createSSLEngine(); + SSLEngine engine; + if (pipeline.getChannel().isConnected()){ + InetSocketAddress remoteAddress = (InetSocketAddress) pipeline.getChannel().getRemoteAddress(); + engine = secure.getContext().createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()); + } else { + engine = secure.getContext().createSSLEngine(); + } engine.setUseClientMode(false); pipeline.addFirst(SSL_HANDLER, new SslHandler(engine)); From c01e990b435f8fa9a35dadb66491c1a9d1ea89f9 Mon Sep 17 00:00:00 2001 From: randymo Date: Wed, 6 Feb 2019 14:34:33 -0500 Subject: [PATCH 2/2] changes made based on comments from PR single location of logic plus tests added --- ...bstractSSLAwareChannelPipelineFactory.java | 10 +- .../netty/BasicChannelUpstreamHandler.java | 502 +++++++++--------- .../james/protocols/netty/SslEngineUtil.java | 103 ++++ .../protocols/netty/SslEngineUtilTest.java | 213 ++++++++ .../james/imapserver/netty/IMAPServer.java | 10 +- .../imapserver/netty/NettyImapSession.java | 10 +- .../ManageSieveChannelUpstreamHandler.java | 9 +- .../netty/ManageSieveServer.java | 9 +- 8 files changed, 573 insertions(+), 293 deletions(-) create mode 100644 protocols/netty/src/main/java/org/apache/james/protocols/netty/SslEngineUtil.java create mode 100644 protocols/netty/src/test/java/org/apache/james/protocols/netty/SslEngineUtilTest.java diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java index d47325760dc..9f1ccffe7d2 100644 --- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java +++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/AbstractSSLAwareChannelPipelineFactory.java @@ -18,8 +18,6 @@ ****************************************************************/ package org.apache.james.protocols.netty; -import java.net.InetSocketAddress; - import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; @@ -68,13 +66,7 @@ public ChannelPipeline getPipeline() throws Exception { if (isSSLSocket()) { // We need to set clientMode to false. // See https://issues.apache.org/jira/browse/JAMES-1025 - SSLEngine engine; - if (pipeline.getChannel().isConnected()){ - InetSocketAddress remoteAddress = (InetSocketAddress) pipeline.getChannel().getRemoteAddress(); - engine = getSSLContext().createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()); - } else { - engine = getSSLContext().createSSLEngine(); - } + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(pipeline.getChannel(), getSSLContext()); engine.setUseClientMode(false); if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { engine.setEnabledCipherSuites(enabledCipherSuites); diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java index 5751aae8ca2..0928f430e66 100644 --- a/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java +++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/BasicChannelUpstreamHandler.java @@ -1,254 +1,248 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ -package org.apache.james.protocols.netty; - -import java.io.Closeable; -import java.net.InetSocketAddress; -import java.nio.channels.ClosedChannelException; -import java.util.LinkedList; -import java.util.List; - -import javax.net.ssl.SSLEngine; - -import org.apache.james.protocols.api.Encryption; -import org.apache.james.protocols.api.Protocol; -import org.apache.james.protocols.api.ProtocolSession; -import org.apache.james.protocols.api.ProtocolSessionImpl; -import org.apache.james.protocols.api.ProtocolTransport; -import org.apache.james.protocols.api.Response; -import org.apache.james.protocols.api.future.FutureResponse; -import org.apache.james.protocols.api.handler.ConnectHandler; -import org.apache.james.protocols.api.handler.DisconnectHandler; -import org.apache.james.protocols.api.handler.LineHandler; -import org.apache.james.protocols.api.handler.ProtocolHandlerChain; -import org.apache.james.protocols.api.handler.ProtocolHandlerResultHandler; -import org.jboss.netty.buffer.ChannelBuffer; -import org.jboss.netty.channel.Channel; -import org.jboss.netty.channel.ChannelHandler.Sharable; -import org.jboss.netty.channel.ChannelHandlerContext; -import org.jboss.netty.channel.ChannelStateEvent; -import org.jboss.netty.channel.ChannelUpstreamHandler; -import org.jboss.netty.channel.ExceptionEvent; -import org.jboss.netty.channel.MessageEvent; -import org.jboss.netty.channel.SimpleChannelUpstreamHandler; -import org.jboss.netty.handler.codec.frame.TooLongFrameException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * {@link ChannelUpstreamHandler} which is used by the SMTPServer and other line based protocols - */ -@Sharable -public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler { - private static final Logger LOGGER = LoggerFactory.getLogger(BasicChannelUpstreamHandler.class); - protected final Protocol protocol; - protected final ProtocolHandlerChain chain; - protected final Encryption secure; - - public BasicChannelUpstreamHandler(Protocol protocol) { - this(protocol, null); - } - - public BasicChannelUpstreamHandler(Protocol protocol, Encryption secure) { - this.protocol = protocol; - this.chain = protocol.getProtocolChain(); - this.secure = secure; - } - - - @Override - public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { - ctx.setAttachment(createSession(ctx)); - super.channelBound(ctx, e); - } - } - - - - /** - * Call the {@link ConnectHandler} instances which are stored in the {@link ProtocolHandlerChain} - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { - List connectHandlers = chain.getHandlers(ConnectHandler.class); - List resultHandlers = chain.getHandlers(ProtocolHandlerResultHandler.class); - ProtocolSession session = (ProtocolSession) ctx.getAttachment(); - LOGGER.info("Connection established from {}", session.getRemoteAddress().getAddress().getHostAddress()); - if (connectHandlers != null) { - for (ConnectHandler cHandler : connectHandlers) { - long start = System.currentTimeMillis(); - Response response = cHandler.onConnect(session); - long executionTime = System.currentTimeMillis() - start; - - for (ProtocolHandlerResultHandler resultHandler : resultHandlers) { - // Disable till PROTOCOLS-37 is implemented - if (response instanceof FutureResponse) { - LOGGER.debug("ProtocolHandlerResultHandler are not supported for FutureResponse yet"); - break; - } - resultHandler.onResponse(session, response, executionTime, cHandler); - } - if (response != null) { - // TODO: This kind of sucks but I was able to come up with something more elegant here - ((ProtocolSessionImpl) session).getProtocolTransport().writeResponse(response, session); - } - - } - } - super.channelConnected(ctx, e); - } - } - - - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { - List connectHandlers = chain.getHandlers(DisconnectHandler.class); - ProtocolSession session = (ProtocolSession) ctx.getAttachment(); - if (connectHandlers != null) { - for (DisconnectHandler connectHandler : connectHandlers) { - connectHandler.onDisconnect(session); - } - } - super.channelDisconnected(ctx, e); - } - } - - - /** - * Call the {@link LineHandler} - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { - try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { - ProtocolSession pSession = (ProtocolSession) ctx.getAttachment(); - LinkedList lineHandlers = chain.getHandlers(LineHandler.class); - LinkedList resultHandlers = chain.getHandlers(ProtocolHandlerResultHandler.class); - - - if (lineHandlers.size() > 0) { - - ChannelBuffer buf = (ChannelBuffer) e.getMessage(); - LineHandler lHandler = (LineHandler) lineHandlers.getLast(); - long start = System.currentTimeMillis(); - Response response = lHandler.onLine(pSession, buf.toByteBuffer()); - long executionTime = System.currentTimeMillis() - start; - - for (ProtocolHandlerResultHandler resultHandler : resultHandlers) { - // Disable till PROTOCOLS-37 is implemented - if (response instanceof FutureResponse) { - LOGGER.debug("ProtocolHandlerResultHandler are not supported for FutureResponse yet"); - break; - } - response = resultHandler.onResponse(pSession, response, executionTime, lHandler); - } - if (response != null) { - // TODO: This kind of sucks but I was able to come up with something more elegant here - ((ProtocolSessionImpl) pSession).getProtocolTransport().writeResponse(response, pSession); - } - - } - - super.messageReceived(ctx, e); - } - } - - - @Override - public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { - ProtocolSession session = (ProtocolSession) ctx.getAttachment(); - LOGGER.info("Connection closed for {}", session.getRemoteAddress().getAddress().getHostAddress()); - cleanup(ctx); - - super.channelClosed(ctx, e); - } - } - - /** - * Cleanup the channel - * - * @param ctx - */ - protected void cleanup(ChannelHandlerContext ctx) { - ProtocolSession session = (ProtocolSession) ctx.getAttachment(); - if (session != null) { - session.resetState(); - session = null; - } - } - - - - protected ProtocolSession createSession(ChannelHandlerContext ctx) throws Exception { - SSLEngine engine = null; - if (secure != null) { - if (ctx.getChannel().isConnected()){ - InetSocketAddress remoteAddress = (InetSocketAddress) ctx.getChannel().getRemoteAddress(); - engine = secure.getContext().createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()); - } else { - engine = secure.getContext().createSSLEngine(); - } - String[] enabledCipherSuites = secure.getEnabledCipherSuites(); - if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { - engine.setEnabledCipherSuites(enabledCipherSuites); - } - } - - return protocol.newSession(new NettyProtocolTransport(ctx.getChannel(), engine)); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { - try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { - Channel channel = ctx.getChannel(); - ProtocolSession session = (ProtocolSession) ctx.getAttachment(); - if (e.getCause() instanceof TooLongFrameException && session != null) { - Response r = session.newLineTooLongResponse(); - ProtocolTransport transport = ((ProtocolSessionImpl) session).getProtocolTransport(); - if (r != null) { - transport.writeResponse(r, session); - } - } else { - if (channel.isConnected() && session != null) { - ProtocolTransport transport = ((ProtocolSessionImpl) session).getProtocolTransport(); - - Response r = session.newFatalErrorResponse(); - if (r != null) { - transport.writeResponse(r, session); - } - transport.writeResponse(Response.DISCONNECT, session); - } - if (e.getCause() instanceof ClosedChannelException) { - LOGGER.info("Unable to process request", e.getCause()); - } else { - LOGGER.error("Unable to process request", e.getCause()); - } - cleanup(ctx); - } - } - } - -} +/**************************************************************** + * 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.james.protocols.netty; + +import java.io.Closeable; +import java.nio.channels.ClosedChannelException; +import java.util.LinkedList; +import java.util.List; + +import javax.net.ssl.SSLEngine; + +import org.apache.james.protocols.api.Encryption; +import org.apache.james.protocols.api.Protocol; +import org.apache.james.protocols.api.ProtocolSession; +import org.apache.james.protocols.api.ProtocolSessionImpl; +import org.apache.james.protocols.api.ProtocolTransport; +import org.apache.james.protocols.api.Response; +import org.apache.james.protocols.api.future.FutureResponse; +import org.apache.james.protocols.api.handler.ConnectHandler; +import org.apache.james.protocols.api.handler.DisconnectHandler; +import org.apache.james.protocols.api.handler.LineHandler; +import org.apache.james.protocols.api.handler.ProtocolHandlerChain; +import org.apache.james.protocols.api.handler.ProtocolHandlerResultHandler; +import org.jboss.netty.buffer.ChannelBuffer; +import org.jboss.netty.channel.Channel; +import org.jboss.netty.channel.ChannelHandler.Sharable; +import org.jboss.netty.channel.ChannelHandlerContext; +import org.jboss.netty.channel.ChannelStateEvent; +import org.jboss.netty.channel.ChannelUpstreamHandler; +import org.jboss.netty.channel.ExceptionEvent; +import org.jboss.netty.channel.MessageEvent; +import org.jboss.netty.channel.SimpleChannelUpstreamHandler; +import org.jboss.netty.handler.codec.frame.TooLongFrameException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * {@link ChannelUpstreamHandler} which is used by the SMTPServer and other line based protocols + */ +@Sharable +public class BasicChannelUpstreamHandler extends SimpleChannelUpstreamHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(BasicChannelUpstreamHandler.class); + protected final Protocol protocol; + protected final ProtocolHandlerChain chain; + protected final Encryption secure; + + public BasicChannelUpstreamHandler(Protocol protocol) { + this(protocol, null); + } + + public BasicChannelUpstreamHandler(Protocol protocol, Encryption secure) { + this.protocol = protocol; + this.chain = protocol.getProtocolChain(); + this.secure = secure; + } + + + @Override + public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { + ctx.setAttachment(createSession(ctx)); + super.channelBound(ctx, e); + } + } + + + + /** + * Call the {@link ConnectHandler} instances which are stored in the {@link ProtocolHandlerChain} + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { + List connectHandlers = chain.getHandlers(ConnectHandler.class); + List resultHandlers = chain.getHandlers(ProtocolHandlerResultHandler.class); + ProtocolSession session = (ProtocolSession) ctx.getAttachment(); + LOGGER.info("Connection established from {}", session.getRemoteAddress().getAddress().getHostAddress()); + if (connectHandlers != null) { + for (ConnectHandler cHandler : connectHandlers) { + long start = System.currentTimeMillis(); + Response response = cHandler.onConnect(session); + long executionTime = System.currentTimeMillis() - start; + + for (ProtocolHandlerResultHandler resultHandler : resultHandlers) { + // Disable till PROTOCOLS-37 is implemented + if (response instanceof FutureResponse) { + LOGGER.debug("ProtocolHandlerResultHandler are not supported for FutureResponse yet"); + break; + } + resultHandler.onResponse(session, response, executionTime, cHandler); + } + if (response != null) { + // TODO: This kind of sucks but I was able to come up with something more elegant here + ((ProtocolSessionImpl) session).getProtocolTransport().writeResponse(response, session); + } + + } + } + super.channelConnected(ctx, e); + } + } + + + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { + List connectHandlers = chain.getHandlers(DisconnectHandler.class); + ProtocolSession session = (ProtocolSession) ctx.getAttachment(); + if (connectHandlers != null) { + for (DisconnectHandler connectHandler : connectHandlers) { + connectHandler.onDisconnect(session); + } + } + super.channelDisconnected(ctx, e); + } + } + + + /** + * Call the {@link LineHandler} + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { + ProtocolSession pSession = (ProtocolSession) ctx.getAttachment(); + LinkedList lineHandlers = chain.getHandlers(LineHandler.class); + LinkedList resultHandlers = chain.getHandlers(ProtocolHandlerResultHandler.class); + + + if (lineHandlers.size() > 0) { + + ChannelBuffer buf = (ChannelBuffer) e.getMessage(); + LineHandler lHandler = (LineHandler) lineHandlers.getLast(); + long start = System.currentTimeMillis(); + Response response = lHandler.onLine(pSession, buf.toByteBuffer()); + long executionTime = System.currentTimeMillis() - start; + + for (ProtocolHandlerResultHandler resultHandler : resultHandlers) { + // Disable till PROTOCOLS-37 is implemented + if (response instanceof FutureResponse) { + LOGGER.debug("ProtocolHandlerResultHandler are not supported for FutureResponse yet"); + break; + } + response = resultHandler.onResponse(pSession, response, executionTime, lHandler); + } + if (response != null) { + // TODO: This kind of sucks but I was able to come up with something more elegant here + ((ProtocolSessionImpl) pSession).getProtocolTransport().writeResponse(response, pSession); + } + + } + + super.messageReceived(ctx, e); + } + } + + + @Override + public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { + ProtocolSession session = (ProtocolSession) ctx.getAttachment(); + LOGGER.info("Connection closed for {}", session.getRemoteAddress().getAddress().getHostAddress()); + cleanup(ctx); + + super.channelClosed(ctx, e); + } + } + + /** + * Cleanup the channel + * + * @param ctx + */ + protected void cleanup(ChannelHandlerContext ctx) { + ProtocolSession session = (ProtocolSession) ctx.getAttachment(); + if (session != null) { + session.resetState(); + session = null; + } + } + + + + protected ProtocolSession createSession(ChannelHandlerContext ctx) throws Exception { + SSLEngine engine = null; + if (secure != null) { + engine = SslEngineUtil.INSTANCE.generateSslEngine(ctx.getChannel(), secure.getContext()); + String[] enabledCipherSuites = secure.getEnabledCipherSuites(); + if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { + engine.setEnabledCipherSuites(enabledCipherSuites); + } + } + + return protocol.newSession(new NettyProtocolTransport(ctx.getChannel(), engine)); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { + try (Closeable closeable = ProtocolMDCContext.from(protocol, ctx)) { + Channel channel = ctx.getChannel(); + ProtocolSession session = (ProtocolSession) ctx.getAttachment(); + if (e.getCause() instanceof TooLongFrameException && session != null) { + Response r = session.newLineTooLongResponse(); + ProtocolTransport transport = ((ProtocolSessionImpl) session).getProtocolTransport(); + if (r != null) { + transport.writeResponse(r, session); + } + } else { + if (channel.isConnected() && session != null) { + ProtocolTransport transport = ((ProtocolSessionImpl) session).getProtocolTransport(); + + Response r = session.newFatalErrorResponse(); + if (r != null) { + transport.writeResponse(r, session); + } + transport.writeResponse(Response.DISCONNECT, session); + } + if (e.getCause() instanceof ClosedChannelException) { + LOGGER.info("Unable to process request", e.getCause()); + } else { + LOGGER.error("Unable to process request", e.getCause()); + } + cleanup(ctx); + } + } + } + +} diff --git a/protocols/netty/src/main/java/org/apache/james/protocols/netty/SslEngineUtil.java b/protocols/netty/src/main/java/org/apache/james/protocols/netty/SslEngineUtil.java new file mode 100644 index 00000000000..322fbdb4d66 --- /dev/null +++ b/protocols/netty/src/main/java/org/apache/james/protocols/netty/SslEngineUtil.java @@ -0,0 +1,103 @@ +/**************************************************************** + * 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.james.protocols.netty; + +import org.jboss.netty.channel.Channel; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import java.net.InetSocketAddress; + +public enum SslEngineUtil { + INSTANCE; + + public enum SslEngineUtilMode { + NONE, + REMOTE_ONLY, + LOCAL_ONLY, + REMOTE_LOCAL, + LOCAL_REMOTE + } + + //default is NONE, so behavior is same as it was if users do nothing + private SslEngineUtilMode sslEngineUtilMode = SslEngineUtilMode.NONE; + + public void setSslEngineUtilMode(final SslEngineUtilMode sslEngineUtilMode) { + if (sslEngineUtilMode != null) { + this.sslEngineUtilMode = sslEngineUtilMode; + } + } + + public SSLEngine generateSslEngine(final Channel channel, final SSLContext sslContext) { + if (channel == null) { + return sslContext.createSSLEngine(); + } + SSLEngine engine; + switch (sslEngineUtilMode != null ? sslEngineUtilMode : SslEngineUtilMode.NONE) { + case NONE: + engine = sslContext.createSSLEngine(); + break; + case REMOTE_ONLY: + if (channel.isConnected() && channel.getRemoteAddress() != null) { + engine = createSslEngineFromAddress(sslContext, (InetSocketAddress) channel.getRemoteAddress()); + } else { + engine = sslContext.createSSLEngine(); + } + break; + case LOCAL_ONLY: + if (channel.isBound() && channel.getLocalAddress() != null) { + engine = createSslEngineFromAddress(sslContext, (InetSocketAddress) channel.getLocalAddress()); + } else { + engine = sslContext.createSSLEngine(); + } + break; + case REMOTE_LOCAL: + if (channel.isConnected() && channel.getRemoteAddress() != null) { + engine = createSslEngineFromAddress(sslContext, (InetSocketAddress) channel.getRemoteAddress()); + } else if (channel.isBound() && channel.getLocalAddress() != null) { + engine = createSslEngineFromAddress(sslContext, (InetSocketAddress) channel.getLocalAddress()); + } else { + engine = sslContext.createSSLEngine(); + } + break; + case LOCAL_REMOTE: + if (channel.isBound() && channel.getLocalAddress() != null) { + engine = createSslEngineFromAddress(sslContext, (InetSocketAddress) channel.getLocalAddress()); + } else if (channel.isConnected() && channel.getRemoteAddress() != null) { + engine = createSslEngineFromAddress(sslContext, (InetSocketAddress) channel.getRemoteAddress()); + } else { + engine = sslContext.createSSLEngine(); + } + break; + default: + engine = sslContext.createSSLEngine(); + break; + } + return engine; + } + + private SSLEngine createSslEngineFromAddress(final SSLContext sslContext, final InetSocketAddress address) { + if (address != null && address.getAddress() != null) { + return sslContext.createSSLEngine(address.getAddress().getHostAddress(), address.getPort()); + } else { + return sslContext.createSSLEngine(); + } + } +} diff --git a/protocols/netty/src/test/java/org/apache/james/protocols/netty/SslEngineUtilTest.java b/protocols/netty/src/test/java/org/apache/james/protocols/netty/SslEngineUtilTest.java new file mode 100644 index 00000000000..58f2527ea7a --- /dev/null +++ b/protocols/netty/src/test/java/org/apache/james/protocols/netty/SslEngineUtilTest.java @@ -0,0 +1,213 @@ +/**************************************************************** + * 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.james.protocols.netty; + + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.jboss.netty.channel.Channel; +import org.junit.Assert; +import org.junit.Test; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLEngine; +import java.net.InetSocketAddress; + +public class SslEngineUtilTest { + + //remote connection fields + private String remoteAddress = "1.1.1.1"; + private int remotePort = 1; + //local connection fields + private String localAddress = "2.2.2.2"; + private int localPort = 2; + + + @Test + public void testDefaultSslEngineUtilMode() throws Exception { + //since channel is null, will use the default implementation + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(null, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + Channel channel = mock(Channel.class); + + //since SslEngineUtil.INSTANCE will use Mode NONE by default, will use the default implementation + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + } + + @Test + public void testSslEngineUtilMode_null() throws Exception { + SslEngineUtil.INSTANCE.setSslEngineUtilMode(null); + //since channel is null, will use the default implementation + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(null, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + Channel channel = mock(Channel.class); + + //since SslEngineUtil.INSTANCE will use Mode NONE by default, will use the default implementation + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + } + + @Test + public void testSslEngineUtilMode_NONE() throws Exception { + SslEngineUtil.INSTANCE.setSslEngineUtilMode(SslEngineUtil.SslEngineUtilMode.NONE); + //since channel is null, will use the default implementation + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(null, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + Channel channel = mock(Channel.class); + //since SslEngineUtil.INSTANCE will use Mode NONE by default, will use the default implementation + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + } + + @Test + public void testSslEngineUtilMode_REMOTE_ONLY() throws Exception { + SslEngineUtil.INSTANCE.setSslEngineUtilMode(SslEngineUtil.SslEngineUtilMode.REMOTE_ONLY); + //channel is null, should return default values + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(null, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //channel is empty, should return default values + Channel channel = mock(Channel.class); + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //set up with local, should return default values + when(channel.isBound()).thenReturn(true); + when(channel.getLocalAddress()).thenReturn(new InetSocketAddress(localAddress, localPort)); + //since channel is connected and has remote address, build SslEngine with those as peer + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //set up with remote, should return remote values + when(channel.isConnected()).thenReturn(true); + when(channel.getRemoteAddress()).thenReturn(new InetSocketAddress(remoteAddress, remotePort)); + //since channel is connected and has remote address, build SslEngine with those as peer + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost().equals(remoteAddress)); + Assert.assertTrue(engine.getPeerPort() == remotePort); + } + + @Test + public void testSslEngineUtilMode_LOCAL_ONLY() throws Exception { + SslEngineUtil.INSTANCE.setSslEngineUtilMode(SslEngineUtil.SslEngineUtilMode.LOCAL_ONLY); + //channel is null, should return default values + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(null, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //channel is empty, should return default values + Channel channel = mock(Channel.class); + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //set up with remote, should return default values + when(channel.isConnected()).thenReturn(true); + when(channel.getRemoteAddress()).thenReturn(new InetSocketAddress(remoteAddress, remotePort)); + //since channel is connected and has remote address, build SslEngine with those as peer + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //set up with local, should return local values + when(channel.isBound()).thenReturn(true); + when(channel.getLocalAddress()).thenReturn(new InetSocketAddress(localAddress, localPort)); + //since channel is connected and has remote address, build SslEngine with those as peer + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost().equals(localAddress)); + Assert.assertTrue(engine.getPeerPort() == localPort); + } + + @Test + public void testSslEngineUtilMode_LOCAL_REMOTE() throws Exception { + SslEngineUtil.INSTANCE.setSslEngineUtilMode(SslEngineUtil.SslEngineUtilMode.LOCAL_REMOTE); + //channel is null, should return default values + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(null, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //channel is empty, should return default values + Channel channel = mock(Channel.class); + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //set up with remote, should return remote values + when(channel.isConnected()).thenReturn(true); + when(channel.getRemoteAddress()).thenReturn(new InetSocketAddress(remoteAddress, remotePort)); + //since channel is connected and has remote address, build SslEngine with those as peer + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost().equals(remoteAddress)); + Assert.assertTrue(engine.getPeerPort() == remotePort); + + //set up with local, should return local values + when(channel.isBound()).thenReturn(true); + when(channel.getLocalAddress()).thenReturn(new InetSocketAddress(localAddress, localPort)); + //since channel is connected and has remote address, build SslEngine with those as peer + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost().equals(localAddress)); + Assert.assertTrue(engine.getPeerPort() == localPort); + } + + @Test + public void testSslEngineUtilMode_REMOTE_LOCAL() throws Exception { + SslEngineUtil.INSTANCE.setSslEngineUtilMode(SslEngineUtil.SslEngineUtilMode.REMOTE_LOCAL); + //channel is null, should return default values + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(null, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //channel is empty, should return default values + Channel channel = mock(Channel.class); + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost() == null); + Assert.assertTrue(engine.getPeerPort() == -1); + + //set up with local, should return local values + when(channel.isBound()).thenReturn(true); + when(channel.getLocalAddress()).thenReturn(new InetSocketAddress(localAddress, localPort)); + //since channel is connected and has remote address, build SslEngine with those as peer + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost().equals(localAddress)); + Assert.assertTrue(engine.getPeerPort() == localPort); + + //set up with remote, should return remote values + when(channel.isConnected()).thenReturn(true); + when(channel.getRemoteAddress()).thenReturn(new InetSocketAddress(remoteAddress, remotePort)); + //since channel is connected and has remote address, build SslEngine with those as peer + engine = SslEngineUtil.INSTANCE.generateSslEngine(channel, SSLContext.getDefault()); + Assert.assertTrue(engine.getPeerHost().equals(remoteAddress)); + Assert.assertTrue(engine.getPeerPort() == remotePort); + } +} diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java index e72f3ccf9d4..d3a2f19300c 100644 --- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java +++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/IMAPServer.java @@ -20,7 +20,6 @@ import static org.jboss.netty.channel.Channels.pipeline; -import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLEngine; @@ -38,6 +37,7 @@ import org.apache.james.protocols.netty.ChannelHandlerFactory; import org.apache.james.protocols.netty.ConnectionLimitUpstreamHandler; import org.apache.james.protocols.netty.ConnectionPerIpLimitUpstreamHandler; +import org.apache.james.protocols.netty.SslEngineUtil; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelUpstreamHandler; @@ -173,13 +173,7 @@ public ChannelPipeline getPipeline() throws Exception { if (secure != null && !secure.isStartTLS()) { // We need to set clientMode to false. // See https://issues.apache.org/jira/browse/JAMES-1025 - SSLEngine engine; - if (pipeline.getChannel().isConnected()){ - InetSocketAddress remoteAddress = (InetSocketAddress) pipeline.getChannel().getRemoteAddress(); - engine = secure.getContext().createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()); - } else { - engine = secure.getContext().createSSLEngine(); - } + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(pipeline.getChannel(), secure.getContext()); engine.setUseClientMode(false); pipeline.addFirst(SSL_HANDLER, new SslHandler(engine)); diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java index 382edead816..f7932fbd1aa 100644 --- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java +++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java @@ -18,7 +18,6 @@ ****************************************************************/ package org.apache.james.imapserver.netty; -import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; @@ -28,6 +27,7 @@ import org.apache.james.imap.api.process.ImapLineHandler; import org.apache.james.imap.api.process.ImapSession; import org.apache.james.imap.api.process.SelectedMailbox; +import org.apache.james.protocols.netty.SslEngineUtil; import org.jboss.netty.channel.Channel; import org.jboss.netty.handler.codec.compression.ZlibDecoder; import org.jboss.netty.handler.codec.compression.ZlibEncoder; @@ -125,13 +125,7 @@ public boolean startTLS() { } channel.setReadable(false); - SslHandler filter; - if (channel.isConnected()){ - InetSocketAddress remoteAddress = (InetSocketAddress) channel.getRemoteAddress(); - filter = new SslHandler(sslContext.createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()), false); - } else { - filter = new SslHandler(sslContext.createSSLEngine(), false); - } + SslHandler filter = new SslHandler(SslEngineUtil.INSTANCE.generateSslEngine(channel, sslContext), false); filter.getEngine().setUseClientMode(false); if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { filter.getEngine().setEnabledCipherSuites(enabledCipherSuites); diff --git a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java index c324cef410b..31d681e6455 100644 --- a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java +++ b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveChannelUpstreamHandler.java @@ -28,6 +28,7 @@ import org.apache.james.managesieve.api.SessionTerminatedException; import org.apache.james.managesieve.transcode.ManageSieveProcessor; import org.apache.james.managesieve.util.SettableSession; +import org.apache.james.protocols.netty.SslEngineUtil; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelFutureListener; @@ -133,13 +134,7 @@ public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws private void turnSSLon(Channel channel) { if (sslContext != null) { channel.setReadable(false); - SslHandler filter; - if (channel.isConnected()){ - InetSocketAddress remoteAddress = (InetSocketAddress) channel.getRemoteAddress(); - filter = new SslHandler(sslContext.createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()), false); - } else { - filter = new SslHandler(sslContext.createSSLEngine(), false); - } + SslHandler filter = new SslHandler(SslEngineUtil.INSTANCE.generateSslEngine(channel, sslContext), false); filter.getEngine().setUseClientMode(false); if (enabledCipherSuites != null && enabledCipherSuites.length > 0) { filter.getEngine().setEnabledCipherSuites(enabledCipherSuites); diff --git a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java index b0bdfb8bec4..3e94939509b 100644 --- a/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java +++ b/server/protocols/protocols-managesieve/src/main/java/org/apache/james/managesieveserver/netty/ManageSieveServer.java @@ -33,6 +33,7 @@ import org.apache.james.protocols.netty.ConnectionLimitUpstreamHandler; import org.apache.james.protocols.netty.ConnectionPerIpLimitUpstreamHandler; import org.apache.james.protocols.netty.LineDelimiterBasedChannelHandlerFactory; +import org.apache.james.protocols.netty.SslEngineUtil; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelUpstreamHandler; @@ -106,13 +107,7 @@ public ChannelPipeline getPipeline() throws Exception { if (secure != null && !secure.isStartTLS()) { // We need to set clientMode to false. // See https://issues.apache.org/jira/browse/JAMES-1025 - SSLEngine engine; - if (pipeline.getChannel().isConnected()){ - InetSocketAddress remoteAddress = (InetSocketAddress) pipeline.getChannel().getRemoteAddress(); - engine = secure.getContext().createSSLEngine(remoteAddress.getAddress().getHostAddress(), remoteAddress.getPort()); - } else { - engine = secure.getContext().createSSLEngine(); - } + SSLEngine engine = SslEngineUtil.INSTANCE.generateSslEngine(pipeline.getChannel(), secure.getContext()); engine.setUseClientMode(false); pipeline.addFirst(SSL_HANDLER, new SslHandler(engine));