1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.waarp.common.crypto.ssl;
21
22 import io.netty.channel.Channel;
23 import io.netty.channel.ChannelFuture;
24 import io.netty.channel.ChannelFutureListener;
25 import io.netty.channel.ChannelHandler;
26 import io.netty.channel.ChannelPipeline;
27 import io.netty.channel.group.ChannelGroup;
28 import io.netty.channel.group.DefaultChannelGroup;
29 import io.netty.handler.ssl.SslHandler;
30 import io.netty.util.concurrent.DefaultEventExecutor;
31 import io.netty.util.concurrent.EventExecutor;
32 import io.netty.util.concurrent.Future;
33 import io.netty.util.concurrent.GenericFutureListener;
34 import org.waarp.common.logging.WaarpLogger;
35 import org.waarp.common.logging.WaarpLoggerFactory;
36 import org.waarp.common.utility.WaarpNettyUtil;
37 import org.waarp.common.utility.WaarpThreadFactory;
38
39 import java.util.NoSuchElementException;
40
41
42
43
44 public final class WaarpSslUtility {
45
46
47
48 private static final WaarpLogger logger =
49 WaarpLoggerFactory.getLogger(WaarpSslUtility.class);
50
51
52
53
54 private static final EventExecutor SSL_EVENT_EXECUTOR =
55 new DefaultEventExecutor(new WaarpThreadFactory("SSLEVENT"));
56
57
58
59 private static final ChannelGroup sslChannelGroup =
60 new DefaultChannelGroup("SslChannelGroup", SSL_EVENT_EXECUTOR);
61
62
63
64
65 public static final ChannelFutureListener SSLCLOSE =
66 new ChannelFutureListener() {
67
68 @Override
69 public final void operationComplete(final ChannelFuture future) {
70 if (future.channel().isActive()) {
71 future.channel().eventLoop()
72 .submit(new SslThread(future.channel()));
73 }
74 }
75 };
76
77 private WaarpSslUtility() {
78 }
79
80
81
82
83
84
85 public static void addSslOpenedChannel(final Channel channel) {
86 sslChannelGroup.add(channel);
87 }
88
89
90
91
92
93
94
95
96
97
98
99 @SuppressWarnings({ "unchecked", "rawtypes" })
100 public static void addSslHandler(final ChannelFuture future,
101 final ChannelPipeline pipeline,
102 final ChannelHandler sslHandler,
103 final GenericFutureListener<? extends Future<? super Channel>> listener) {
104 if (future == null) {
105 logger.debug("Add SslHandler: {}", pipeline.channel());
106 pipeline.channel().config().setAutoRead(true);
107 pipeline.addFirst("SSL", sslHandler);
108 ((SslHandler) sslHandler).handshakeFuture().addListener(listener);
109 } else {
110 future.addListener(new GenericFutureListener() {
111 @Override
112 public final void operationComplete(final Future future) {
113 logger.debug("Add SslHandler: {}", pipeline.channel());
114 pipeline.channel().config().setAutoRead(true);
115 pipeline.addFirst("SSL", sslHandler);
116 ((SslHandler) sslHandler).handshakeFuture().addListener(listener);
117 }
118 });
119 }
120 logger.debug("Checked Ssl Handler to be added: {}", pipeline.channel());
121 }
122
123
124
125
126
127
128
129
130
131
132 public static boolean waitForHandshake(final Channel channel) {
133 final ChannelHandler handler = channel.pipeline().first();
134 if (handler instanceof SslHandler) {
135 logger.debug("Start handshake SSL: {}", channel);
136 final SslHandler sslHandler = (SslHandler) handler;
137
138
139 final Future<Channel> handshakeFuture = sslHandler.handshakeFuture();
140 WaarpNettyUtil.awaitOrInterrupted(handshakeFuture,
141 sslHandler.getHandshakeTimeoutMillis() +
142 1000);
143 logger.debug("Handshake: {}:{}", handshakeFuture.isSuccess(), channel,
144 handshakeFuture.cause());
145 if (!handshakeFuture.isSuccess()) {
146 channel.close().awaitUninterruptibly(100);
147 return false;
148 }
149 } else {
150 logger.info("SSL Not found but connected: {} {}",
151 handler.getClass().getName());
152 }
153 return true;
154 }
155
156
157
158
159
160
161
162
163
164 public static Channel waitforChannelReady(final ChannelFuture future) {
165
166 WaarpNettyUtil.awaitOrInterrupted(future);
167 if (!future.isSuccess()) {
168 logger.error("Channel not connected", future.cause());
169 return null;
170 }
171 final Channel channel = future.channel();
172 if (waitForHandshake(channel)) {
173 return channel;
174 }
175 return null;
176 }
177
178
179
180
181 public static void forceCloseAllSslChannels() {
182 if (SSL_EVENT_EXECUTOR.isShutdown()) {
183 for (final Channel channel : sslChannelGroup) {
184 closingSslChannel(channel);
185 }
186 WaarpNettyUtil.awaitOrInterrupted(sslChannelGroup.close());
187 SSL_EVENT_EXECUTOR.shutdownGracefully();
188 }
189 }
190
191
192
193
194
195
196 public static ChannelFuture closingSslChannel(final Channel channel) {
197 if (channel.isActive()) {
198 removingSslHandler(null, channel, true);
199 logger.debug(
200 "Close the channel and returns the ChannelFuture: " + channel);
201 return channel.closeFuture();
202 }
203 if (channel.closeFuture().isDone()) {
204 return channel.closeFuture();
205 }
206 logger.debug("Already closed");
207 return channel.newSucceededFuture();
208 }
209
210
211
212
213
214
215
216
217
218
219 @SuppressWarnings({ "rawtypes", "unchecked" })
220 public static void removingSslHandler(final ChannelFuture future,
221 final Channel channel,
222 final boolean close) {
223 if (channel.isActive()) {
224 channel.config().setAutoRead(true);
225 final ChannelHandler handler = channel.pipeline().first();
226 if (handler instanceof SslHandler) {
227 final SslHandler sslHandler = (SslHandler) handler;
228 if (future != null) {
229 future.addListener(new GenericFutureListener() {
230 @Override
231 public final void operationComplete(final Future future) {
232 waitForSslClose(channel, sslHandler, close);
233 }
234 });
235 } else {
236 waitForSslClose(channel, sslHandler, close);
237 }
238 } else {
239 if (close) {
240 channel.close();
241 }
242 }
243 }
244 }
245
246 private static void waitForSslClose(final Channel channel,
247 final SslHandler sslHandler,
248 final boolean close) {
249 logger.debug("Found SslHandler and wait for Ssl.closeOutbound() : {}",
250 channel);
251 if (channel.isActive()) {
252 sslHandler.closeOutbound()
253 .addListener(new GenericFutureListener<Future<? super Void>>() {
254 @Override
255 public final void operationComplete(
256 final Future<? super Void> future) {
257 logger.debug("Ssl closed: {}", channel);
258 channel.pipeline().remove(sslHandler);
259 if (close && channel.isActive()) {
260 channel.close();
261 }
262 }
263 });
264 }
265 }
266
267
268
269
270
271
272
273
274
275 public static boolean waitForClosingSslChannel(final Channel channel,
276 final long delay) {
277 if (!WaarpNettyUtil.awaitOrInterrupted(channel.closeFuture(), delay)) {
278 try {
279 channel.pipeline().remove(SslHandler.class);
280 logger.debug("try to close anyway");
281 if (channel.isActive()) {
282 WaarpNettyUtil.awaitOrInterrupted(channel.close(), delay);
283 }
284 return false;
285 } catch (final NoSuchElementException e) {
286
287 if (channel.isActive()) {
288 WaarpNettyUtil.awaitOrInterrupted(channel.closeFuture(), delay);
289 }
290 }
291 }
292 return true;
293 }
294
295
296
297
298 private static class SslThread implements Runnable {
299 private final Channel channel;
300
301
302
303
304 private SslThread(final Channel channel) {
305 this.channel = channel;
306 }
307
308 @Override
309 public void run() {
310 closingSslChannel(channel);
311 }
312
313 }
314
315 }